We have a really weird crash, which points to the system classes. It appears on application start.
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.android/com.myapp.android.main.BaseMainActivity}: java.lang.RuntimeException: Unable to create application 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) at android.os.Handler.dispatchMessage(Handler.java:110) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) at java.lang.reflect.Method.invokeNative(Method.java) at 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) at dalvik.system.NativeStart.main(NativeStart.java) Caused by java.lang.RuntimeException: Unable to create application 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) at android.os.Handler.dispatchMessage(Handler.java:110) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) at java.lang.reflect.Method.invokeNative(Method.java) at 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) at dalvik.system.NativeStart.main(NativeStart.java) Caused by 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) at com.myapp.android.injection.modules.ApplicationScopeModule.provideAppTrackingInstance(ApplicationScopeModule.java:326) at 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) at 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) at com.myapp.android.main.MyApp.onCreate(MyApp.java:51) at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1007) at 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) at android.os.Handler.dispatchMessage(Handler.java:110) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:5333) at java.lang.reflect.Method.invokeNative(Method.java) at 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) at dalvik.system.NativeStart.main(NativeStart.java)
We use Dagger 1
, our application is multidex
-ed.
Dagger module:
@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 class:
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 class:
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);
}
}
From the implementation and stacktrace we get the crash cause:
Caused by java.lang.NullPointerException at android.content.Context.getString(Context.java:343)
It means that when the user starts the application, Dagger
injects into AppTrackingInstance
"broken" application context. How it can be possible?
We use Dagger
widely, and this context injected in many places without problems. Only in some specific cases (which I can't reproduce) app crashes on start due to broken context.
Crash appears on different devices and OS versions, mostly on 4.x OS, but rarely appears on some 5.0.2 OS versions too:
Since it's a crash on app start, I've investigated it a lot and found quite similar problems (1, 2, app crash on update).
Than I took some test devices - Nexus 4 (Android 5.0.1), Samsung S3 (Android 4.3) - and tried to reproduce the issue:
0 crashes during this tests, but the crash still appears on users devices, and I have no idea why it's happening.
Probably, it happens cause of multidex
or Dagger 1
, but I can't say with confidence.
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{.......
I had such stacktrace once and it's absolutely not as scary, at it sounds. It means, exception has been thrown in onCreate()
of MyApp
.
I.e. context.getResources()
, you provided to the AppTrackingInstance
class is null and it's causing crash.
The reason, why getResources()
return null
(=> crash happens) for me sounds like a race condition, especially, as it happens not every time (from what I understood from the post).
Since I'm also using Dagger1 and MultiDex and I don't have this issue, I can guess, that possible solution would be to start initialize ObjectGraph
lazily.
This snippet works like a charm for me:
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
, which extends 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;
}
}
}
Then in the ActivityBase
's - the base class for every Activity
I'm using in app:
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
}
}
}
It should help you, since during onCreate()
of the first Activity
(extension of ActivityBase
), resources are definitely already defined, so getResources()
shouldn't return null.
Another two options are
I hope, it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With