android.content.Context.getString中的NPE导致应用程序启动时崩溃

android.content.Context.getString中的NPE导致应用程序启动时崩溃,android,crash,dagger,android-multidex,Android,Crash,Dagger,Android Multidex,我们有一个非常奇怪的崩溃,它指向系统类。它出现在应用程序启动时 致命异常:java.lang.RuntimeException:无法启动活动 ComponentInfo{com.myapp.android/com.myapp.android.main.BaseMainActivity}: java.lang.RuntimeException:无法创建应用程序 com.myapp.android.main.myapp:java.lang.NullPointerException 在android.

我们有一个非常奇怪的崩溃,它指向系统类。它出现在应用程序启动时

致命异常:java.lang.RuntimeException:无法启动活动 ComponentInfo{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.Invokenactive(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.Invokenactive(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.singleton.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$ProviderAppTrackingInstanceProvideAdapter.get(ApplicationScopeModule$$ModuleAdapter.java:1618) 在com.myapp.android.injection.modules.ApplicationScopeModule$$ModuleAdapter$ProvideAppTrackingInstanceProvideAdapter.get(ApplicationScopeModule$$ModuleAdapter.java:1552)上 at 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) 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.Invokenactive(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)

我们使用
dagger1
,我们的应用程序是
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);
  }
}
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;
  }
}
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);
  }
}
MyApp类:

@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);
  }
}
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;
  }
}
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引起 位于android.content.Context.getString(Context.java:343)

这意味着当用户启动应用程序时,
Dagger
注入到
apptrackingstance
中断的应用程序上下文中。怎么可能呢? 我们广泛使用
Dagger
,在许多地方注入这种上下文都没有问题。只有在某些特定情况下(我无法重现),应用程序在启动时由于上下文中断而崩溃

崩溃出现在不同的设备和操作系统版本上,主要出现在4.x操作系统上,但很少出现在某些5.0.2操作系统版本上:

由于它是应用程序启动时的崩溃,我对它进行了很多调查,发现了非常类似的问题(,)

然后我拿了一些测试设备——Nexus4(安卓5.0.1)、三星S3(安卓4.3)——试着重现这个问题:

  • 打开有/无internet连接的应用程序
  • 打开/关闭应用程序的50倍
  • 打开应用程序,从play market卸载,从play market重新安装
    @Provides
    @Singleton
    AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) {
        return new AppTrackingInstance(context);
    }