Ошибка приложения при запуске, вызванная NPE в android.content.Context.getString
У нас действительно странный крах, который указывает на системные классы. Он появляется при запуске приложения.
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:
![3 screenshot]()
Поскольку это крах при запуске приложения, я много исследовал его и нашел довольно похожие проблемы (1, 2, сбой приложения при обновлении).
Чем я взял некоторые тестовые устройства - Nexus 4 (Android 5.0.1), Samsung S3 (Android 4.3) - и попытался воспроизвести проблему:
- открыть приложение с/без подключения к Интернету
- открыть/закрыть в 50 раз больше, чем приложение
- открыть приложение, удалить с игрового рынка, установить обратно игровой рынок и снова открыть.
- открыть приложение из разных деадаптеров
- открыть приложение с мобильного сайта
- установить приложение с игрового рынка и не открывать его. Холодный старт от депиптиков
- открыть приложение из push-уведомлений
- открыть приложение с разными локалями
- открыть приложение из recents
- удалить данные приложения и открыть
- установить старую сборку, обновить до последней сборки сборки вручную
- установить старую сборку, обновить до последней из игрового рынка.
- перемещаться по приложению через XXX минут, а затем обновлять до последней версии с игрового рынка.
0 сбой во время этих тестов, но авария все еще появляется на пользовательских устройствах, и я понятия не имею, почему это происходит.
Возможно, это происходит из-за multidex
или Dagger 1
, но я не могу сказать с уверенностью.
Ответы
Ответ 1
Fatal Exception: java.lang.RuntimeException: невозможно запустить активность ComponentInfo {.......
У меня была такая stacktrace один раз, и это абсолютно не так страшно, при этом звучит. Это означает, что исключение было выбрано в onCreate()
из MyApp
.
т.е. context.getResources()
, вы предоставили классу AppTrackingInstance
значение null и это приведет к сбою.
Причина, почему getResources()
return null
(= > crash happens) для меня звучит как состояние гонки, особенно, поскольку это происходит не каждый раз (из того, что я понял из после).
Так как я также использую Dagger1 и MultiDex, и у меня нет этой проблемы, я могу догадаться, что возможным решением было бы начать инициализировать ObjectGraph
лениво.
Этот фрагмент работает как прелесть для меня:
public final class ApplicationScopeModule {
private final Context applicationContext;
public ApplicationScopeModule(final Context applicationContext) {
this.applicationContext = applicationContext;
}
@Provides
@Singleton
@SuppressWarnings("unused") // invoked by Dagger
public Context provideApplicationContext() {
return applicationContext;
}
@Provides
@Singleton
@SuppressWarnings("unused") // invoked by Dagger
public Analytics provideAnalytics(Context context) {
return new DefaultAnalytics(context);
}
//...<other providers>..
}
MyApplication
, который расширяет Application
:
public class MyApplication extends Application {
private ObjectGraph objectGraph;
private final Object lock = new Object();
@Override
public void onCreate() {
super.onCreate();
}
protected List<Object> getModules() {
final ArrayList<Object> modules = new ArrayList<>();
modules.add(new ApplicationScopeModule(getApplicationContext()));
return modules;
}
public ObjectGraph getApplicationGraph() {
synchronized (lock) {
if (objectGraph == null) {
objectGraph = ObjectGraph.create(getModules().toArray());
}
return objectGraph;
}
}
}
Затем в ActivityBase
- базовом классе для каждого Activity
, который я использую в приложении:
public abstract class FragmentActivityBase extends ActionBarActivity {
private ObjectGraph activityGraph;
@Override
protected void onCreate(final Bundle savedInstanceState) {
inject(this);
super.onCreate(savedInstanceState);
}
public void inject(final Object object) {
try {
if (activityGraph == null) {
final MyApplication application = (MyApplication) getApplication();
activityGraph = application.getApplicationGraph();
}
activityGraph.inject(object);
} catch (IllegalArgumentException e) {
//log error
}
}
}
Это должно помочь вам, поскольку во время onCreate()
первого Activity
(расширение ActivityBase
) ресурсы определенно уже определены, поэтому getResources()
не должен возвращать null.
Еще две опции:
- Избегайте Multidex
- Опрос на данный момент, когда ваш контекст заполнен (хотя, как только getResources() терпит неудачу - кто знает, что еще может быть неправильно, и я боюсь, что это приведет к другим сбоям - imho)
Надеюсь, это поможет.
Ответ 2
Кажется, что объект Context не инициализируется. Ошибка в этом вызове:
@Provides
@Singleton
AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) {
return new AppTrackingInstance(context);
}
Проверьте, является ли контекст нулевым в этом методе. Я считаю, что проблема там.