Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

部分Android14系统上的崩溃问题:lateinit property _pluginClassLoader has not been initialized #1338

Open
xiboliya opened this issue Jul 17, 2024 · 11 comments

Comments

@xiboliya
Copy link

我们发现有部分荣耀手机升级到Android14系统后,跳转插件页面时会必现崩溃,卸载重新安装APP也不行。
这个问题是我们崩溃后台收集线上用户的崩溃,我们用Android14的荣耀手机没有复现,当然与用户崩溃的机型不同,所以这可能是某些机型上才会出现的问题。
崩溃堆栈如下:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.demo/com.demo.pluginruntime.PluginSingleTaskActivity1}: kotlin.UninitializedPropertyAccessException: lateinit property _pluginClassLoader has not been initialized
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4610)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4806)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:118)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:153)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:104)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:3067)
at android.os.Handler.dispatchMessage(Handler.java:117)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:302)
at android.app.ActivityThread.main(ActivityThread.java:9652)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:601)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1062)
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property _pluginClassLoader has not been initialized
at com.tencent.shadow.core.loader.delegates.ShadowDelegate.getMPluginClassLoader(ShadowDelegate.kt:60)
at com.tencent.shadow.core.loader.delegates.ShadowActivityDelegate.getClassLoader(ShadowActivityDelegate.kt:288)
at com.tencent.shadow.core.runtime.container.GeneratedPluginContainerActivity.getClassLoader(GeneratedPluginContainerActivity.java:124)
at com.tencent.shadow.core.runtime.container.PluginContainerActivity.getClassLoader(PluginContainerActivity.java:38)
at android.view.LayoutInflater.checkPluginContext(LayoutInflater.java:321)
at android.view.LayoutInflater.(LayoutInflater.java:291)
at com.android.internal.policy.PhoneLayoutInflater.(PhoneLayoutInflater.java:44)
at com.android.internal.policy.HwPhoneLayoutInflater.(HwPhoneLayoutInflater.java:69)
at com.android.internal.policy.HwPolicyFactoryImpl.getHwPhoneLayoutInflater(HwPolicyFactoryImpl.java:52)
at com.android.internal.policy.HwPolicyFactory.getHwPhoneLayoutInflater(HwPolicyFactory.java:65)
at android.app.SystemServiceRegistry$34.createService(SystemServiceRegistry.java:584)
at android.app.SystemServiceRegistry$34.createService(SystemServiceRegistry.java:581)
at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:2051)
at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:1725)
at android.app.ContextImpl.getSystemService(ContextImpl.java:2348)
at android.view.LayoutInflater.from(LayoutInflater.java:332)
at android.view.ContextThemeWrapper.getSystemService(ContextThemeWrapper.java:184)
at android.app.Activity.getSystemService(Activity.java:7769)
at android.view.LayoutInflater.from(LayoutInflater.java:332)
at com.android.internal.policy.PhoneWindow.(PhoneWindow.java:414)
at com.android.internal.policy.PhoneWindow.(PhoneWindow.java:477)
at com.android.internal.policy.HwPhoneWindow.(HwPhoneWindow.java:106)
at com.android.internal.policy.HwPolicyFactoryImpl.getHwPhoneWindow(HwPolicyFactoryImpl.java:42)
at com.android.internal.policy.HwPolicyFactory.getHwPhoneWindow(HwPolicyFactory.java:50)
at android.app.Activity.attach(Activity.java:8902)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:4558)
... 12 more

根据堆栈的初步分析如下:
ShadowDelegate中的_pluginClassLoader是通过inject(pluginClassLoader: PluginClassLoader)接口赋值的,而这个接口是在ShadowPluginLoader中的inject(delegate: ShadowDelegate, partKey: String)接口调用的。而此接口中会判断pluginParts是否为null,为null时将直接抛出异常,导致崩溃,而崩溃信息与上述堆栈不同;如果不为null则会调用delegate.inject(pluginParts.classLoader),如果pluginParts.classLoader为null,则又会抛出空指针异常,崩溃信息也与上述堆栈不同。而ShadowPluginLoader中的inject接口是ShadowActivityDelegate中的onCreate接口中调用的,而除非onCreate中没有调用inject接口才会出现异常,但是这种情况感觉是不会存在的。

