🧩 一、前提条件
- 主应用已启用 App Bundle(即使用
.aab发布) build.gradle (Project)中使用 Android Gradle Plugin 3.2+- 在主模块的
build.gradle中启用了动态交付支持(通常默认开启)
less
// app/build.gradle
android {
dynamicFeatures = [":dynamic_feature"] // 声明动态模块
}
🛠️ 二、创建 Dynamic Feature Module
步骤 1:在 Android Studio 中创建模块
-
菜单栏:File > New > New Module
-
选择 Dynamic Feature Module
-
配置:
- Module name :
dynamic_feature - Package name :
com.example.myapp.dynamic_feature - Minimum API level: 与主模块一致(建议 ≥ 21)
- Title: "高级工具"(可选,用于 Play 商店展示)
- Module name :
-
点击 Finish
✅ 此操作会自动生成一个带
SplitCompat支持的模块,并在主模块的dynamicFeatures中自动注册(若未自动添加,请手动添加如上所示)。
📄 三、编写动态模块内容
1. 创建 Activity(DynamicActivity.kt)
kotlin
// dynamic_feature/src/main/java/com/example/myapp/dynamic_feature/DynamicActivity.kt
package com.example.myapp.dynamic_feature
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.myapp.dynamic_feature.databinding.ActivityDynamicBinding
class DynamicActivity : AppCompatActivity() {
private lateinit var binding: ActivityDynamicBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDynamicBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.textView.text = "这是动态加载的功能模块!"
binding.buttonBack.setOnClickListener { finish() }
}
}
2. 添加布局文件(activity_dynamic.xml)
ini
<!-- dynamic_feature/src/main/res/layout/activity_dynamic.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
<Button
android:id="@+id/buttonBack"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="返回" />
</LinearLayout>
3. 在 AndroidManifest.xml 中声明 Activity
xml
<!-- dynamic_feature/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<activity
android:name=".DynamicActivity"
android:exported="false" />
</application>
</manifest>
⚠️ 注意:不要设置
android:exported="true",除非你明确需要外部启动。
🔌 四、在主模块中请求安装并启动动态模块
1. 添加依赖(通常已自动添加)
确保主模块的 build.gradle 包含:
arduino
implementation 'com.google.android.play:core-ktx:1.8.1' // 推荐使用 KTX
// 或基础版
implementation 'com.google.android.play:core:1.10.3'
2. 编写加载逻辑(例如在 MainActivity 中)
kotlin
// app/src/main/java/com/example/myapp/MainActivity.kt
package com.example.myapp
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.pm.PackageInfoCompat
import com.example.myapp.databinding.ActivityMainBinding
import com.google.android.play.core.splitinstall.SplitInstallManagerFactory
import com.google.android.play.core.splitinstall.SplitInstallRequest
import com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
import com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var splitInstallManager: SplitInstallManager
private lateinit var listener: SplitInstallStateUpdatedListener
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
splitInstallManager = SplitInstallManagerFactory.create(this)
// 监听安装状态
listener = SplitInstallStateUpdatedListener { state ->
when (state.status()) {
SplitInstallSessionStatus.INSTALLED -> {
Toast.makeText(this, "模块安装成功!", Toast.LENGTH_SHORT).show()
startDynamicActivity()
}
SplitInstallSessionStatus.FAILED -> {
Toast.makeText(this, "模块安装失败: ${state.errorCode()}", Toast.LENGTH_LONG).show()
}
SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION -> {
// 可选:处理需要用户确认的情况(如大体积模块)
splitInstallManager.startConfirmationDialogForResult(
state,
this,
REQ_CODE_CONFIRMATION
)
}
}
}
binding.buttonLoadFeature.setOnClickListener {
loadAndLaunchDynamicFeature()
}
}
private fun loadAndLaunchDynamicFeature() {
if (isDynamicFeatureInstalled()) {
startDynamicActivity()
} else {
val request = SplitInstallRequest.newBuilder()
.addModule("dynamic_feature") // 模块名必须与 build.gradle 中的模块名一致
.build()
splitInstallManager.registerListener(listener)
splitInstallManager.startInstallation(request)
}
}
private fun isDynamicFeatureInstalled(): Boolean {
return splitInstallManager.installedModules.contains("dynamic_feature")
}
private fun startDynamicActivity() {
try {
val intent = Intent().setClassName(
packageName,
"com.example.myapp.dynamic_feature.DynamicActivity"
)
startActivity(intent)
} catch (e: Exception) {
Toast.makeText(this, "无法启动动态模块", Toast.LENGTH_SHORT).show()
}
}
override fun onDestroy() {
splitInstallManager.unregisterListener(listener)
super.onDestroy()
}
companion object {
private const val REQ_CODE_CONFIRMATION = 1001
}
}
3. 添加按钮到主界面布局
xml
<!-- app/src/main/res/layout/activity_main.xml -->
<Button
android:id="@+id/buttonLoadFeature"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="加载动态功能" />
🧪 五、本地测试方法
由于 .aab 不能直接安装,需用 bundletool 生成 APK 并安装:
ini
# 1. 构建 aab
./gradlew bundleDebug
# 2. 生成 apks(需签名)
java -jar bundletool.jar build-apks --bundle=app/build/outputs/bundle/debug/app-debug.aab --output=app.apks --mode=default
# 3. 安装到设备
java -jar bundletool.jar install-apks --apks=app.apks
如果你只是调试,也可以在 Android Studio Run 时选择 "Deploy as instant app" 或使用 Internal App Sharing 快速测试。
📦 六、发布注意事项
- 动态模块会随主 App 一起上传到 Google Play Console
- 用户首次使用该功能时才会下载对应模块(节省初始安装体积)
- 模块大小建议 ≤ 10MB,否则可能触发用户确认弹窗
- 可结合 on-demand delivery (按需)、conditional delivery (条件分发)、instant delivery(免安装体验)使用
✅ 总结
| 步骤 | 内容 |
|---|---|
| 1 | 创建 Dynamic Feature Module |
| 2 | 编写模块内的 Activity 和资源 |
| 3 | 主模块通过 Play Core Library 请求安装 |
| 4 | 安装成功后通过类名启动 Activity |
| 5 | 使用 bundletool 或 Play Console 测试 |