У нас действительно странный крах, который указывает на системные классы. Он появляется при запуске приложения.
Fatal Exception: java.lang.RuntimeException: невозможно запустить активность ComponentInfo {com.myapp.android/com.myapp.android.main.BaseMainActivity}: java.lang.RuntimeException: невозможно создать приложение com.myapp.android.main.MyApp: java.lang.NullPointerException at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2377) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429) at android.app.ActivityThread.access $800 (ActivityThread.java:151) at android.app.ActivityThread $H.handleMessage(ActivityThread.java:1342) на android.os.Handler.dispatchMessage(Handler.java:110) на android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) в java.lang.reflect.Method.invokeNative(Method.java) в java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit $MethodAndArgsCaller.run(ZygoteInit.java:828) at 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 at android.app.LoadedApk.makeApplication(LoadedApk.java:529) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2292) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429) at android.app.ActivityThread.access $800 (ActivityThread.java:151) at android.app.ActivityThread $H.handleMessage(ActivityThread.java:1342) на android.os.Handler.dispatchMessage(Handler.java:110) на android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) в java.lang.reflect.Method.invokeNative(Method.java) в java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit $MethodAndArgsCaller.run(ZygoteInit.java:828) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:644) в dalvik.system.NativeStart.main(NativeStart.java) Вызывается java.lang.NullPointerException at android.content.Context.getString(Context.java:343) at com.myapp.android.api.singletons.AppTrackingInstance.initAdjust(AppTrackingInstance.java:114) at 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) at com.myapp.android.injection.modules.ApplicationScopeModule $$ ModuleAdapter $ProvideAppTrackingInstanceProvidesAdapter.get(ApplicationScopeModule $$ ModuleAdapter.java: 1552) на dagger.internal.Linker $SingletonBinding.get(Linker.java:364) at com.myapp.android.main.MyApp $$ InjectAdapter.injectMembers(MyApp $$ InjectAdapter.java: 70) at com.myapp.android.main.MyApp $$ InjectAdapter.injectMembers(MyApp $$ InjectAdapter.java: 23) at dagger.ObjectGraph $DaggerObjectGraph.inject(ObjectGraph.java:281) at com.myapp.android.main.MyApp $1.run(MyApp.java:57) на com.myapp.android.main.MyApp.onCreate(MyApp.java:51) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1007) в android.app.LoadedApk.makeApplication(LoadedApk.java:526) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2292) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2429) at android.app.ActivityThread.access $800 (ActivityThread.java:151) at android.app.ActivityThread $H.handleMessage(ActivityThread.java:1342) на android.os.Handler.dispatchMessage(Handler.java:110) на android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) в java.lang.reflect.Method.invokeNative(Method.java) в java.lang.reflect.Method.invoke(Method.java:515) at com.android.internal.os.ZygoteInit $MethodAndArgsCaller.run(ZygoteInit.java:828) в com.android.internal.os.ZygoteInit.Основной (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);
}
}
Из реализации и stacktrace мы получаем причину сбоя:
Вызывается java.lang.NullPointerException at android.content.Context.getString(Context.java:343)
Это означает, что когда пользователь запускает приложение, Dagger
вводит в AppTrackingInstance
"сломанный" контекст приложения. Как это возможно?
Мы широко используем Dagger
, и этот контекст вводится во многих местах без проблем. Только в некоторых конкретных случаях (которые я не могу воспроизвести) приложение запускается при запуске из-за разбитого контекста.
Сбой появляется на разных устройствах и версиях ОС, в основном на 4.x ОС, но редко появляется на некоторых версиях ОС 5.0.2:
Поскольку это крах при запуске приложения, я много исследовал его и нашел довольно похожие проблемы (1, 2, сбой приложения при обновлении).
Чем я взял некоторые тестовые устройства - Nexus 4 (Android 5.0.1), Samsung S3 (Android 4.3) - и попытался воспроизвести проблему:
- открыть приложение с/без подключения к Интернету
- открыть/закрыть в 50 раз больше, чем приложение
- открыть приложение, удалить с игрового рынка, установить обратно игровой рынок и снова открыть.
- открыть приложение из разных деадаптеров
- открыть приложение с мобильного сайта
- установить приложение с игрового рынка и не открывать его. Холодный старт от депиптиков
- открыть приложение из push-уведомлений
- открыть приложение с разными локалями
- открыть приложение из recents
- удалить данные приложения и открыть
- установить старую сборку, обновить до последней сборки сборки вручную
- установить старую сборку, обновить до последней из игрового рынка.
- перемещаться по приложению через XXX минут, а затем обновлять до последней версии с игрового рынка.
0 сбой во время этих тестов, но авария все еще появляется на пользовательских устройствах, и я понятия не имею, почему это происходит.
Возможно, это происходит из-за multidex
или Dagger 1
, но я не могу сказать с уверенностью.