上述牵涉到的代码片段如下:
abstract class ShadowDelegate() {
fun inject(pluginClassLoader: PluginClassLoader) {
_pluginClassLoader = pluginClassLoader
}
private lateinit var _pluginClassLoader: PluginClassLoader
protected val mPluginClassLoader: PluginClassLoader
get() = _pluginClassLoader
}

abstract class ShadowPluginLoader(hostAppContext: Context) : DelegateProvider, DI,
ContentProviderDelegateProvider {
override fun inject(delegate: ShadowDelegate, partKey: String) {
mLock.withLock {
val pluginParts = mPluginPartsMap[partKey+"-test"]
if (pluginParts == null) {
throw IllegalStateException("partKey==${partKey}在map中找不到。此时map:${mPluginPartsMap}")
} else {
delegate.inject(pluginParts.appComponentFactory)
delegate.inject(pluginParts.application)
delegate.inject(pluginParts.classLoader)
delegate.inject(pluginParts.resources)
delegate.inject(mComponentManager)
}
}
}
}

open class ShadowActivityDelegate(private val mDI: DI) : GeneratedShadowActivityDelegate(),
HostActivityDelegate {
override fun onCreate(savedInstanceState: Bundle?) {
val pluginInitBundle = savedInstanceState ?: mHostActivityDelegator.intent.extras!!

    mCallingActivity = pluginInitBundle.getParcelable(CM_CALLING_ACTIVITY_KEY)
    mBusinessName = pluginInitBundle.getString(CM_BUSINESS_NAME_KEY, "")
    val partKey = pluginInitBundle.getString(CM_PART_KEY)!!
    mPartKey = partKey
    mDI.inject(this, partKey)
    ...
}

}

@shifujun 请问大神有遇到过类似的问题吗?对于此问题,能给一些分析的思路和建议吗?谢谢!

@shifujun
Copy link
Collaborator

从经验来说,线上收集的奇奇怪怪的难以理解的崩溃始终存在。这些难以理解的堆栈,简单可以分为两类。

一是难以复现的操作路径,使得一些异步操作执行顺序发生变化,从而暴露了编程错误。比如在子线程初始化变量,在主线程使用变量。子线程总是特别快,所以很少有用户遇到问题。赶上了个别用户的特殊操作,子线程慢了就会暴露问题。不仔细看代码,一时意识不到初始化和调用在两个不同线程,就会觉得堆栈难以理解。

二是超出代码错误范畴的意外。比如灰产在调试篡改代码,但是数据上报没关掉。比如用户突然强行中止APP,多个进程退出时机也没有同步。

分析这种问题,还是要先多关注上报的用户分布情况。用户必须很分散,量又大,才能确认确实存在值得关注的问题。

如果复现频率高,迟迟不能复现,就在启动的关键路径上加log上报。看看这些用户的操作和其他用户有什么区别,尝试本地复现。

你贴的这个堆栈,处于插件启动的关键路径上。这是从shadow上线就没改过的逻辑。所以我看到这种上报是不怎么慌的。即便某个机型的某个系统版本可以稳定复现。大概率也是这个系统错误的魔改了什么奇怪的系统调用。shadow又没有用私有api,那公开api行为如果出错了,要么只能这个系统改bug。要么我们能复现就可以想办法兼容它,动态下发一个逻辑。

@zhuqichao
Copy link

这个是定制Rom的问题,堆栈中这行代码:at android.view.LayoutInflater.checkPluginContext(LayoutInflater.java:321)
在Android源码中是不存在的,说明Rom被修改过

@xiboliya
Copy link
Author

从经验来说,线上收集的奇奇怪怪的难以理解的崩溃始终存在。这些难以理解的堆栈,简单可以分为两类。

一是难以复现的操作路径,使得一些异步操作执行顺序发生变化,从而暴露了编程错误。比如在子线程初始化变量,在主线程使用变量。子线程总是特别快,所以很少有用户遇到问题。赶上了个别用户的特殊操作,子线程慢了就会暴露问题。不仔细看代码,一时意识不到初始化和调用在两个不同线程,就会觉得堆栈难以理解。

二是超出代码错误范畴的意外。比如灰产在调试篡改代码,但是数据上报没关掉。比如用户突然强行中止APP,多个进程退出时机也没有同步。

