【发布时间】:2016-04-19 13:28:54
【问题描述】:
我们遇到了一个非常奇怪的崩溃,它指向系统类。它出现在应用程序启动时。
致命异常:java.lang.RuntimeException:无法启动活动 组件信息{com.myapp.android/com.myapp.android.main.BaseMainActivity}: java.lang.RuntimeException:无法创建应用程序 com.myapp.android.main.MyApp:java.lang.NullPointerException 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2377) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429) 在 android.app.ActivityThread.access$800(ActivityThread.java:151) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342) 在 android.os.Handler.dispatchMessage(Handler.java:110) 在 android.os.Looper.loop(Looper.java:193) 在 android.app.ActivityThread.main(ActivityThread.java:5333) 在 java.lang.reflect.Method.invokeNative(Method.java) 在 java.lang.reflect.Method.invoke(Method.java:515) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644) 在 dalvik.system.NativeStart.main(NativeStart.java) 由 java.lang.RuntimeException 引起:无法创建应用程序 com.myapp.android.main.MyApp:java.lang.NullPointerException 在 android.app.LoadedApk.makeApplication(LoadedApk.java:529) 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2292) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429) 在 android.app.ActivityThread.access$800(ActivityThread.java:151) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342) 在 android.os.Handler.dispatchMessage(Handler.java:110) 在 android.os.Looper.loop(Looper.java:193) 在 android.app.ActivityThread.main(ActivityThread.java:5333) 在 java.lang.reflect.Method.invokeNative(Method.java) 在 java.lang.reflect.Method.invoke(Method.java:515) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644) 在 dalvik.system.NativeStart.main(NativeStart.java) 由 java.lang.NullPointerException 引起 在 android.content.Context.getString(Context.java:343) 在 com.myapp.android.api.singletons.AppTrackingInstance.initAdjust(AppTrackingInstance.java:114) 在 com.myapp.android.api.singletons.AppTrackingInstance.(AppTrackingInstance.java:92) 在 com.myapp.android.injection.modules.ApplicationScopeModule.provideAppTrackingInstance(ApplicationScopeModule.java:326) 在 com.myapp.android.injection.modules.ApplicationScopeModule$$ModuleAdapter$ProvideAppTrackingInstanceProvidesAdapter.get(ApplicationScopeModule$$ModuleAdapter.java:1618) 在 com.myapp.android.injection.modules.ApplicationScopeModule$$ModuleAdapter$ProvideAppTrackingInstanceProvidesAdapter.get(ApplicationScopeModule$$ModuleAdapter.java:1552) 在 dagger.internal.Linker$SingletonBinding.get(Linker.java:364) 在 com.myapp.android.main.MyApp$$InjectAdapter.injectMembers(MyApp$$InjectAdapter.java:70) 在 com.myapp.android.main.MyApp$$InjectAdapter.injectMembers(MyApp$$InjectAdapter.java:23) 在 dagger.ObjectGraph$DaggerObjectGraph.inject(ObjectGraph.java:281) 在 com.myapp.android.main.MyApp$1.run(MyApp.java:57) 在 com.myapp.android.main.MyApp.onCreate(MyApp.java:51) 在 android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1007) 在 android.app.LoadedApk.makeApplication(LoadedApk.java:526) 在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2292) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429) 在 android.app.ActivityThread.access$800(ActivityThread.java:151) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1342) 在 android.os.Handler.dispatchMessage(Handler.java:110) 在 android.os.Looper.loop(Looper.java:193) 在 android.app.ActivityThread.main(ActivityThread.java:5333) 在 java.lang.reflect.Method.invokeNative(Method.java) 在 java.lang.reflect.Method.invoke(Method.java:515) 在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:828) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644) 在 dalvik.system.NativeStart.main(NativeStart.java)
我们使用Dagger 1,我们的应用是multidex-ed。
匕首模块:
@Module(
library = true,
injects = {
MyApp.class
}
)
public class ApplicationScopeModule {
private final MyApp application;
public ApplicationScopeModule(MyApp application) {
this.application = application;
}
@Provides
@Singleton
@ForApplication
Context provideApplicationContext() {
return application.getApplicationContext();
}
@Provides
@Singleton
AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) {
return new AppTrackingInstance(context);
}
}
MyApp 类:
package com.myapp.android.main;
public class MyApp extends MultiDexApplication {
private ObjectGraph objectGraph;
@Inject
AppTrackingInstance appTrackingInstance;
@Override
public void onCreate() {
super.onCreate();
// workaround for multi-dex enabled projects
// taken from http://frogermcs.github.io/MultiDex-solution-for-64k-limit-in-Dalvik/
// multi-dex separates dex files, and some classes going to additional dex file.
// Additional .dex files are loaded in Application.attachBaseContext(Context) method
// (by MultiDex.install(Context) invokation). It means, that before this moment
// we can’t use classes from them. So i.e. we cannot declare static fields
// with types attached out of main .dex file.
// Otherwise we’ll get java.lang.NoClassDefFoundError.
//
// the issue should be fixed on the Android level
//
new Runnable() {
@Override
public void run() {
initFabric();
objectGraph = ObjectGraph.create(getModules().toArray());
objectGraph.inject(MyApp.this);
appTrackingInstance.trackAppLaunch();
}
}.run();
}
private void initFabric() {
Fabric.with(MyApp.this, new Crashlytics.Builder().core(new CrashlyticsCore.Builder().disabled(BuildConfig.IS_DEBUG_BUILD).build()).build());
}
public List<Object> getModules() {
return Arrays.<Object>asList(new ApplicationScopeModule(this));
}
public ObjectGraph getObjectGraph() {
return objectGraph;
}
}
AppTrackingInstance 类:
package com.myapp.android.api.singletons;
public class AppTrackingInstance {
Context context;
public AppTrackingInstance(Context context) {
this.context = context;
initAdjust();
}
private void initAdjust() {
// "broken" context here
String variable = context.getString(R.string.adjust_variable);
}
}
从实现和堆栈跟踪我们得到崩溃原因:
由 java.lang.NullPointerException 引起 在 android.content.Context.getString(Context.java:343)
这意味着当用户启动应用程序时,Dagger 注入到AppTrackingInstance“破碎”的应用程序上下文中。怎么可能?
我们广泛使用Dagger,并且这个上下文在很多地方注入都没有问题。仅在某些特定情况下(我无法重现)应用程序在启动时由于上下文中断而崩溃。
崩溃出现在不同的设备和操作系统版本上,主要出现在 4.x 操作系统上,但也很少出现在某些 5.0.2 操作系统版本上:
由于是应用启动时崩溃,我进行了很多调查,发现了非常相似的问题(1、2、app crash on update)。
我使用了一些测试设备 - Nexus 4 (Android 5.0.1)、Samsung S3 (Android 4.3) - 并试图重现该问题:
- 在有/没有互联网连接的情况下打开应用程序
- 打开/关闭应用程序的 50 倍
- 打开应用程序,从 Play Market 卸载,从 Play Market 安装回来并再次打开
- 从不同的深层链接打开应用程序
- 从移动网站打开应用程序
- 从游戏市场安装应用程序,不要打开它。从深层链接冷启动
- 从推送通知打开应用程序
- 打开具有不同语言环境的应用程序
- 从最近打开应用程序
- 清除应用数据并打开
- 安装旧的生产版本,手动更新到最新的生产版本
- 安装旧的生产版本,从 Play Market 更新到最新版本
- 浏览应用程序 XXX 分钟,然后从 Play Market 更新到最新版本
在此测试期间发生了 0 次崩溃,但崩溃仍然出现在用户设备上,我不知道为什么会发生这种情况。
可能是multidex 或Dagger 1 的原因,但我不能自信地说。
【问题讨论】:
-
不是一个真正的解决方案(为此发表评论),而是一项改进,可以通过阻止 getString() 调用来解决您的问题:配置设置不应放入 strings.xml!如果您有一个 Adjust-Tracking id,您应该将其作为 buildConfigField 添加到您的风格中,并通过 BuildConfig.ADJUST_TRACKING_ID 或类似命名来引用它们。
-
谢谢,我们在这种情况下使用
BuildConfig变量和风格。 -
您确定
R.string.adjust_variable的翻译正确吗?另外,尝试重现崩溃的你应该尝试使用其他语言 -
好吧,这个字符串没有翻译,因为它在每种语言中都应该是相同的。我试过不同的语言,但不是这样。
标签: android crash dagger android-multidex