我正在关注谷歌示例以在我的应用程序中使用动态交付功能。我的示例应用程序在 pie 上运行良好,但在 marshmallow 6.0.1 自定义 rom 上崩溃,因为我的选项卡在 kitkat 4.4.4 之后不支持官方更新。我在内部测试中上传了应用程序(.abb),从我的手机(os pie)和我的标签(os marshmallow)上的Play商店下载,我在两者上运行应用程序,下载动态模块,成功下载后,我启动模块,模块在 Pie 上启动,但在 Marshmallow 上崩溃,找不到资源异常。经过一些研究,stackoverflow中有人说我只是更新版本代码并重新上传应用程序,它工作正常,所以我尝试修改版本代码并测试它在棉花糖上工作正常。
有人可以帮我看看我在这里缺少什么吗?
清单(动态模块)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dist="http://schemas.android.com/apk/distribution"
xmlns:tools="http://schemas.android.com/tools"
package="com.testdynamicdelivery.ondemand.kotlin_test">
<dist:module
dist:instant="false"
dist:title="@string/title_kotlin_test">
<dist:delivery>
<dist:on-demand />
</dist:delivery>
<dist:fusing dist:include="true" />
</dist:module>
<application
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.testdynamicdelivery.ondemand.TestKotlin">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
</intent-filter>
</activity>
</application>
</manifest>
构建 Gradle 文件(Dunamic 模块)
apply plugin: 'com.android.dynamic-feature'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 19
targetSdkVersion 29
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':app')
}
TestKotlin 活动(动态模块)
class TestKotlin : AppCompatActivity() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
// Emulates installation of on demand modules using SplitCompat.
SplitCompat.installActivity(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_kotlin)
}
}
应用程序文件(基础应用程序)
class MyApplication : SplitCompatApplication() {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
SplitCompat.install(this)
}
}
BaseSplitActivity(基础应用程序)
abstract class BaseSplitActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
super.attachBaseContext(newBase)
SplitCompat.install(this)
}
}
主要活动(基础应用程序)
private const val CONFIRMATION_REQUEST_CODE = 1
private const val PACKAGE_NAME = "com.testdynamicdelivery"
private const val PACKAGE_NAME_ONDEMAND = "$PACKAGE_NAME.ondemand"
private const val KOTLIN_SAMPLE_CLASSNAME = "$PACKAGE_NAME_ONDEMAND.TestKotlin"
class MainActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase)
SplitCompat.install(this)
}
/** Listener used to handle changes in state for install requests. */
private val listener = SplitInstallStateUpdatedListener { splitInstallSessionState ->
when (splitInstallSessionState.status()) {
SplitInstallSessionStatus.PENDING ->{
sb.append("\nPending Feature")
setText()
}
SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION ->{
/*
This may occur when attempting to download a sufficiently large module.
In order to see this, the application has to be uploaded to the Play Store.
Then features can be requested until the confirmation path is triggered.
*/
manager.startConfirmationDialogForResult(splitInstallSessionState, this, CONFIRMATION_REQUEST_CODE)
}
SplitInstallSessionStatus.DOWNLOADED ->{
sb.append("\nFeature Downloaded.")
setText()
}
SplitInstallSessionStatus.CANCELING ->{
sb.append("\nCanceling Feature.")
setText()
}
SplitInstallSessionStatus.INSTALLED -> {
sb.append("\nFeature Installed.")
setText()
}
SplitInstallSessionStatus.CANCELED ->
{
sb.append("\nCanceled Feature.")
setText()
}
SplitInstallSessionStatus.DOWNLOADING ->
{
sb.append("\nDownloading Feature...")
progressBar.max = splitInstallSessionState.totalBytesToDownload().toInt()
progressBar.progress = splitInstallSessionState.bytesDownloaded().toInt()
setText()
}
SplitInstallSessionStatus.INSTALLING ->
{
sb.append("\nInstalling Feature...")
setText()
}
SplitInstallSessionStatus.UNKNOWN ->
{
sb.append("\nError: ${splitInstallSessionState.errorCode()} for module ${splitInstallSessionState.moduleNames()}")
setText()
}
SplitInstallSessionStatus.FAILED ->
{
sb.append("\nFailed ${splitInstallSessionState.errorCode()} , ${splitInstallSessionState.moduleNames()}")
setText()
}
}
/*if (splitInstallSessionState.sessionId() == mySessionID) {
}*/
}
/** This is needed to handle the result of the manager.startConfirmationDialogForResult
request that can be made from SplitInstallSessionStatus.REQUIRES_USER_CONFIRMATION
in the listener above. */
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == CONFIRMATION_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.
if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(context, "You Canceled To Download!", Toast.LENGTH_LONG).show()
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private lateinit var context: Context
private lateinit var manager: SplitInstallManager
private lateinit var progressBar: ProgressBar
private var mySessionID: Int = 0
private val sb = StringBuilder()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
context = this
manager = SplitInstallManagerFactory.create(this)
progressBar = findViewById(R.id.progress_bar)
sb.append("Logs: Version: ${BuildConfig.VERSION_NAME}, Code: ${BuildConfig.VERSION_CODE}")
setText()
setClickListeners()
}
private fun displayProgress() {
progressBar.visibility = View.VISIBLE
}
private fun setClickListeners()
{
btn_feature_kotlin.setOnClickListener {
if (manager.installedModules.contains("kotlin_test"))
{
sb.append("\nStarting $KOTLIN_SAMPLE_CLASSNAME")
setText()
launchActivity(KOTLIN_SAMPLE_CLASSNAME)
}
else
{
sb.append("\nDownloading Feature Kotlin")
setText()
downloadDynamicModule("kotlin_test")
}
}
}
override fun onResume() {
// Listener can be registered even without directly triggering a download.
manager.registerListener(listener)
super.onResume()
}
override fun onPause() {
// Make sure to dispose of the listener once it's no longer needed.
manager.unregisterListener(listener)
super.onPause()
}
private fun downloadDynamicModule(module: String) {
displayProgress()
val request = SplitInstallRequest
.newBuilder()
.addModule(module)
.build()
manager.registerListener(listener)
manager.startInstall(request)
.addOnFailureListener {
sb.append("\nException: ${it.message}")
setText()
println("Exception: $it")
}
.addOnSuccessListener { sessionId ->
sb.append("\nSuccess getting session id: $sessionId")
setText()
mySessionID = sessionId
}
.addOnCompleteListener {
sb.append("\naddOnCompleteListener")
setText()
}
}
private fun setText()
{
tv_logs.text = sb.toString()
scroll_view.post { scroll_view.fullScroll(ScrollView.FOCUS_DOWN) }
}
/** Launch an activity by its class name. */
private fun launchActivity(className: String) {
val intent = Intent().setClassName(BuildConfig.APPLICATION_ID, className)
startActivity(intent)
}
}
构建 Gradle 文件(基础应用程序)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId 'com.testdynamicdelivery.ondemand'
minSdkVersion 19
targetSdkVersion 29
versionCode 28
versionName '1.2.8'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
dynamicFeatures = [":features:kotlin_test"]
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// Play Core
api 'com.google.android.play:core:1.6.4'
// Support Library
//implementation 'com.android.support:appcompat-v7:28.0.0'
// Android X Libraries
api 'androidx.appcompat:appcompat:1.1.0'
api 'androidx.core:core-ktx:1.1.0'
api 'androidx.legacy:legacy-support-v4:1.0.0'
api "androidx.lifecycle:lifecycle-extensions:2.1.0"
api 'androidx.constraintlayout:constraintlayout:1.1.3'
api 'androidx.swiperefreshlayout:swiperefreshlayout:1.0.0'
api 'androidx.gridlayout:gridlayout:1.0.0'
api 'androidx.cardview:cardview:1.0.0'
api 'androidx.recyclerview:recyclerview:1.1.0'
}