分析这种问题,还是要先多关注上报的用户分布情况。用户必须很分散,量又大,才能确认确实存在值得关注的问题。

如果复现频率高,迟迟不能复现,就在启动的关键路径上加log上报。看看这些用户的操作和其他用户有什么区别,尝试本地复现。

你贴的这个堆栈,处于插件启动的关键路径上。这是从shadow上线就没改过的逻辑。所以我看到这种上报是不怎么慌的。即便某个机型的某个系统版本可以稳定复现。大概率也是这个系统错误的魔改了什么奇怪的系统调用。shadow又没有用私有api,那公开api行为如果出错了,要么只能这个系统改bug。要么我们能复现就可以想办法兼容它,动态下发一个逻辑。

感谢提供思路!

@xiboliya
Copy link
Author

这个是定制Rom的问题,堆栈中这行代码:at android.view.LayoutInflater.checkPluginContext(LayoutInflater.java:321) 在Android源码中是不存在的,说明Rom被修改过

谢谢提醒,我也查过Android14的源码,在LayoutInflater中的确没有找到checkPluginContext方法,当时感觉非常迷惑。你这么一说,我就明白了,看来是系统被修改过了。感谢!

@zhuqichao
Copy link

你知道崩溃的手机型号和系统版本吗,我最近也在看这个问题

@xiboliya
Copy link
Author

你知道崩溃的手机型号和系统版本吗,我最近也在看这个问题

目前我们收集到2个型号有此问题:REP-AN00(ROM版本:8.0.0.138CHNC00E138R5P3)、BVL-N49(ROM版本:8.0.0.155SP51C900E110R1P2)。

@xiboliya
Copy link
Author

你知道崩溃的手机型号和系统版本吗,我最近也在看这个问题

你们那边收集的手机型号是哪些?你们打算怎么处理,有解决思路吗?

@xiboliya
Copy link
Author

从经验来说,线上收集的奇奇怪怪的难以理解的崩溃始终存在。这些难以理解的堆栈,简单可以分为两类。

一是难以复现的操作路径,使得一些异步操作执行顺序发生变化,从而暴露了编程错误。比如在子线程初始化变量,在主线程使用变量。子线程总是特别快,所以很少有用户遇到问题。赶上了个别用户的特殊操作,子线程慢了就会暴露问题。不仔细看代码,一时意识不到初始化和调用在两个不同线程,就会觉得堆栈难以理解。

二是超出代码错误范畴的意外。比如灰产在调试篡改代码,但是数据上报没关掉。比如用户突然强行中止APP,多个进程退出时机也没有同步。

分析这种问题,还是要先多关注上报的用户分布情况。用户必须很分散,量又大,才能确认确实存在值得关注的问题。

如果复现频率高,迟迟不能复现,就在启动的关键路径上加log上报。看看这些用户的操作和其他用户有什么区别,尝试本地复现。

你贴的这个堆栈,处于插件启动的关键路径上。这是从shadow上线就没改过的逻辑。所以我看到这种上报是不怎么慌的。即便某个机型的某个系统版本可以稳定复现。大概率也是这个系统错误的魔改了什么奇怪的系统调用。shadow又没有用私有api,那公开api行为如果出错了,要么只能这个系统改bug。要么我们能复现就可以想办法兼容它,动态下发一个逻辑。

你好,针对这个问题如果要做兼容的话是否可行?可行的话,该怎么修改呢?谢谢!

@zhuqichao
Copy link

ShadowActivityDelegate中修改:
override fun getClassLoader(): ClassLoader {
return if (mDependenciesInjected) {
mPluginClassLoader
} else {
this::class.java.classLoader
}
}

@xiboliya
Copy link
Author

ShadowActivityDelegate中修改: override fun getClassLoader(): ClassLoader { return if (mDependenciesInjected) { mPluginClassLoader } else { this::class.java.classLoader } }

我给荣耀发了工单,荣耀回复说此问题已经修复,等下一个正式系统版本就会上线。具体发布时间还不清楚。

@xiboliya
Copy link
Author

@zhuqichao
nkyn08DIAc
荣耀回复了,在八月初的版本中会修复。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants