Closed LeonWu6 closed 1 year ago
大致浏览之后,我是比较相信这里是遇到bug了的。因为PreferenceFragmentCompat应该只是个androidx的fragment,怎么都不应该被实例化成Activity(ShadowActivity)的。所以问题可能出在ShadowAppComponentFactory上。
androidx不需要特别支持,所以我们应该找出它依赖的系统api有什么需要支持的。
如果你不能自己解决这个问题,最好fork项目后在sample或test工程里用最少代码复现一下这个问题。
androidx相关的例子可以在这个插件工程里添加代码: https://github.com/Tencent/Shadow/tree/master/projects/test/plugin/androidx-cases/test-plugin-androidx-cases
Hi @shifujun 由于我们公司网络限制,无法 push code changes 到 github 上,请辛苦浏览复制以下复现问题的测试代码。 我在 Sample 里面加了一个 testcase,可复现此 Issue 所说的问题。 请看下面的 patch
diff --git a/projects/sample/source/sample-plugin/sample-app/build.gradle b/projects/sample/source/sample-plugin/sample-app/build.gradle
index 5bb93f01..85546e31 100644
--- a/projects/sample/source/sample-plugin/sample-app/build.gradle
+++ b/projects/sample/source/sample-plugin/sample-app/build.gradle
@@ -18,6 +18,7 @@ buildscript {
apply plugin: 'com.android.application'
apply plugin: 'com.tencent.shadow.plugin'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion project.COMPILE_SDK_VERSION
@@ -68,6 +69,7 @@ dependencies {
//Shadow Transform后业务代码会有一部分实际引用runtime中的类
//如果不以compileOnly方式依赖,会导致其他Transform或者Proguard找不到这些类
pluginCompileOnly 'com.tencent.shadow.core:runtime'
+ implementation 'androidx.preference:preference:1.2.0'
}
preBuild.dependsOn(":sample-host-lib:jarDebugPackage")
diff --git a/projects/sample/source/sample-plugin/sample-app/src/main/AndroidManifest.xml b/projects/sample/source/sample-plugin/sample-app/src/main/AndroidManifest.xml
index abac57e8..83fbc3c7 100644
--- a/projects/sample/source/sample-plugin/sample-app/src/main/AndroidManifest.xml
+++ b/projects/sample/source/sample-plugin/sample-app/src/main/AndroidManifest.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.tencent.shadow.sample.plugin.app.lib">
<uses-feature android:glEsVersion="0x00020000" />
@@ -62,6 +63,8 @@
<activity android:name=".usecases.host_communication.PluginUseHostClassActivity" />
<activity android:name=".usecases.webview.WebViewActivity" />
<activity android:name=".usecases.fragment.TestDialogFragmentActivity" />
+ <activity android:name=".usecases.preferencefragmentcompatcase.RfFragment"
+ tools:ignore="Instantiatable" />
<provider
android:authorities="${applicationId}.provider.test"
diff --git a/projects/sample/source/sample-plugin/sample-app/src/main/java/com/tencent/shadow/sample/plugin/app/lib/UseCaseApplication.java b/projects/sample/source/sample-plugin/sample-app/src/main/java/com/tencent/shadow/sample/plugin/app/lib/UseCaseApplication.java
index 93c05284..3f43f005 100644
--- a/projects/sample/source/sample-plugin/sample-app/src/main/java/com/tencent/shadow/sample/plugin/app/lib/UseCaseApplication.java
+++ b/projects/sample/source/sample-plugin/sample-app/src/main/java/com/tencent/shadow/sample/plugin/app/lib/UseCaseApplication.java
@@ -22,6 +22,7 @@ import com.tencent.shadow.sample.plugin.app.lib.usecases.fragment.TestDynamicFra
import com.tencent.shadow.sample.plugin.app.lib.usecases.fragment.TestXmlFragmentActivity;
import com.tencent.shadow.sample.plugin.app.lib.usecases.host_communication.PluginUseHostClassActivity;
import com.tencent.shadow.sample.plugin.app.lib.usecases.packagemanager.TestPackageManagerActivity;
+import com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase.RfFragment;
import com.tencent.shadow.sample.plugin.app.lib.usecases.provider.TestDBContentProviderActivity;
import com.tencent.shadow.sample.plugin.app.lib.usecases.provider.TestFileProviderActivity;
import com.tencent.shadow.sample.plugin.app.lib.usecases.receiver.TestDynamicReceiverActivity;
@@ -51,7 +52,8 @@ public class UseCaseApplication extends Application {
new TestActivityWindowSoftMode.Case(),
new TestActivitySetTheme.Case(),
new TestActivityOptionMenu.Case(),
- new WebViewActivity.Case()
+ new WebViewActivity.Case(),
+ new RfFragment.Case()
});
useCases.add(activityCategory);
@@ -97,5 +99,8 @@ public class UseCaseApplication extends Application {
new PluginUseHostClassActivity.Case(),
});
useCases.add(communicationCategory);
+ UseCaseCategory PreferenceFragmentCompatCategory = new UseCaseCategory("PreferenceFragmentCompat嵌套用例",
+ new UseCase[]{new RfFragment.Case(),});
+ useCases.add(PreferenceFragmentCompatCategory);
}
}
diff --git a/projects/sample/source/sample-plugin/sample-app/src/main/res/values/strings.xml b/projects/sample/source/sample-plugin/sample-app/src/main/res/values/strings.xml
index 8cb0af94..289eb0cb 100644
--- a/projects/sample/source/sample-plugin/sample-app/src/main/res/values/strings.xml
+++ b/projects/sample/source/sample-plugin/sample-app/src/main/res/values/strings.xml
@@ -20,5 +20,9 @@
<!-- Simple strings. -->
<string name="app_name">Shadow主测试用例集合</string>
<string name="host_add_plugin_view">这是插件中的string资源</string>
+ <string name="nv_backup_title">NV Backup</string>
+ <string name="rf_items">RF Module</string>
+ <string name="qti_nv">NV parameters</string>
+ <string name="qti_adjust">Calibration status</string>
</resources>
$ git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add
Untracked files:
(use "git add
------------------------------------------------------------------------------------------------------
2.1 com/tencent/shadow/sample/plugin/app/lib/usecases/preferencefragmentcompatcase/RfFragment.kt
package com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase
import android.os.Bundle import com.tencent.shadow.sample.plugin.app.lib.R import com.tencent.shadow.sample.plugin.app.lib.gallery.cases.entity.UseCase
class RfFragment : EngineerFragmentCompat() {
class Case : UseCase() {
override fun getName(): String {
return "PreferenceFragmentCompat 嵌套测试"
}
override fun getSummary(): String {
return "测试PreferenceFragmentCompat嵌套"
}
override fun getPageClass(): Class<*> {
return RfFragment::class.java
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.fragment_rf_container)
}
}
2.2 com/tencent/shadow/sample/plugin/app/lib/usecases/preferencefragmentcompatcase/NVBackupUI.kt
package com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase import android.os.Bundle import android.util.Log import androidx.preference.PreferenceFragmentCompat import com.tencent.shadow.sample.plugin.app.lib.R
class NVBackupUI : PreferenceFragmentCompat() {
private val TAG = "NVBackupUI"
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
Log.d(TAG, "onCreatePreferences")
addPreferencesFromResource(R.xml.qti_nv_options)
}
}
2.3 com/tencent/shadow/sample/plugin/app/lib/usecases/preferencefragmentcompatcase/EngineerFragmentCompat.kt
package com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase
import android.content.Context import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceScreen import android.util.Log import androidx.annotation.RequiresApi import com.tencent.shadow.sample.plugin.app.lib.R
open class EngineerFragmentCompat : PreferenceFragmentCompat() {
private val TAG = "EngineerFragmentCompat"
private var mContext: Context? = null
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
Log.d(TAG, "onCreatePreferences")
}
override fun onAttach(p0: Context) {
super.onAttach(p0)
mContext = p0
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val preferenceScreen: PreferenceScreen = preferenceManager.preferenceScreen
for (i in 0 until preferenceScreen.preferenceCount) {
val preference = preferenceScreen.getPreference(i)
val intent = preference.intent
if (intent != null) {
preference.isEnabled = true
} else {
Log.w(TAG, "onViewCreated preference is not available for i: $i")
preference.isEnabled = false
}
}
}
/**
* 实例化 Fragment 来显示视图,这将在 onCreate 和 onViewCreated 之间调用。
* */
@RequiresApi(Build.VERSION_CODES.M)
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val view: View = super.onCreateView(inflater, container, savedInstanceState)
view.setBackgroundColor(
resources.getColor(R.color.material_grey_850, mContext?.theme)
)
return view
}
override fun getCallbackFragment(): Fragment {
return this
}
}
2.4 xml/fragment_rf_container.xml
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:key="rf_container" android:title="@string/rf_items">
<androidx.preference.Preference
android:key="nv_backup"
android:title="@string/nv_backup_title">
<intent
android:action="android.intent.action.MAIN"
android:targetClass="com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase.NVBackupUI"
android:targetPackage="com.tencent.shadow.sample.host" />
</androidx.preference.Preference>
2.5 xml/qti_nv_options.xml
<?xml version="1.0" encoding="utf-8"?>
3. 测试方法:
基于本代码 commit chagnes
安装 sample-host:installDebug
进入 Shadow Dynamic测试宿主App
点击 启动插件
左边栏选择最后一项:PreferenceFragmentCompat嵌套用例,点击“启动”
4. 异常出现:
插件APP 闪退,UI界面回到宿主UI,com.tencent.shadow.sample.host:plugin Crashed
AndroidRuntime: Shutting down VM
--------- beginning of crash
AndroidRuntime: FATAL EXCEPTION: main
AndroidRuntime: Process: com.tencent.shadow.sample.host:plugin, PID: 27763
AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tencent.shadow.sample.host/com.tencent.shadow.sample.plugin.runtime.PluginDefaultProxyActivity}: java.lang.RuntimeException: java.lang.ClassCastException: com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase.RfFragment cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity
AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3876)
AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4018)
AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:111)
AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2474)
AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:240)
AndroidRuntime: at android.os.Looper.loop(Looper.java:351)
AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8426)
AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584)
AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)
AndroidRuntime: Caused by: java.lang.RuntimeException: java.lang.ClassCastException: com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase.RfFragment cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity
AndroidRuntime: at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:159)
AndroidRuntime: at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:84)
AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8460)
AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8433)
AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1403)
AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3849)
AndroidRuntime: ... 12 more
AndroidRuntime: Caused by: java.lang.ClassCastException: com.tencent.shadow.sample.plugin.app.lib.usecases.preferencefragmentcompatcase.RfFragment cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity
AndroidRuntime: at com.tencent.shadow.core.runtime.ShadowAppComponentFactory.instantiateActivity(ShadowAppComponentFactory.java:18)
AndroidRuntime: at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45)
AndroidRuntime: at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:120)
AndroidRuntime: ... 17 more
你前面这个patch我复制下来倒是能像你说的那样复现一个RfFragment cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity
的crash。但这个看起来是非常正常的,因为你写了:
override fun getPageClass(): Class<*> {
return RfFragment::class.java
}
这是把一个车fragment填到intent里去startActivity了。(https://github.com/Tencent/Shadow/blob/f5800f3a7ff3ddf1edae1b5d78bfc8079ce2f6ae/projects/sample/source/sample-plugin/sample-base-lib/src/main/java/com/tencent/shadow/sample/plugin/app/lib/gallery/cases/UseCaseSummaryFragment.java#L65 )
如果这就是你说的原本的问题,那肯定是你的业务代码哪写错了,把fragment当activity去start了。
另外你代码中也没有NVBackupUIActivity
,看起来莫名其妙的。
特意指明让你在test-plugin-androidx-cases中复现问题就是避免sample-app中依赖的support包和你要添加的androidx冲突。否则你这个例子现在sample-app能单独运行吗?我这很正常的报Duplicate class found了。如果你能运行,我估计也会很正常的报类错误吧。改在test-plugin-androidx-cases中试试吧。test-plugin-androidx-cases和sample-app一样,test-dynamic-host和sample-host一样,只是这些test工程有自动化测试。
另外这样贴代码绝对不是正常的交流方式,你只是赶上我有空贴一下patch而已。你下次还是push上来吧。最不济也可以push到国内的其他公开git仓库上。
把fragment当activity去start了。
Hi @shifujun 确实,我是把 fragment 当作 activity 去start 了,导致了原来的问题。 感谢您的指导!
另外,回答您的 comment 中的问题:
另外你代码中也没有NVBackupUIActivity,看起来莫名其妙的。 因为我贴代码时,认为只要调用到第二层嵌套的 PreferenceFragmentCompat UI 即可,不会再进到 NVBackupUIActivity,所以我没有把 NVBackupUIActivity 的代码贴出来。
谢谢!
在插件APP中, 第一个 UI 是继承 PreferenceFragmentCompat 的,然后通过 Intent 拉起第二层UI,第二层UI 也是继承 PreferenceFragmentCompat 的。 当拉起第二层 UI 时,会报如下异常:
AndroidRuntime: Shutting down VM --------- beginning of crash AndroidRuntime: FATAL EXCEPTION: main AndroidRuntime: Process: com.oplus.en:plugin, PID: 29307 AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.oplus.en/com.oplus.en.runtime.PluginDefaultProxyActivity}: java.lang.RuntimeException: java.lang.ClassCastException: com.oplus.en.cellular_app.rf.nvbackupui.NVBackupUI cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3876) AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4018) AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:111) AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135) AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95) AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2474) AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106) AndroidRuntime: at android.os.Looper.loopOnce(Looper.java:240) AndroidRuntime: at android.os.Looper.loop(Looper.java:351) AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:8426) AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method) AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:584) AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013) AndroidRuntime: Caused by: java.lang.RuntimeException: java.lang.ClassCastException: com.oplus.en.cellular_app.rf.nvbackupui.NVBackupUI cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity AndroidRuntime: at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:159) AndroidRuntime: at com.tencent.shadow.core.runtime.container.PluginContainerActivity.onCreate(PluginContainerActivity.java:84) AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8460) AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8433) AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1403) AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3849) AndroidRuntime: ... 12 more AndroidRuntime: Caused by: java.lang.ClassCastException: com.oplus.en.cellular_app.rf.nvbackupui.NVBackupUI cannot be cast to com.tencent.shadow.core.runtime.ShadowActivity AndroidRuntime: at com.tencent.shadow.core.runtime.ShadowAppComponentFactory.instantiateActivity(ShadowAppComponentFactory.java:18) AndroidRuntime: at androidx.core.app.CoreComponentFactory.instantiateActivity(CoreComponentFactory.java:45) AndroidRuntime: at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.onCreate(ShadowActivityDelegate.kt:120) AndroidRuntime: ... 17 more
是 Shadow 框架无法支持嵌套 PreferenceFragmentCompat 吗?
代码流程:
继承关系 RfFragment 继承于 EngineerFragmentCompat, EngineerFragmentCompat 继承于 PreferenceFragmentCompat
调用关系 RfFragment 通过 intent 调用起 NVBackupUI NVBackupUI 也是继承于 PreferenceFragmentCompat
当通过 RfFragment 调起 NVBackupUI 时,异常发生。
下面是代码:
EngineerFragmentCompat.kt (基类,继承于PreferenceFragmentCompat)
open class EngineerFragmentCompat : PreferenceFragmentCompat() { private val mLogger: Logger = LoggerFactory.getLogger(EngineerFragmentCompat::class.java) private var mContext: Context? = null
}
package com.oplus.en.cellular_app import android.os.Bundle class RfFragment : EngineerFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.fragment_rf_container) } }
<?xml version="1.0" encoding="utf-8"?> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" android:key="rf_container" android:title="@string/rf_items"> <androidx.preference.Preference android:key="nv_backup" android:title="@string/nv_backup_title"> <intent android:action="android.intent.action.MAIN" android:targetClass="com.oplus.en.cellular_app.rf.nvbackupui.NVBackupUI" android:targetPackage="com.oplus.en" /> </androidx.preference.Preference>
package com.oplus.en.cellular_app.rf.nvbackupui import android.os.Bundle import androidx.preference.PreferenceFragmentCompat import com.oplus.en.cellular_app.R
class NVBackupUI : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { addPreferencesFromResource(R.xml.qti_nv_options) } }
<?xml version="1.0" encoding="utf-8"?>