obb的生成及使用

2024-09-04 17:58
文章标签 使用 生成 obb

本文主要是介绍obb的生成及使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

obb的制作及使用

obb制作官方文档参考:

  • obb制作及使用官方文档
  • obb文件生成可参考jobb工具生成(官网推荐)

前言:

  Google Play应用商店在上传限制100MB大小,超过该大小的应用必须将超过部分以扩展文件的形式进行上传处理,总共可上传2个扩展文件,每个最大文件可为2GB,同时obb文件格式【扩展文件可以使用任何文件格式(ZIP, PDF, MP4, 等)。不管任何文件格式Android都认为他们是obb(opaque binary blobs)文件】可自选。
  对每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件,所以一般只需要处理main扩展文件。

一 、OBB命名规则

[main|patch].<expansion-version>.<package-name>.obb

文件名包含四部分,其中:

  • main|patch: 用来表示是主扩展还是补充扩展。
  • expansion-version: 当前上传apk的version code。
  • package-name: apk的包名
  • obb:后缀,其实本质就是zip文件,Google play会自动将zip文件改为obb文件。

例如,假设您的APK版本是(versionCode)25,你的包名为com.example.app。如果上传的主扩展文件,该文件被重命名为:

                         main.25.com.example.app.obb

注意:

  • 上传APK时,OBB会另外上传,不能只更新OBB文件
  • 下载应用程序时,OBB文件下载到%external storage%/Android/obb/package name目录

二、obb 生成方法:

1、google官方提供的工具 jobb

  制作成OBB(Opaque Binary Blob)格式文件的一种工具,在Android SDK中, %ANDROID——HOME/tools%中,在tools/bin下有jobb

命令:

jobb -d [目录名称的完整路径] -o [输出目标文件的完整路径] -pn [软件包名] -pv [包版本]

注:如果没有将android sdk配置环境就需要打开命令窗口后将jobb.bat拖入cmd窗口内才能执行

EG:

源文件目录 D:contents\main\assets\ 目标文件夹: D:\obb\output.obb 软件包名 com.example.app 包版本 25
jobb -d D:\contents\ main\assets\ -o D:\obb\output.obb -pn com.example.app -pv 25

注意:

  • jobb -d,-o 需要指定完整路径。如果指定相对路径,%ANDROID_HOME%/tools/将作为相对路径
  • JOBB工具如果不能输出有一定容量的.obb的文件的话,出现错误提示
  • 下表列出了该jobb工具的命令行选项。
选项
描述
-d 设置用于创建OBB文件的输入目录,或在提取(-dump)现有文件时设置输出目录。创建OBB文件时,指定目录及其所有子目录的内容都包含在OBB文件系统中。
-o 指定OBB文件的文件名。创建OBB并提取(转储)其内容时,此参数是必需的。
-pn 指定安装OBB文件的应用程序的软件包名称,该名称对应package于应用程序清单中指定的值。创建OBB文件时需要此参数。
-pv 设置可以挂载OBB文件的应用程序的最低版本,该版本对应android:versionCode于应用程序清单中的值。创建OBB文件时需要此参数。
-k 指定用于加密新OBB文件或解密现有加密OBB文件的密码。
-ov创建OBB文件,该文件是现有OBB文件结构的叠加层。此选项允许将新包装内容装入与先前包装相同的位置,并用于创建以前生成的OBB文件的修补程序版本。覆盖OBB文件中的文件替换具有相同路径的文件。
-dump 提取指定OBB文件的内容。使用此选项时,还必须使用-d 参数指定内容的输出目录。
注意:转储现有OBB文件时,可以省略该 -d 参数以获取文件内的目录列表,而不提取内容。
-v设置该工具的详细输出。
-about显示该jobb工具的版本和帮助信息。
2、使用winRAR进行压缩(推荐)

将需要压缩的文件放入assets 或者根据目录中在使用winRAR进行压缩。
选中assets文件夹右击—添加到压缩文件,打开下面的窗口,根据命名规则进行命名。

tip:
如果你的扩展文件是一些媒体文件并且你不想解压资源包,而是借助media playback call
(例如MediaPlayer.setDataSource() and SoundPool.load())直接播放资源包里面的媒体文件。
那么在创建扩展文件包的时候务必不要压缩文件,而仅仅是打包即可 压缩方式为:存储


