代码之家  ›  专栏  ›  技术社区  ›  Denis Knauer

Android 7.0和三星设备上Dagger 2的运行时异常

  •  31
  • Denis Knauer  · 技术社区  · 7 年前

    自从我开始使用Dagger 2以来,在我的Google Play控制台上我看到了很多崩溃报告,但只在Android 7.0上,主要是在三星设备、一些华威和摩托罗拉设备以及一些罕见的Xperia设备上:

    java.lang.RuntimeException: 
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2984)
      at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3045)
      at android.app.ActivityThread.-wrap14 (ActivityThread.java)
      at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1642)
      at android.os.Handler.dispatchMessage (Handler.java:102)
      at android.os.Looper.loop (Looper.java:154)
      at android.app.ActivityThread.main (ActivityThread.java:6776)
      at java.lang.reflect.Method.invoke (Method.java)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:1518)
      at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1408)
    Caused by: java.lang.RuntimeException: 
      at dagger.android.AndroidInjection.inject (AndroidInjection.java:48)
      at dagger.android.support.DaggerAppCompatActivity.onCreate (DaggerAppCompatActivity.java:43)
      at com.package.MainActivity.onCreate (MainActivity.java:83)
      at android.app.Activity.performCreate (Activity.java:6956)
      at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1126)
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2927)
    

    我通过研究了解到,活动的onCreate很可能是在活动实际附加到应用程序之前调用的。但我无法证明这一说法。。。

    我的应用程序类:

    public class App extends DaggerApplication {
    
        @Override
        public void onCreate() {
            super.onCreate();
        }
    
        @Override
        protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
            AppComponent appComponent = DaggerAppComponent.builder().application(this).build();
            appComponent.inject(this);
            return appComponent;
        }
    
    }
    

    我的主要活动类:

    public class MainActivity extends DaggerAppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
    }
    

    相关匕首2代码:

    DaggerAppCompatActivity: https://github.com/google/dagger/blob/e8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8/java/dagger/android/support/DaggerAppCompatActivity.java#L42-L45

    protected void onCreate(@Nullable Bundle savedInstanceState) { 
        AndroidInjection.inject(this); 
        super.onCreate(savedInstanceState); 
    }
    

    雄激素注射: https://github.com/google/dagger/blob/e8d7cd4c29c1316c5bb1cf0737d4f29111fcb1c8/java/dagger/android/AndroidInjection.java#L43-L52

    public static void inject(Activity activity) { 
        checkNotNull(activity, "activity"); 
        Application application = activity.getApplication(); 
        if (!(application instanceof HasActivityInjector)) { 
            throw new RuntimeException( 
                String.format( 
                    "%s does not implement %s", 
                    application.getClass().getCanonicalName(), 
                    HasActivityInjector.class.getCanonicalName())); 
        }
    

    我不知道如何解决这次崩溃,但崩溃的数量太大了,不容忽视。由于我的Dagger 2在所有其他Android版本和设备上都能完美运行,我认为这不是由我使用Dagger 2的方式引起的,而是由一些特定于供应商的7.0实现引起的。如果有人面临同样的问题并找到了解决方案,请,请,请帮助我!

    由于这个错误让我抓狂,我向10万用户推出了一个测试版本,试图了解整个问题的症结所在。

    public abstract class TestDaggerAppCompatActivity extends AppCompatActivity implements HasFragmentInjector, HasSupportFragmentInjector {
    
        @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
        @Inject DispatchingAndroidInjector<android.app.Fragment> frameworkFragmentInjector;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            inject();
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public AndroidInjector<Fragment> supportFragmentInjector() {
            return supportFragmentInjector;
        }
    
        @Override
        public AndroidInjector<android.app.Fragment> fragmentInjector() {
            return frameworkFragmentInjector;
        }
    
        private void inject() {
            Application application = getApplication();
    
            if(application == null) {
                injectWithNullApplication();
                return;
            }
    
            if (!(application instanceof HasActivityInjector)) {
                injectWithWrongApplication();
                return;
            }
    
            // Everything seems ok...
            injectNow(application);
        }
    
        private void injectWithNullApplication() {
            Application application = (Application) getApplicationContext();
            injectNow(application);
        }
    
        private void injectWithWrongApplication() {
            Application application = (Application) getApplicationContext();
            injectNow(application);
        }
    
        private void injectNow(Application application) {
            checkNotNull(application, "Application must not be null");
    
            if (!(application instanceof HasActivityInjector)) {
                throw new RuntimeException(String.format("%s does not implement %s", application.getClass().getCanonicalName(), HasActivityInjector.class.getCanonicalName()));
            }
    
            AndroidInjector<Activity> activityInjector = ((HasActivityInjector) application).activityInjector();
            checkNotNull(activityInjector, "%s.activityInjector() returned null", application.getClass().getCanonicalName());
    
            activityInjector.inject(this);
        }
    
    }
    

    该活动基于Dagger的活动和内联的AndroidInjection代码。我的想法是,如果这个问题不能通过使用ApplicationContext而不是 getApplication() 我的堆栈跟踪应该详细说明发生了什么:

    • 如果问题是由 堆栈跟踪将包含 injectWithNullApplication() injectWithWrongApplication()
    • 抛出NPE将表明 getApplicationContext() 返回null
    • 抛出的RuntimeException将显示 getApplicationContext() 不是我的申请
    • 如果不会引发异常,则 getApplication() getApplicationContext() 返回我的申请,我不在乎是什么解决了这个问题

    这是堆栈跟踪:

    Caused by: java.lang.RuntimeException: 
      at com.package.di.TestDaggerAppCompatActivity.inject (TestDaggerAppCompatActivity.java:49)
      at com.package.di.TestDaggerAppCompatActivity.onCreate (TestDaggerAppCompatActivity.java:31)
      at com.package.MainActivity.onCreate (MainActivity.java:83)
      at android.app.Activity.performCreate (Activity.java:6942)
      at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1126)
      at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2880)
    

    所以if子句 !(application instanceof HasActivityInjector) 在里面 inject() 未重新路由到 InjectWithErrorApplication() 但是相同的if子句导致了中的RuntimeException injectNow(Application application) 在同一应用程序实例上。世界跆拳道联盟?我看起来像在我的代码100倍,但如果我有一个错误在那里,请让我知道!否则,我想在7.0的一些供应商实现中会发生一些非常奇怪的事情,这些事情可能无法修复。。。

    基于以下讨论: https://github.com/google/dagger/issues/748 我还推出了一个只使用 getApplicationContext() 而不是 getApplication() 在所有匕首组件中没有任何区别。

    <application
        android:name=".App"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/SplashScreenTheme"
        android:fullBackupContent="false">
    
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
        <meta-data android:name="com.google.android.gms.games.APP_ID" android:value="@string/app_id" />
    
        <meta-data android:name="android.max_aspect" android:value="2.1" />
    
        <activity
            android:name="com.package.MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
        <service android:name="com.package.GeneratorService" android:exported="false"/>
    </application>
    
    2 回复  |  直到 6 年前
        1
  •  14
  •   Denis Knauer    7 年前

    最后,我找到了一种解决在Android 7.0下使用Dagger 2导致应用程序崩溃的方法。请注意 这并不能解决Android 7.0下未正确使用自定义应用程序的问题 . 在我的例子中,除了实现Dagger 2之外,我的自定义应用程序中没有重要的逻辑,所以我只是替换了 DaggerApplication 基于的实现 ApplicationlessInjection 在下面

    已知问题

    • 在自定义应用程序类中没有依赖注入(不管怎样,这对于令人抓狂的安卓7.0 OEM实现来说可能不是一个好主意)
    • 并非所有匕首组件都由我修改,我只更换了 DaggerAppCompatActivity , DaggerIntentService DaggerFragment . 如果您使用其他组件,如 DaggerDialogFragment DaggerBroadcastReceiver 您需要创建自己的工具,但我想这应该不会太难:)

    实施

    停止使用 匕首应用 Application 或者完全摆脱自定义应用程序。对于Dagger 2的依赖注入,不再需要了。只需扩展,例如。 FixedDaggerAppCompatActivity 而你很好的去用匕首2迪进行活动。

    您可能注意到,我仍在将应用程序上下文传递给 ApplicationlessInjection.getInstance() . 依赖注入本身根本不需要上下文,但我希望能够轻松地将应用程序上下文注入到我的其他组件和模块中。在那里,我不在乎应用程序上下文是我的自定义应用程序还是安卓7.0中的一些疯狂的其他东西,只要它是一个上下文。

    应用程序注射

    public class ApplicationlessInjection
            implements
                HasActivityInjector,
                HasFragmentInjector,
                HasSupportFragmentInjector,
                HasServiceInjector,
                HasBroadcastReceiverInjector,
                HasContentProviderInjector {
    
        private static ApplicationlessInjection instance = null;
    
        @Inject DispatchingAndroidInjector<Activity> activityInjector;
        @Inject DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector;
        @Inject DispatchingAndroidInjector<android.app.Fragment> fragmentInjector;
        @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
        @Inject DispatchingAndroidInjector<Service> serviceInjector;
        @Inject DispatchingAndroidInjector<ContentProvider> contentProviderInjector;
    
        public ApplicationlessInjection(Context applicationContext) {
            AppComponent appComponent = DaggerAppComponent.builder().context(applicationContext).build();
            appComponent.inject(this);
        }
    
        @Override
        public DispatchingAndroidInjector<Activity> activityInjector() {
            return activityInjector;
        }
    
        @Override
        public DispatchingAndroidInjector<android.app.Fragment> fragmentInjector() {
            return fragmentInjector;
        }
    
        @Override
        public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
            return supportFragmentInjector;
        }
    
        @Override
        public DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
            return broadcastReceiverInjector;
        }
    
        @Override
        public DispatchingAndroidInjector<Service> serviceInjector() {
            return serviceInjector;
        }
    
        @Override
        public AndroidInjector<ContentProvider> contentProviderInjector() {
            return contentProviderInjector;
        }
    
        public static ApplicationlessInjection getInstance(Context applicationContext) {
            if(instance == null) {
                synchronized(ApplicationlessInjection.class) {
                    if (instance == null) {
                        instance = new ApplicationlessInjection(applicationContext);
                    }
                }
            }
    
            return instance;
        }
    
    }
    

    固定聚合应用程序活动

    public abstract class FixedDaggerAppCompatActivity extends AppCompatActivity implements HasFragmentInjector, HasSupportFragmentInjector {
    
        @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
        @Inject DispatchingAndroidInjector<android.app.Fragment> frameworkFragmentInjector;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            inject();
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public AndroidInjector<Fragment> supportFragmentInjector() {
            return supportFragmentInjector;
        }
    
        @Override
        public AndroidInjector<android.app.Fragment> fragmentInjector() {
            return frameworkFragmentInjector;
        }
    
        private void inject() {
            ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
    
            AndroidInjector<Activity> activityInjector = injection.activityInjector();
    
            if (activityInjector == null) {
                throw new NullPointerException("ApplicationlessInjection.activityInjector() returned null");
            }
    
            activityInjector.inject(this);
        }
    
    }
    

    固定碎片

    public abstract class FixedDaggerFragment extends Fragment implements HasSupportFragmentInjector {
    
        @Inject DispatchingAndroidInjector<Fragment> childFragmentInjector;
    
        @Override
        public void onAttach(Context context) {
            inject();
            super.onAttach(context);
        }
    
        @Override
        public AndroidInjector<Fragment> supportFragmentInjector() {
            return childFragmentInjector;
        }
    
    
        public void inject() {
            HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector();
    
            AndroidInjector<Fragment> fragmentInjector = hasSupportFragmentInjector.supportFragmentInjector();
    
            if (fragmentInjector == null) {
                throw new NullPointerException(String.format("%s.supportFragmentInjector() returned null", hasSupportFragmentInjector.getClass().getCanonicalName()));
            }
    
            fragmentInjector.inject(this);
        }
    
        private HasSupportFragmentInjector findHasFragmentInjector() {
            Fragment parentFragment = this;
    
            while ((parentFragment = parentFragment.getParentFragment()) != null) {
                if (parentFragment instanceof HasSupportFragmentInjector) {
                    return (HasSupportFragmentInjector) parentFragment;
                }
            }
    
            Activity activity = getActivity();
    
            if (activity instanceof HasSupportFragmentInjector) {
                return (HasSupportFragmentInjector) activity;
            }
    
            ApplicationlessInjection injection = ApplicationlessInjection.getInstance(activity.getApplicationContext());
            if (injection != null) {
                return injection;
            }
    
            throw new IllegalArgumentException(String.format("No injector was found for %s", getClass().getCanonicalName()));
        }
    
    }
    

    固定标记服务

    public abstract class FixedDaggerIntentService extends IntentService {
    
        public FixedDaggerIntentService(String name) {
            super(name);
        }
    
        @Override
        public void onCreate() {
            inject();
            super.onCreate();
        }
    
        private void inject() {
            ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
    
            AndroidInjector<Service> serviceInjector = injection.serviceInjector();
    
            if (serviceInjector == null) {
                throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null");
            }
    
            serviceInjector.inject(this);
        }
    
    }
    

    @Singleton
    @Component(modules = {
            AppModule.class,
            ActivityBindingModule.class,
            AndroidSupportInjectionModule.class
    })
    public interface AppComponent extends AndroidInjector<ApplicationlessInjection> {
    
        @Override
        void inject(ApplicationlessInjection instance);
    
        @Component.Builder
        interface Builder {
    
            @BindsInstance
            AppComponent.Builder context(Context applicationContext);
    
            AppComponent build();
    
        }
    
    }
    

    我的AppModule

    @Module
    public abstract class AppModule {
    
        @Binds
        @ApplicationContext
        abstract Context bindContext(Context applicationContext);
    
    }
    

    为了完整起见,我的@ApplicationContext注释

    @Qualifier
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApplicationContext {}
    

    希望我也能在代码方面帮助其他人。对我来说,我可以解决与推出Dagger 2和怪异的Android 7.0版本有关的所有崩溃。

    如果需要更多的澄清,请告诉我!

        2
  •  0
  •   chandrakant sharma    7 年前

    我在我的应用程序中遇到了相同的问题,我使用以下代码解决了这个问题:

    Application app = activity.getApplication();
    if(app == null) {
         app = (Application)activity.getApplicationContext();
    }