在Android.content.Context.getString

时间:2016-01-14 09:30:49

标签: android crash dagger 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.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)           at 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)           at 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)           at 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)           at 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)           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)           at 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)           at 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。

Dagger模块:

@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&#34;已损坏&#34;应用背景。怎么可能? 我们广泛使用Dagger,并且在许多地方注入了这种上下文而没有任何问题。仅在某些特定情况下(我无法重现)应用程序因启动环境而崩溃。

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

由于应用程序启动时出现崩溃,我已对其进行了大量调查,发现了非常类似的问题(12app crash on update)。

我采用了一些测试设备 - Nexus 4(Android 5.0.1),三星S3(Android 4.3) - 并试图重现这个问题:

  • 使用/不使用互联网连接打开应用程序
  • 打开/关闭应用程序的50倍
  • 打开应用程序,从游戏市场卸载,安装回到游戏市场并再次打开
  • 从不同的深层链接打开应用程序
  • 从移动网站打开应用程序
  • 从游戏市场安装应用程序并且不打开它。来自deeplinks的冷启动
  • 从推送通知中打开应用程序
  • 使用不同的语言环境打开应用程序
  • 从最近开放申请
  • 清除应用数据并打开
  • 安装旧的生产版本,手动更新到最新的生产版本
  • 安装旧的生产版本,更新到来自游戏市场的最新版本
  • 浏览应用程序XXX分钟,然后从Play市场更新到最新版本
在此测试期间,

0崩溃,但崩溃仍然出现在用户设备上,我不知道它为什么会发生。

可能是由multidexDagger 1引起的,但我无法自信地说。

2 个答案:

答案 0 :(得分:2)

  

致命异常:java.lang.RuntimeException:无法启动活动   ComponentInfo {.......

我有一次这样的堆栈跟踪,听起来绝对不是那么可怕。 这意味着,onCreate() MyApp引发了异常。

即。 context.getResources(),您提供给AppTrackingInstance类的内容为null,导致崩溃。

为什么getResources()为我返回null(=&gt;崩溃)的原因听起来像竞争条件,尤其是因为它不是每次都发生(来自我从帖子中了解到的)。

由于我也在使用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中 - 我在app中使用的每个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),立即进行投票。

我希望,这有帮助。

答案 1 :(得分:0)

似乎没有初始化Context对象。此调用出错:

@Provides
@Singleton
AppTrackingInstance provideAppTrackingInstance(@ForApplication Context context) {
    return new AppTrackingInstance(context);
}

验证此方法中的上下文是否为null。我相信问题就在那里。