这里写图片描述
压缩过程

三、扩展文件的保存位置

  当Android Market下载程序的扩展文件的时候会保存到系统的共享存储区。为了确保程序正常运行,您不能删除、移动或者重命名扩展文件。在某些设备上Market无法自动下载该扩展文件,那么您应该在程序启动的时候去下载该文件并且保存到同样的位置。
扩展文件保存位置如下:

<shared-storage>/Android/obb/<package-name>/
<shared-storage> 代表共享文件的目录路径,通过函数getExternalStorageDirectory()获取;
<package-name> APK的Java包名。其中shared-storage是设备的primary external storage。

  对于每个App而言,该目录下最多只能包含2个扩展文件。一个是main扩展文件另外一个是patch扩展文件。当更新程序的时候,如果有新的扩展文件则新文件会覆盖旧的扩展文件。
  如果您需要解压缩扩展文件来使用,请注意不要删除该.obb文件,并且也不要把文件解压缩到该目录。您应该把解压缩后的文件保存到getExternalFilesDir()返回的目录下面。如果有可能的话,最好使用程序能直接读取的文件格式而不用再次解压缩文件了。Android开发团队提供了一个项目( APK Expansion Zip Library)可以直接读取ZIP文件中的内容而不用解压缩该文件.
  需要注意的是:保存在系统共享存储区的文件,用户和其他APP也可以访问。

四、APK扩展文件使用实例

要在App中使用扩展文件,需要两个附加的Android库项目:

  • Google Market Licensing package-
  • Google Market APK Expansion Library package-

可以通过Android SDK Manager来下载,也可以直接通过如下链接下载:
https://dl-ssl.google.com/android/repository/market_licensing-r02.zip
https://dl-ssl.google.com/android/repository/market_apk_expansion-r01.zip

  下载完成后使用market_licensing-r02.zip文件中的目录google_market_licensing\library来创建一个库项目;
  然后使用market_apk_expansion-r01.zip中的google_market_apk_expansion\downloader_library来创建另外一个库项目。
  同时为了简化对ZIP格式扩展文件的处理,在market_apk_expansion-r01.zip文件中还包含了一个对ZIP文件处理的库项目:google_market_apk_expansion\zip_file。 如果您使用的扩展文件格式是ZIP,那么也可以创建这个库项目。

1. 声明需要的权限
<manifest...><!-- Required to access Android Market Licensing --><uses-permissionandroid:name="com.android.vending.CHECK_LICENSE"/><!-- Required to download files from Android Market --><uses-permissionandroid:name="android.permission.INTERNET"/><!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --><uses-permissionandroid:name="android.permission.WAKE_LOCK"/><!-- Required to poll the state of the network connection and respond to changes --><uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/><!-- Required to check whether Wi-Fi is enabled --><uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/><!-- Required to read and write the expansion files on shared storage --><uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>...
</manifest>

注意:默认情况下,下载库项目需要的API level为4 而APK扩展ZIP库项目需要API level为5.
准备工作完成后,下面来具体看看如何使用扩展文件。

2. 实现下载服务(Implementing the downloader service)

  为了实现在后台下载文件,下载库项目提供了一个Service实现,名称为DownloaderService。您应该继承自这个文件来实现您的下载服务。为了简化下载服务的开发,该DownloaderService还实现了如下功能:

  • 注册一个BroadcastReceiver来监听设备的网络连接状态的改变。如果网络连接断开就暂停下载;如果网络连接恢复就继续下载。-
  • 安排一个 RTC_WAKEUP 通知,当下载服务被终结的时候可以通过该通知来启动下载服务
  • 生成一个通知(Notification )来显示下载的进度以及下载错误等状态
  • 允许您的程序手工的暂停和恢复下载
  • 检测共享存储区挂载了并且可用,在下载文件之前检测 文件是否已经存在、存储空间是否足够。如果出现问题就通知用户。

      您仅仅需要创建一个继承自DownloaderService的类,并且实现如下三个函数即可:
    getPublicKey():您Market账号的 Base64 编码 RSA 公共密钥,可以通过如下网址获取:
    https://market.android.com/publish/Home#ProfileEditorPlace:

getSALT(): 许可策略用来生成混淆器(Obfuscator)的一组随机bytes。

getAlarmReceiverClassName(): 返回您程序中用来重启下载进程的BroadcastReceiver类名称。当某些情况下,下载服务被意外终止的时候通过该BroadcastReceiver类来重新下载。比如 进程管理的程序终止了下载服务。
  

  • 下面是一个DownloaderService类的实现代码:
public class SampleDownloaderService extends DownloaderService {// You must use the public key belonging to your publisher accountpublic static final String BASE64_PUBLIC_KEY ="YourAndroidMarketLVLKey";// You should also modify this saltpublic static final byte[] SALT =new byte[] {1,42, -12, -1,54,98,-100, -12,43,2, -8, -4,9,5, -106, -107, -33,45, -1,84};@Overridepublic String getPublicKey() {return BASE64_PUBLIC_KEY;}@Overridepublic byte[] getSALT() {return SALT;}@Overridepublic String getAlarmReceiverClassName() {return SampleAlarmReceiver.class.getName();}
}

  然后 在Manifest文件中声明该Service即可。非常简单吧!

<application ...><service android:name=".SampleDownloaderService" />    
</application>
  • 实现AlarmReceiver
      为了检测下载进程和重启下载服务,DownloaderService会安排一个RTC_WAKEUP Alarm来发送一个Intent到程序的 BroadcastReceiver。你必需定义这个 BroadcastReceiver 来调用 Downloader Library提供的函数,通过该函数来检测下载状态和在必要的情况下重启下载服务。
      实现这个类也是非常简单的,一般来说只要覆写onReceive()函数并且调用DownloaderClientMarshaller.startDownloadServiceIfRequired()函数即可。
    定义AlarmReceiver继承自BroadcastReceiver如下所示:
public class AlarmReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {try {PrintLog.d("AlarmReceiver startDownloadServiceIfRequired");DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent,YRDownloaderService.class);} catch (NameNotFoundException e) {e.printStackTrace();}       }}

  然后 在Manifest文件中声明该Receiver即可。非常简单吧!

<application ...><service android:name=".AlarmReceiver" />    
</application>
3.开始下载扩展文件

程序的主Activity(通过Launcher图标启动的Activity)应该负责检查扩展文件是否存在、如果不存在就启动下载服务。
使用Downloader Library来下载需要遵守如下步骤:

1)检查文件是否已经下载了
Downloader Library中的Helper类中包含了一些函数来简化这个步骤:
getExtendedAPKFileName(Context, c, boolean mainFile, int versionCode)
doesFileExist(Context c, String fileName, long fileSize)
例如在示例项目中,在Activity的onCreate()函数中通过如下函数来检查文件是否存在:

boolean expansionFilesDelivered() {for(XAPKFile xf : xAPKS) {String fileName = Helpers.getExpansionAPKFileName(this, xf.mIsBase, xf.mFileVersion);if(!Helpers.doesFileExist(this, fileName, xf.mFileSize,false))returnfalse;}returntrue;
}

这里的XAPKFile对象保存了已知扩展文件的版本号和大小以及是否为main扩展文件。如果该函数返回false则启动下载服务。

2)通过 DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, ClassserviceClass)该函数来开始下载。
该函数的参数如下:

  • context: Your application’s Context.
  • notificationClient: 用来启动主Activity的PendingIntent。用在DownloaderService 创建的用来显示下载进度的通知中。当用户选择该通知,系统调用该PendingIntent来打开显示下载进度的Activity(一般而言就是启动下载的Activity)。
  • serviceClass: 程序中继承自DownloaderService的类。在必要的情况下会启动该服务来开始下载。

这个函数返回一个整数来表示是否有必要下载文件。有如下几个值:

  • NO_DOWNLOAD_REQUIRED: 表示文件已经存在或者当前正在下载。
  • LVL_CHECK_REQUIRED:表示需要授权验证来获取下载扩展文件的URL。
  • DOWNLOAD_REQUIRED: 表示扩展文件的URL已经获取到了,但是还没开始下载。
  • LVL_CHECK_REQUIRED 和 DOWNLOAD_REQUIRED 在本质上是一样的,一般而言您无需关注这个状态。在您的主Activity中调用 startDownloadServiceIfRequired(),你只需要看看返回值是否为NO_DOWNLOAD_REQUIRED即可。如果返回值不是NO_DOWNLOAD_REQUIRED, Downloader Library 开始启动下载,您应该更新程序界面来显示下载进度;如果返回值是 NO_DOWNLOAD_REQUIRED,表明该文件已经下载好了,您的程序可以正常启动了。

例如:

@Override
public void onCreate(Bundle savedInstanceState) {// Check if expansion files are available before going any furtherif(!expansionFilesDelivered()) {// Build an Intent to start this activity from the NotificationIntent notifierIntent =newIntent(this, MainActivity.getClass());notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |Intent.FLAG_ACTIVITY_CLEAR_TOP);...PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT);// Start the download service (if required)intstartResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,pendingIntent, SampleDownloaderService.class);// If download has started, initialize this activity to show download progressif(startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {// This is where you do set up to display the download progress (next step)...return;}// If the download wasn't necessary, fall through to start the app}startApp();// Expansion files are available, start the app
}

3)  当 startDownloadServiceIfRequired() 函数的返回值不是NO_DOWNLOAD_REQUIRED的时候,
调用DownloaderClientMarshaller.CreateStub(IDownloaderClient client, ClassdownloaderService)函数来创建一个IStub实例。这个IStub实例提供了Activity和下载服务之前的绑定功能,这样您的Activity就可以收到下载事件了。
  CreateStub()函数需要一个实现了IDownloaderClient接口的类和DownloaderService的实现类作为参数。一般而言只要让Activity实现IDownloaderClient接口即可。
  Android开发团队推荐在Activity的onCreate()函数中创建IStub对象(在startDownloadServiceIfRequired()函数之后创建)。

例如:

// Start the download service (if required)
int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this,pendingIntent, SampleDownloaderService.class);
// If download has started, initialize activity to show progress
if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) {// Instantiate a member instance of IStubmDownloaderClientStub = DownloaderClientMarshaller.CreateStub(this,SampleDownloaderService.class);// Inflate layout that shows download progresssetContentView(R.layout.downloader_ui);return;
}

  当onCreate()函数返回以后,Activity会执行onResume()函数,在该函数中调用IStub的 connect() 函数。同样在onStop()函数中调用IStub的 disconnect()函数。

例如:

@Override
protectedvoidonResume() {if(null!= mDownloaderClientStub) {mDownloaderClientStub.connect(this);}super.onResume();
}@Override
protectedvoidonStop() {if(null!= mDownloaderClientStub) {mDownloaderClientStub.disconnect(this);}super.onStop();
}

调用connect()用来绑定Activity和DownloaderService 。

4. 处理下载进度

  要接收下载进度信息,需要实现IDownloaderClient 接口。该接口有如下函数:

onServiceConnected(Messenger m)
  在初始化完IStub后,会回调该函数。该函数的参数是用来访问您的DownloaderService的,通过 DownloaderServiceMarshaller.CreateProxy()函数来创建这个IDownloaderService对象,然后可以用这个对象来控制下载服务,比如 暂停、继续下载等。

推荐的实现方式:

private IDownloaderService mRemoteService;
...@Override
public void onServiceConnected(Messenger m) {mRemoteService = DownloaderServiceMarshaller.CreateProxy(m);mRemoteService.onClientUpdated(mDownloaderClientStub.getMessenger());
}

onDownloadStateChanged(int newState)
  当下载状态发生变化的时候调用该函数,例如 开始下载或者下载完成。

  参数newState的值是IDownloaderClient接口中定义的一些常量之一(以 STATE_ 开头的);
  可以通过函数 Helpers.getDownloaderStringResourceIDFromState()来获取一个状态的文本描述,这样用户更容易理解。例如 STATE_PAUSED_ROAMING 对应的文本描述是: “Download paused because you are roaming/当前在漫游状态,下载停止”

onDownloadProgress(DownloadProgressInfo progress)
  该函数的参数DownloadProgressInfo包含了下载进度的各种信息,例如 预计完成时间、当前下载速度、完成的百分比等。可以根据该信息来更新下载界面。

另外还有一些有用的函数:

  • requestPauseDownload()
    暂停下载
  • requestContinueDownload()
    恢复下载
  • setDownloadFlags(int flags)
    设置下载的网络标示。当前只支持一个标示:FLAGS_DOWNLOAD_OVER_CELLULAR。 通过移动网络下载扩展文件。默认情况下该标示没有启用,所以默认情况下只通过WIFI下载。
