首先,它与Why I get this error when on google map "Failed to load DynamiteLoader: java.lang.ClassNotFoundException: Didn't find class?中描述的错误项目配置无关。
有一天,我在 Fabric 的崩溃中遇到了高峰(1 天内崩溃了约 1300 次)
com.google.android.gms.dynamite.DynamiteModule$zza: Remote load failed. No local fallback,
com.google.android.gms.dynamite.DynamiteModule$zzc: No acceptable module found. Local version is 0 and remote version is 0 例外。
起初我并不清楚为什么会发生这种情况,因为在那段时间我没有发布任何应用程序更新。经过深入调查(总共几天),我发现问题是由 Google Play 服务版本与过时的 Google Cast SDK 之间的兼容性问题造成的。
我的 Android 应用程序使用 10.2.4 版本的 Google Cast SDK,此版本与某些 Google Play 服务版本不兼容。
我发现11.3.02 (480-161239932) 版本的 Google Play Service 与 10.2.4 Google Cast SDK 不兼容。 (当然)还有其他版本的 Google Play 服务与 Google Cast SDK 库不兼容,但我只找到了一个版本,它足以进行测试和进一步修复。
在设备上安装了11.3.02 (480-161239932) 版本的 Google Play 服务后,我的应用程序在启动时立即崩溃。值得注意的是,我发现我的设备上的其他应用程序在启动时立即崩溃并且堆栈跟踪是相同的。
修复崩溃
修复对我来说很简单 - 我必须将 Google Play 服务从 10.2.4 更新到 11.4.2,
特别是play-services-cast-framework、play-services-ads、firebase-core、firebase-messaging。
Fabric 中的尖峰历史:
- 9 月 25 日(约 400 次崩溃),第一次高峰
- 9 月 27 日(约 200 次崩溃)
- 9 月 29 日(~140 次崩溃)
- 10 月 8 日(~1300 次崩溃)
10 月 8 日之后的崩溃并没有那么严重(每天大约 10-20 次崩溃),并且没有观察到更多的峰值。这意味着用户更新了 Google Play 服务并且应用程序在启动时不再崩溃。
一旦我更新了 Google Play 服务版本并且用户更新到新版本的应用程序 - 崩溃消失了(每天大约 1-2 次崩溃)。
为什么会发生这种崩溃的解释
您可能知道,Google Play 服务会在后台自动更新。这意味着有一天用户可以收到与应用程序中使用的 Google Cast SDK 不兼容的 Google Play 服务更新,并且应用程序将崩溃 (100%)。
根据我的经验,Google Cast SDK 会发生这种情况,但其他库可能会出现这种情况(没有证据)。
当您从 Google Cast SDK 调用任何方法时,应用程序会崩溃。我评论了我的应用程序中依赖 Google Cast SDK 的不同地方,并且应用程序总是崩溃。
详细指南
如果有人好奇如何模拟这种崩溃,这里有详细指南。
- 首先,我们需要卸载现有版本的 Google Play 服务。这可以从“Google Play 服务”应用程序信息屏幕中实现
-
在 Android 设备 (apkmirror link) 上安装 11.3.02 (480-161239932) 版本的 Google Play 服务。
adb install -r com.google.android.gms_11.3.02_(480-161239932)-11302480_minAPI23(x86,x86_64)(nodpi)_apkmirror.com.apk
- 访问 Google Cast SDK 时打开应用程序和观察者应用程序崩溃。
第二次崩溃
我以为我已经通过更新 Google Play 服务解决了崩溃问题,但在沉默几周后,我收到了通知,再次出现具有相同堆栈跟踪的崩溃。
经过一番调查(又是几天),我能够重现该问题。这是一个令人遗憾的消息——我对这次崩溃无能为力:sad_face: 或者至少我还没有找到优雅的解决方案。
好消息是,重新出现的崩溃现象非常罕见(每月约 1-2 次崩溃)。
Google Play 服务自动更新或强制卸载(不太可能)强制停止打开的应用程序以使 Play 服务更新生效。这是一个不可控的过程,这意味着 Android OS 可以随时静默安装 Google Play Services。 如果用户在某个时候打开了应用程序并且 Google Play 服务安装完成,则应用程序将被系统杀死,用户需要重新打开它。当应用程序被系统杀死时,我们可以看到在日志中,崩溃的堆栈跟踪与我们在第一种情况下的堆栈跟踪完全相同。
这很难重现,而且只发生在我身上几次。
但这种情况更有可能是因为 Google Play 服务经常更新。
虽然有一种方法可以重现此崩溃(在现实生活中不太可能) - “从设备中删除 Google Play 服务更新”。它产生相同的堆栈跟踪。
所以基本上,我们需要开始删除 Google Play 服务更新并快速(我的意思是非常快)打开我们的应用程序。如果我们成功了,应用程序将在启动时被系统杀死,因为 Google Play 服务被删除并且系统杀死了应用程序。
这是堆栈跟踪:
FATAL EXCEPTION: main
Process: XXX, PID: 26149
java.lang.RuntimeException: Unable to start activity ComponentInfo{XXX/XXX.SomeActivity}: android.view.InflateException: Binary XML file line #0: Binary XML file line #0: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2733)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2819)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1532)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6321)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: android.view.InflateException: Binary XML file line #0: Binary XML file line #0: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #0: Error inflating class fragment
Caused by: java.lang.RuntimeException: com.google.android.gms.dynamite.DynamiteModule$zzc: No acceptable module found. Local version is 0 and remote version is 0.
at com.google.android.gms.internal.zzavl.zzbp(Unknown Source)
at com.google.android.gms.internal.zzavl.zza(Unknown Source)
at com.google.android.gms.cast.framework.CastContext.<init>(Unknown Source)
at com.google.android.gms.cast.framework.CastContext.getSharedInstance(Unknown Source)
at com.google.android.gms.cast.framework.media.uicontroller.UIMediaController.<init>(Unknown Source)
at com.google.android.gms.cast.framework.media.widget.MiniControllerFragment.onCreateView(Unknown Source)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:2343)
at android.support.v4.app.FragmentManagerImpl.ensureInflatedFragmentView(FragmentManager.java:1645)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1390)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1640)
at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1896)
at android.support.v4.app.FragmentManagerImpl.onCreateView(FragmentManager.java:3673)
at android.support.v4.app.FragmentController.onCreateView(FragmentController.java:111)
at android.support.v4.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:331)
at android.support.v4.app.BaseFragmentActivityApi14.onCreateView(BaseFragmentActivityApi14.java:39)
at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:65)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:777)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:727)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:858)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:821)
at android.view.LayoutInflater.inflate(LayoutInflater.java:518)
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.view.LayoutInflater.inflate(LayoutInflater.java:377)
at android.support.v7.app.AppCompatDelegateImplV9.setContentView(AppCompatDelegateImplV9.java:287)
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:139)
at XXX.SomeActivity.setContentView(SomeActivity:XXX)
at XXX.SomeActivity.onCreate(SomeActivity:XXX)
at android.app.Activity.performCreate(Activity.java:6760)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1134)
更新
有一个Google Play Services 12.0.0 release,应该可以解决问题:
修复了在具有过期版本的 Google Play 服务的设备上调用 CastContext.getSharedInstance() 导致崩溃的错误。过时的 Google Play 服务版本仍会导致方法失败,因此客户端应检查适当的版本。
但事实上,即使在更新到 12.0.0 版本后,我也看到了很多崩溃。当设备上发生 Google Play 服务更新时会发生崩溃。
为了解决这个问题,我们需要通过检查调用Cast API时是否没有抛出异常来检查Cast API是否可用。
fun isCastApiAvailable(): Boolean {
val isCastApiAvailable = isNotTv(context)
&& GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS
try {
CastContext.getSharedInstance(context)
} catch (e: Exception) {
// track non-fatal
return false
}
return isCastApiAvailable
}
这样我们可以防止崩溃。如果在应用程序处于前台时发生 Play Services 更新,应用程序可能会被系统杀死并重新启动。或者,Cast API 相关功能将不可用(Chromecast 图标不可见)。
因此,我们以这种方式避免了崩溃并报告为非致命事件,只是为了了解这种情况发生的频率。