借助 Play Feature Delivery,您的应用可以在搭载 Android 5.0(API 级别 21)及更高版本的设备上按需下载功能模块。您的应用只需调用 Play Core 库中的 API,即可根据需要下载和安装此类模块,Google Play 商店只会将该模块所需的代码和资源推送给设备。您还可以使用此 API 为您的 Android 免安装应用下载按需模块。
如需了解如何先在您的项目中添加功能模块并将其配置为按需提供,请参阅创建功能模块。
此外,阅读完本指南后,请尝试通过 Play Core API 示例应用了解 API 的实际用法,同时了解如何支持应用内更新。
最后,在发布应用之前测试您的 App Bundle 以验证应用的按需功能可否正常运行。
在您的项目中添加 Play Core 库
开始之前,您需要先将 Play Core 库导入您的项目。
请求按需模块
当您的应用需要使用功能模块时,它可以通过 SplitInstallManager
类在前台进行请求。在发起请求时,您的应用需要指定由目标模块清单中的 split
元素所定义的模块名称。当您使用 Android Studio 创建功能模块时,构建系统会使用您提供的模块名称,在编译时将该属性注入模块清单中。如需了解详情,请参阅功能模块清单。
例如,假设某个具有按需模块的应用可使用设备的相机拍摄和发送图片消息,并且此按需模块在其清单中指定了 split="pictureMessages"
。以下示例使用 SplitInstallManager
请求 pictureMessages
模块(以及包含一些宣传过滤器的另一个模块):
Kotlin
// Creates an instance of SplitInstallManager. val splitInstallManager = SplitInstallManagerFactory.create(context) // Creates a request to install a module. val request = SplitInstallRequest .newBuilder() // You can download multiple on demand modules per // request by invoking the following method for each // module you want to install. .addModule("pictureMessages") .addModule("promotionalFilters") .build() splitInstallManager // Submits the request to install the module through the // asynchronous startInstall() task. Your app needs to be // in the foreground to submit the request. .startInstall(request) // You should also be able to gracefully handle // request state changes and errors. To learn more, go to // the section about how to Monitor the request state. .addOnSuccessListener { sessionId -> ... } .addOnFailureListener { exception -> ... }
Java
// Creates an instance of SplitInstallManager. SplitInstallManager splitInstallManager = SplitInstallManagerFactory.create(context); // Creates a request to install a module. SplitInstallRequest request = SplitInstallRequest .newBuilder() // You can download multiple on demand modules per // request by invoking the following method for each // module you want to install. .addModule("pictureMessages") .addModule("promotionalFilters") .build(); splitInstallManager // Submits the request to install the module through the // asynchronous startInstall() task. Your app needs to be // in the foreground to submit the request. .startInstall(request) // You should also be able to gracefully handle // request state changes and errors. To learn more, go to // the section about how to Monitor the request state. .addOnSuccessListener(sessionId -> { ... }) .addOnFailureListener(exception -> { ... });
当您的应用请求按需模块时,Play Core 库会采用“即发即弃”策略。也就是说,它会发送请求以将该模块下载到平台,但不会监控安装是否成功。如需在安装后继续用户操作流程或妥善处理错误,请务必监控请求状态。
注意:您可以请求已安装在设备上的功能模块。如果检测到该模块已安装,API 会立即将该请求视为已完成。此外,安装模块后,Google Play 会自动使其保持最新状态。也就是说,当您上传新版 App Bundle 时,平台会更新所有属于您应用的已安装 APK。如需了解详情,请参阅管理应用更新。
如需访问模块的代码和资源,您的应用需要启用 SplitCompat。请注意,Android 免安装应用不需要使用 SplitCompat。
延迟安装按需模块
如果您不需要应用立即下载并安装按需模块,可以延迟到应用在后台运行时再安装该模块。例如,您想要预先加载一些宣传材料并在之后应用启动时使用这些材料。
您可以使用 deferredInstall()
方法指定之后需要下载的模块,如下所示。而且,与 SplitInstallManager.startInstall()
不同,您的应用无需在前台就可发起延迟安装请求。
Kotlin
// Requests an on demand module to be downloaded when the app enters // the background. You can specify more than one module at a time. splitInstallManager.deferredInstall(listOf("promotionalFilters"))
Java
// Requests an on demand module to be downloaded when the app enters // the background. You can specify more than one module at a time. splitInstallManager.deferredInstall(Arrays.asList("promotionalFilters"));
收到延迟安装请求后,系统将尽力而为,您无法跟踪其进度。因此,在尝试访问您已指定为延迟安装的模块之前,应检查该模块是否已安装。如果您需要立即使用该模块,请改为使用 SplitInstallManager.startInstall()
进行请求,如上一部分中所示。
监控请求状态
为了能够更新进度条、在安装后触发 Intent 或者妥善处理请求错误,您需要监听来自异步 SplitInstallManager.startInstall()
任务的状态更新。在开始接收安装请求更新之前,请先注册监听器并获取该请求的会话 ID,如下所示。
Kotlin
// Initializes a variable to later track the session ID for a given request. var mySessionId = 0 // Creates a listener for request status updates. val listener = SplitInstallStateUpdatedListener { state -> if (state.sessionId() == mySessionId) { // Read the status of the request to handle the state update. } } // Registers the listener. splitInstallManager.registerListener(listener) ... splitInstallManager .startInstall(request) // When the platform accepts your request to download // an on demand module, it binds it to the following session ID. // You use this ID to track further status updates for the request. .addOnSuccessListener { sessionId -> mySessionId = sessionId } // You should also add the following listener to handle any errors // processing the request. .addOnFailureListener { exception -> // Handle request errors. } // When your app no longer requires further updates, unregister the listener. splitInstallManager.unregisterListener(listener)
Java
// Initializes a variable to later track the session ID for a given request. int mySessionId = 0; // Creates a listener for request status updates. SplitInstallStateUpdatedListener listener = state -> { if (state.sessionId() == mySessionId) { // Read the status of the request to handle the state update. } }; // Registers the listener. splitInstallManager.registerListener(listener); ... splitInstallManager .startInstall(request) // When the platform accepts your request to download // an on demand module, it binds it to the following session ID. // You use this ID to track further status updates for the request. .addOnSuccessListener(sessionId -> { mySessionId = sessionId; }) // You should also add the following listener to handle any errors // processing the request. .addOnFailureListener(exception -> { // Handle request errors. }); // When your app no longer requires further updates, unregister the listener. splitInstallManager.unregisterListener(listener);
处理请求错误
您应使用 addOnFailureListener()
妥善处理下载或安装模块时出现的失败,如下所示:
Kotlin
splitInstallManager .startInstall(request) .addOnFailureListener { exception -> when ((exception as SplitInstallException).errorCode) { SplitInstallErrorCode.NETWORK_ERROR -> { // Display a message that requests the user to establish a // network connection. } SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED -> checkForActiveDownloads() ... } } fun checkForActiveDownloads() { splitInstallManager // Returns a SplitInstallSessionState object for each active session as a List. .sessionStates .addOnCompleteListener { task -> if (task.isSuccessful) { // Check for active sessions. for (state in task.result) { if (state.status() == SplitInstallSessionStatus.DOWNLOADING) { // Cancel the request, or request a deferred installation. } } } } }
Java
splitInstallManager .startInstall(request) .addOnFailureListener(exception -> { switch (((SplitInstallException) exception).getErrorCode()) { case SplitInstallErrorCode.NETWORK_ERROR: // Display a message that requests the user to establish a // network connection. break; case SplitInstallErrorCode.ACTIVE_SESSIONS_LIMIT_EXCEEDED: checkForActiveDownloads(); ... }); void checkForActiveDownloads() { splitInstallManager // Returns a SplitInstallSessionState object for each active session as a List. .getSessionStates() .addOnCompleteListener( task -> { if (task.isSuccessful()) { // Check for active sessions. for (SplitInstallSessionState state : task.getResult()) { if (state.status() == SplitInstallSessionStatus.DOWNLOADING) { // Cancel the request, or request a deferred installation. } } } }); }
下表介绍了您的应用可能需要处理的错误状态:
错误代码 | 说明 | 建议采取的措施 |
---|---|---|
ACTIVE_SESSIONS_LIMIT_EXCEEDED | 请求遭到拒绝,因为当前至少有一个请求正在下载。 | 检查是否有任何仍在下载的请求,如上例所示。 |
MODULE_UNAVAILABLE | Google Play 无法根据当前安装的应用版本、设备和用户的 Google Play 帐号找到所请求的模块。 | 如果用户无权访问该模块,请通知他们。 |
INVALID_REQUEST | Google Play 已收到请求,但该请求无效。 | 验证请求中包含的信息是否完整准确。 |
SESSION_NOT_FOUND | 找不到指定会话 ID 对应的会话。 | 如果您尝试通过会话 ID 监控请求的状态,请确保会话 ID 正确无误。 |
API_NOT_AVAILABLE | 当前设备不支持 Play Core 库。也就是说,该设备无法按需下载和安装功能。 | 对于搭载 Android 4.4(API 级别 20)或更低版本的设备,您应在安装时使用 dist:fusing 清单属性添加功能模块。如需了解详情,请参阅功能模块清单。 |
ACCESS_DENIED | 由于权限不足,应用无法注册该请求。 | 通常,当应用在后台运行时,会出现这种情况。在应用返回到前台时尝试请求。 |
NETWORK_ERROR | 由于出现网络连接错误,请求失败。 | 提示用户建立网络连接或更改为其他网络。 |
INCOMPATIBLE_WITH_EXISTING_SESSION | 该请求包含一个或多个已请求但尚未安装的模块。 | 创建一个新请求,该请求不包含应用已请求的模块,或等待所有当前已请求的模块完成安装,然后再重试请求。 请注意,请求已安装的模块无法解决错误。 |
SERVICE_DIED | 负责处理请求的服务已终止。 | 请重试请求。
此错误代码会作为对 |
如果用户请求下载按需模块并出现错误,请考虑显示一个对话框并为用户提供如下两个选项:重试(再次尝试该请求)和取消(放弃该请求)。如需其他支持,您还应该提供帮助链接,引导用户访问 Google Play 帮助中心。
处理状态更新
注册监听器并记录请求的会话 ID 后,请使用 StateUpdatedListener.onStateUpdate()
处理状态变更,如下所示。
Kotlin
override fun onStateUpdate(state : SplitInstallSessionState) { if (state.status() == SplitInstallSessionStatus.FAILED && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) { // Retry the request. return } if (state.sessionId() == mySessionId) { when (state.status()) { SplitInstallSessionStatus.DOWNLOADING -> { val totalBytes = state.totalBytesToDownload() val progress = state.bytesDownloaded() // Update progress bar. } SplitInstallSessionStatus.INSTALLED -> { // After a module is installed, you can start accessing its content or // fire an intent to start an activity in the installed module. // For other use cases, see access code and resources from installed modules. // If the request is an on demand module for an Android Instant App // running on Android 8.0 (API level 26) or higher, you need to // update the app context using the SplitInstallHelper API. } } } }
Java
@Override public void onStateUpdate(SplitInstallSessionState state) { if (state.status() == SplitInstallSessionStatus.FAILED && state.errorCode() == SplitInstallErrorCode.SERVICE_DIES) { // Retry the request. return; } if (state.sessionId() == mySessionId) { switch (state.status()) { case SplitInstallSessionStatus.DOWNLOADING: int totalBytes = state.totalBytesToDownload(); int progress = state.bytesDownloaded(); // Update progress bar. break; case SplitInstallSessionStatus.INSTALLED: // After a module is installed, you can start accessing its content or // fire an intent to start an activity in the installed module. // For other use cases, see access code and resources from installed modules. // If the request is an on demand module for an Android Instant App // running on Android 8.0 (API level 26) or higher, you need to // update the app context using the SplitInstallHelper API. } } }
安装请求的可能状态如下表所述。
请求状态 | 说明 | 建议采取的措施 |
---|---|---|
PENDING | 已接受该请求,即将开始下载。 | 初始化界面组件(例如进度栏),向用户提供关于下载的反馈。 |
REQUIRES_USER_CONFIRMATION | 下载需要用户确认。这很可能是由于下载内容大小超过 10 MB。 | 提示用户接受下载请求。如需了解详情,请转到有关如何获取用户确认的部分。 |
DOWNLOADING | 下载正在进行中。 | 如果您为下载提供了进度条,请使用 SplitInstallSessionState.bytesDownloaded() 和 SplitInstallSessionState.totalBytesToDownload() 方法更新界面(请参见此表上方的代码示例)。 |
DOWNLOADED | 设备已下载模块,但尚未开始安装。 | 应用应启用 SplitCompat,以便访问已下载的模块并避免出现此状态。必须执行此操作才能访问功能模块的代码和资源。 |
INSTALLING | 设备当前正在安装该模块。 | 更新进度条。此状态通常较短。 |
INSTALLED | 该模块已安装在设备上。 | 访问模块中的代码和资源以继续用户操作流程。 如果该模块针对的是在 Android 8.0(API 级别 26)或更高版本设备上运行的 Android 免安装应用,您需要使用 |
FAILED | 在模块安装到设备上之前,请求已失败。 | 提示用户重试请求或取消请求。 |
CANCELING | 设备正在取消请求。 | 如需了解详情,请转到有关如何取消安装请求的部分。 |
CANCELED | 请求已取消。 |
获取用户确认
在某些情况下,Google Play 在满足下载请求之前可能需要用户确认。例如,在请求需要下载大量内容,而设备使用的是移动数据网络的情况下。在这种情况下,请求的状态会报告 REQUIRES_USER_CONFIRMATION
,您的应用需要先获得用户确认,然后设备才能下载并安装请求的模块。如需获得确认,您的应用应按以下方式提示用户:
Kotlin
override fun onSessionStateUpdate(state: SplitInstallSessionState) { if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) { // Displays a dialog for the user to either “Download” // or “Cancel” the request. splitInstallManager.startConfirmationDialogForResult( state, /* activity = */ this, // You use this request code to later retrieve the user's decision. /* requestCode = */ MY_REQUEST_CODE) } ... }
Java
@Override void onSessionStateUpdate(SplitInstallSessionState state) { if (state.status() == SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION) { // Displays a dialog for the user to either “Download” // or “Cancel” the request. splitInstallManager.startConfirmationDialogForResult( state, /* activity = */ this, // You use this request code to later retrieve the user's decision. /* requestCode = */ MY_REQUEST_CODE); } ... }
请求的状态会根据用户响应进行更新:
- 如果用户选择“下载”,请求状态会更改为
PENDING
并继续下载。 - 如果用户选择“取消”,请求状态会更改为
CANCELED
。 - 如果用户在对话框被销毁之前未做出选择,请求状态会保持为
REQUIRES_USER_CONFIRMATION
。您的应用可能会再次提示用户完成请求。
如需通过用户的响应接收回调,请使用 onActivityResult()
,如下所示。
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { if (requestCode == MY_REQUEST_CODE) { // Handle the user's decision. For example, if the user selects "Cancel", // you may want to disable certain functionality that depends on the module. } }
Java
@Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == MY_REQUEST_CODE) { // Handle the user's decision. For example, if the user selects "Cancel", // you may want to disable certain functionality that depends on the module. } }
取消安装请求
如果您的应用需要在安装之前取消请求,它可以使用请求的会话 ID 调用 cancelInstall()
方法,如下所示。
Kotlin
SplitInstallManager // Cancels the request for the given session ID. .cancelInstall(mySessionId)
Java
SplitInstallManager // Cancels the request for the given session ID. .cancelInstall(mySessionId);
访问模块
如需在下载后从已下载的模块访问代码和资源,您的应用需要为应用和应用下载的功能模块中的每个 Activity 启用 SplitCompat 库。
不过请注意,在下载模块后的一段时间(在某些情况下是数天),该平台在访问模块内容时会受到以下限制:
- 平台无法应用模块引入的任何新的清单条目。
- 平台无法访问系统界面组件(如通知)的模块资源。如果您需要立即使用此类资源,请考虑将这些资源添加到应用的基本模块中。
启用 SplitCompat
为了让您的应用从已下载的模块访问代码和资源,您只需使用以下几部分中所述的其中一个方法启用 SplitCompat。
为您的应用启用 SplitCompat 后,您还需要为您希望应用访问的功能模块中的每个 Activity 启用 SplitCompat。
在清单中声明 SplitCompatApplication
如需启用 SplitCompat,最简单的方法是在您的应用清单中将 SplitCompatApplication
声明为 Application
子类,如下所示:
<application
...
android:name="com.google.android.play.core.splitcompat.SplitCompatApplication">
</application>
应用安装在设备上后,您可以自动从已下载的功能模块访问代码和资源。
在运行时调用 SplitCompat
您还可以在运行时在特定 Activity 或服务中启用 SplitCompat。如需以这种方式启用 SplitCompat,您需要启动功能模块中所包含的 Activity。为此,请替换 attachBaseContext
,如下所示。
如果您有自定义 Application 类,应使其改为扩展 SplitCompatApplication
,以便为您的应用启用 SplitCompat,如下所示:
Kotlin
class MyApplication : SplitCompatApplication() { ... }
Java
public class MyApplication extends SplitCompatApplication { ... }
SplitCompatApplication
仅会替换 ContextWrapper.attachBaseContext()
以包含 SplitCompat.install(Context applicationContext)
。如果您不想 Application
类扩展 SplitCompatApplication
,您可以手动替换 attachBaseContext()
方法,如下所示:
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) // Emulates installation of future on demand modules using SplitCompat. SplitCompat.install(this) }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // Emulates installation of future on demand modules using SplitCompat. SplitCompat.install(this); }
如果您的按需模块可同时与免安装应用和安装式应用兼容,您可以根据具体情况调用 SplitCompat,如下所示:
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) if (!InstantApps.isInstantApp(this)) { SplitCompat.install(this) } }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); if (!InstantApps.isInstantApp(this)) { SplitCompat.install(this); } }
为模块 Activity 启用 SplitCompat
为基本应用启用 SplitCompat 后,您需要为应用在功能模块中下载的每个 Activity 启用 SplitCompat。为此,请使用 SplitCompat.installActivity()
方法,如下所示:
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) // Emulates installation of on demand modules using SplitCompat. SplitCompat.installActivity(this) }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); // Emulates installation of on demand modules using SplitCompat. SplitCompat.installActivity(this); }
从已安装的模块访问代码和资源
只要您为基本应用上下文和功能模块中的 Activity 启用 SplitCompat,在按需模块的请求报告为 INSTALLED
后,您就可以开始使用其代码和资源,就像它是基本 APK 的一部分一样。
如果您想从应用的其他已安装模块访问新安装模块中存在的资源,您必须使用应用上下文执行此操作。尝试访问资源的组件上下文此时还不会更新。或者,您也可以在安装功能模块之后重新创建该组件或在其上安装 SplitCompat。
此外,请不要在您的应用中缓存 Android ApplicationInfo
对象及其内容或包含这些对象的对象。您应始终根据需要从应用上下文中提取这些对象。在较新版本的 Android 上安装 Split 时,缓存此类对象可能会导致应用崩溃。
访问已安装的 Android 免安装应用
当 Android 免安装应用模块报告为 INSTALLED
后,您可以使用刷新后的应用上下文访问其代码和资源。您的应用在安装模块之前创建的上下文(例如,已存储在变量中的上下文)不包含新模块的内容。不过,新的上下文包含此类内容,您可以使用 createPackageContext
获取新的上下文。
Kotlin
// Generate a new context as soon as a request for a new module // reports as INSTALLED. override fun onStateUpdate(state: SplitInstallSessionState ) { if (state.sessionId() == mySessionId) { when (state.status()) { ... SplitInstallSessionStatus.INSTALLED -> { val newContext = context.createPackageContext(context.packageName, 0) // If you use AssetManager to access your app’s raw asset files, you’ll need // to generate a new AssetManager instance from the updated context. val am = newContext.assets } } } }
Java
// Generate a new context as soon as a request for a new module // reports as INSTALLED. @Override public void onStateUpdate(SplitInstallSessionState state) { if (state.sessionId() == mySessionId) { switch (state.status()) { ... case SplitInstallSessionStatus.INSTALLED: Context newContext = context.createPackageContext(context.getPackageName(), 0); // If you use AssetManager to access your app’s raw asset files, you’ll need // to generate a new AssetManager instance from the updated context. AssetManager am = newContext.getAssets(); } } }
Android 8.0 及更高版本上的 Android 免安装应用
在 Android 8.0(API 级别 26)及更高版本的设备上请求 Android 免安装应用的按需模块时,在安装请求报告为 INSTALLED
后,您需要通过调用 SplitInstallHelper.updateAppInfo(Context context)
,使用新模块的上下文更新该应用。否则,应用不会知道该模块的代码和资源。更新应用的元数据后,您应通过调用新的 Handler
,在下一个主线程事件期间加载模块内容,如下所示:
Kotlin
override fun onStateUpdate(state: SplitInstallSessionState ) { if (state.sessionId() == mySessionId) { when (state.status()) { ... SplitInstallSessionStatus.INSTALLED -> { // You need to perform the following only for Android Instant Apps // running on Android 8.0 (API level 26) and higher. if (BuildCompat.isAtLeastO()) { // Updates the app’s context with the code and resources of the // installed module. SplitInstallHelper.updateAppInfo(context) Handler().post { // Loads contents from the module using AssetManager val am = context.assets ... } } } } } }
Java
@Override public void onStateUpdate(SplitInstallSessionState state) { if (state.sessionId() == mySessionId) { switch (state.status()) { ... case SplitInstallSessionStatus.INSTALLED: // You need to perform the following only for Android Instant Apps // running on Android 8.0 (API level 26) and higher. if (BuildCompat.isAtLeastO()) { // Updates the app’s context with the code and resources of the // installed module. SplitInstallHelper.updateAppInfo(context); new Handler().post(new Runnable() { @Override public void run() { // Loads contents from the module using AssetManager AssetManager am = context.getAssets(); ... } }); } } } }
加载 C/C++ 库
如果您想从设备已下载到免安装应用中的模块加载 C/C++ 库,请使用 SplitInstallHelper.loadLibrary(Context context, String libName)
,如下所示:
Kotlin
override fun onStateUpdate(state: SplitInstallSessionState) { if (state.sessionId() == mySessionId) { when (state.status()) { SplitInstallSessionStatus.INSTALLED -> { // Updates the app’s context as soon as a module is installed. val newContext = context.createPackageContext(context.packageName, 0) // To load C/C++ libraries from an installed module, use the following API // instead of System.load(). SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”) ... } } } }
Java
public void onStateUpdate(SplitInstallSessionState state) { if (state.sessionId() == mySessionId) { switch (state.status()) { case SplitInstallSessionStatus.INSTALLED: // Updates the app’s context as soon as a module is installed. Context newContext = context.createPackageContext(context.getPackageName(), 0); // To load C/C++ libraries from an installed module, use the following API // instead of System.load(). SplitInstallHelper.loadLibrary(newContext, “my-cpp-lib”); ... } } }
管理已安装模块
如需检查设备上当前已安装的功能模块,您可以调用 SplitInstallManager.getInstalledModules()
,它会返回已安装模块名称的 Set<String>
,如下所示。
Kotlin
val installedModules: Set<String> = splitInstallManager.installedModules
Java
Set<String> installedModules = splitInstallManager.getInstalledModules();
卸载模块
您可以通过调用 SplitInstallManager.deferredUninstall(List<String> moduleNames)
请求设备卸载模块,如下所示。
Kotlin
// Specifies two feature modules for deferred uninstall. splitInstallManager.deferredUninstall(listOf("pictureMessages", "promotionalFilters"))
Java
// Specifies two feature modules for deferred uninstall. splitInstallManager.deferredUninstall(Arrays.asList("pictureMessages", "promotionalFilters"));
模块卸载不会立即发生。也就是说,设备会根据需要在后台进行卸载,以节省存储空间。您可以通过调用 SplitInstallManager.getInstalledModules()
并检查结果以确认设备是否已删除模块,如上一部分中所述。
下载其他语言资源
通过 App Bundle,设备只会下载运行应用所需的代码和资源。因此,对于语言资源,用户的设备只会下载与设备设置中当前所选的一种或多种语言相符的应用语言资源。
如果您希望应用能够访问其他语言资源(例如,为了实现应用内语言选择器),您可以使用 Play Core 库根据需要下载这些资源。该流程与下载功能模块的流程相似,如下所示。
Kotlin
// Captures the user’s preferred language and persists it // through the app’s SharedPreferences. sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply() ... // Creates a request to download and install additional language resources. val request = SplitInstallRequest.newBuilder() // Uses the addLanguage() method to include French language resources in the request. // Note that country codes are ignored. That is, if your app // includes resources for “fr-FR” and “fr-CA”, resources for both // country codes are downloaded when requesting resources for "fr". .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))) .build() // Submits the request to install the additional language resources. splitInstallManager.startInstall(request)
Java
// Captures the user’s preferred language and persists it // through the app’s SharedPreferences. sharedPrefs.edit().putString(LANGUAGE_SELECTION, "fr").apply(); ... // Creates a request to download and install additional language resources. SplitInstallRequest request = SplitInstallRequest.newBuilder() // Uses the addLanguage() method to include French language resources in the request. // Note that country codes are ignored. That is, if your app // includes resources for “fr-FR” and “fr-CA”, resources for both // country codes are downloaded when requesting resources for "fr". .addLanguage(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))) .build(); // Submits the request to install the additional language resources. splitInstallManager.startInstall(request);
该请求的处理方式与功能模块请求的处理方式相同。也就是说,您可以像平常一样监控请求状态。
如果您的应用不需要立即使用其他语言资源,您可以延迟到应用在后台运行时再进行安装,如下所示。
Kotlin
splitInstallManager.deferredLanguageInstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
Java
splitInstallManager.deferredLanguageInstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
访问已下载的语言资源
如需访问已下载的语言资源,您的应用需要在需要访问这些资源的每个 Activity 的 attachBaseContext()
方法内运行 SplitCompat.installActivity()
方法,如下所示。
Kotlin
override fun attachBaseContext(base: Context) { super.attachBaseContext(base) SplitCompat.installActivity(this) }
Java
@Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); SplitCompat.installActivity(this); }
对于您想要使用应用已下载的语言资源的每个 Activity,请更新基本上下文并通过其 Configuration
设置新的语言区域:
Kotlin
override fun attachBaseContext(base: Context) { val configuration = Configuration() configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))) val context = base.createConfigurationContext(configuration) super.attachBaseContext(context) SplitCompat.install(this) }
Java
@Override protected void attachBaseContext(Context base) { Configuration configuration = new Configuration(); configuration.setLocale(Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION))); Context context = base.createConfigurationContext(configuration); super.attachBaseContext(context); SplitCompat.install(this); }
为使这些更改生效,您必须在新语言安装完毕且可供使用后重新创建 Activity。您可以使用 Activity#recreate()
方法。
Kotlin
when (state.status()) { SplitInstallSessionStatus.INSTALLED -> { // Recreates the activity to load resources for the new language // preference. activity.recreate() } ... }
Java
switch (state.status()) { case SplitInstallSessionStatus.INSTALLED: // Recreates the activity to load resources for the new language // preference. activity.recreate(); ... }
卸载其他语言资源
与功能模块类似,您可以随时卸载其他资源。在请求卸载之前,您可能需要先确定当前安装的语言,如下所示。
Kotlin
val installedLanguages: Set<String> = splitInstallManager.installedLanguages
Java
Set<String> installedLanguages = splitInstallManager.getInstalledLanguages();
然后,您可以使用 deferredLanguageUninstall()
方法确定需要卸载的语言,如下所示。
Kotlin
splitInstallManager.deferredLanguageUninstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)))
Java
splitInstallManager.deferredLanguageUninstall( Locale.forLanguageTag(sharedPrefs.getString(LANGUAGE_SELECTION)));
其他资源
如需详细了解如何使用 Play Core 库,请尝试以下资源。
示例
- PlayCore API 示例:演示了如何使用 PlayCore API 请求和下载按需功能。
- 动态代码加载示例:演示了从安装的功能模块安全访问代码的三种不同方法。
Codelab
- 按需模块:可帮助您打造按需下载和安装功能的应用。