5.读取扩展文件

  将APK扩展文件下载下来之后,紧接着我们就要考虑如何使用它了。但是Android 6.0的一些实现(API级别23)以及以后的一些实现仍然需要权限,因此需要在应用程序清单中声明存储权限,并在运行时请求外部存储权限,如下所示:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
或者
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

  在使用obb扩展文件前需要将Zip Library的库移入自己的项目中进行依赖使用此库可以轻松地将ZIP扩展文件中的资源作为虚拟文件系统读取。

Tip:(Zip Library位于其中/extras/google/google_market_apk_expansion/zip_file/)

  使用APKExpansionSupport类来获取obb中的资源
  提供一些访问扩展文件名和ZIP文件的方法:

  • getAPKExpansionFiles()
上面显示的相同方法返回两个扩展文件的完整文件路径。
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
返回ZipResourceFile表示主文件和补丁文件的总和。也就是说,如果同时指定mainVersion和patchVersion,则返回一个ZipResourceFile提供对所有数据的读访问权,并将补丁文件的数据合并到主文件的顶部。
  • ZipResourceFile
表示共享存储上的ZIP文件,并执行基于ZIP文件提供虚拟文件系统的所有工作。您可以使用- APKExpansionSupport.getAPKExpansionZipFile()或ZipResourceFile通过将实例传递给扩展文件来获取实例。此类包含各种有用的方法,但您通常不需要访问其中的大多数方法。一些重要的方法是:
  • getInputStream(String assetPath)
    提供InputStream读取ZIP文件中的文件。的 assetPath必须的路径所需的文件,相对于的ZIP文件内容的根。
  • getAssetFileDescriptor(String assetPath)
    提供一个AssetFileDescriptor对ZIP文件中的文件。的assetPath必须的路径所需的文件,相对于的ZIP文件内容的根。这对某些需要的Android API很有用AssetFileDescriptor

首先要获取扩展文件的路径,可以通过如下代码完成该操作:

// The shared path to all app expansion files
private final static String EXP_PATH ="/Android/obb/";static String[] getAPKExpansionFiles(Context ctx, intmainVersion,intpatchVersion) {String packageName = ctx.getPackageName();Vector<String> ret =newVector<String>();if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// Build the full path to the app's expansion filesFile root = Environment.getExternalStorageDirectory();File expPath =newFile(root.toString() + EXP_PATH + packageName);// Check that expansion file path existsif(expPath.exists()) {if( mainVersion >0) {String strMainPath = expPath + File.separator +"main."+mainVersion +"."+ packageName +".obb";File main =newFile(strMainPath);if( main.isFile() ) {ret.add(strMainPath);}}if( patchVersion >0) {String strPatchPath = expPath + File.separator +"patch."+mainVersion +"."+ packageName +".obb";File main =newFile(strPatchPath);if( main.isFile() ) {ret.add(strPatchPath);}}}}String[] retArray =newString[ret.size()];ret.toArray(retArray);returnretArray;
}
示例提取资源:
ProvinceUtil    provinceUtil = new ProvinceUtil();
ZipResourceFile expansionFile = null;
try {expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getApplicationContext(), BuildConfig.VERSION_CODE, 0);if (expansionFile != null) {AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("assets/my_province_data.txt");PrintLog.e("-------" + fd.getLength());InputStream inputStream = expansionFile.getInputStream("assets/my_province_data.txt");if (inputStream != null) {provinceUtil.initProvinceDatas(inputStream);String[] provinces = provinceUtil.getmProvinceDatas();if (provinces != null) {//参考ArrayAdapter的构造函数listView.setAdapter(new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,provinces));}}}
} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();
}

这篇关于obb的生成及使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1136629

相关文章

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

使用 sql-research-assistant进行 SQL 数据库研究的实战指南(代码实现演示)

《使用sql-research-assistant进行SQL数据库研究的实战指南(代码实现演示)》本文介绍了sql-research-assistant工具,该工具基于LangChain框架,集... 目录技术背景介绍核心原理解析代码实现演示安装和配置项目集成LangSmith 配置(可选)启动服务应用场景