草庐IT

android - 在 Android 7.0 和三星设备上出现 Dagger 2 的 RuntimeException

coder 2023-06-06 原文

自从我开始使用 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)

我无法重现该问题,因为我手头没有任何受影响的设备,而且似乎并非某种类型的所有设备都受到影响,更像是随机启动失败。

从我通过研究了解到的情况是, Activity 的 onCreate 很可能在 Activity 实际附加到应用程序之前被调用。但我无法证明这个说法......

我正在遵循 Google 的 MVP+Dagger 架构蓝图。

我的应用程序类:

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;
    }

}

我的 MainActivity 类:

public class MainActivity extends DaggerAppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

}

Dagger 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); 
}

Android注入(inject): 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);
    }

}

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

  • 如果问题是由 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)

所以 inject() 中的 if 子句 !(application instanceof HasActivityInjector) 并没有重新路由到 injectWithWrongApplication() 而是相同的 if 子句在同一 Application 实例上的 injectNow(Application application) 中导致 RuntimeException。怎么回事?我的代码看起来像 100 次,但如果我有错误,请告诉我!否则,我想在 7.0 的一些供应商实现中会发生一些非常奇怪的事情,这些事情可能无法修复......

基于 https://github.com/google/dagger/issues/748 上的讨论我还推出了一个测试版本,它在所有 Dagger 组件中只使用 getApplicationContext() 而不是 getApplication(),没有任何区别。

list 中的我的应用程序标记

<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>

最佳答案

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

已知问题

  • 在自定义应用程序类中没有依赖注入(inject)(无论如何,这对于可怕的 Android 7.0 OEM 实现来说可能不是一个好主意)
  • 并不是我修改了所有 Dagger 组件,我只替换了 DaggerAppCompatActivityDaggerIntentServiceDaggerFragment。如果您使用其他组件,如 DaggerDialogFragmentDaggerBroadcastReceiver 您需要创建自己的工具,但我想这应该不会太难:)

实现

停止使用 DaggerApplication。要么从标准 Application 再次扩展您的自定义应用程序,要么完全摆脱自定义应用程序。对于 Dagger 2 的依赖注入(inject),它不再需要了。只需扩展例如FixedDaggerAppCompatActivity,您可以使用 Dagger 2 DI 进行 Activity 。

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

无应用程序注入(inject)

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;
    }

}

FixedDaggerAppCompatActivity

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);
    }

}

FixedDaggerFragment

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()));
    }

}

FixedDaggerIntentService

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();

    }

}

我的应用模块

@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 版本相关的所有崩溃问题。

如果需要更多说明,请告诉我!

关于android - 在 Android 7.0 和三星设备上出现 Dagger 2 的 RuntimeException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46784685/

有关android - 在 Android 7.0 和三星设备上出现 Dagger 2 的 RuntimeException的更多相关文章

  1. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. ruby - 使用 rbenv 和 ruby​​-build 构建 ruby​​ 失败,出现 undefined symbol : SSLv2_method - 2

    我正在尝试在配备ARMv7处理器的SynologyDS215j上安装ruby​​2.2.4或2.3.0。我用了optware-ng安装gcc、make、openssl、openssl-dev和zlib。我根据README中的说明安装了rbenv(版本1.0.0-19-g29b4da7)和ruby​​-build插件。.这些是随optware-ng安装的软件包及其版本binutils-2.25.1-1gcc-5.3.0-6gconv-modules-2.21-3glibc-opt-2.21-4libc-dev-2.21-1libgmp-6.0.0a-1libmpc-1.0.2-1libm

  4. ruby-on-rails - 禁用设备的 :confirmable on-the-fly to batch-generate users - 2

    Devise是一个Ruby库,它为我提供了这个User类:classUser当写入:confirmable时,注册时会发送一封确认邮件。上周我不得不批量创建300个用户,所以我在恢复之前注释掉了:confirmable几分钟。现在我正在为用户批量创建创建一个UI,因此我需要即时添加/删除:confirmable。(我也可以直接修改Devise的源码,但我宁愿不去调和它)问题:如何即时添加/删除:confirmable? 最佳答案 WayneConrad的解决方案:user=User.newuser.skip_confirmation

  5. ruby - 为什么 return 关键字会导致我的 'if block' 出现问题? - 2

    下面的代码工作正常:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson)do|key,oldv,newv|ifkey==:aoldvelsifkey==:bnewvelsekeyendendputskerson.inspect但是如果我在“ifblock”中添加return,我会得到一个错误:person={:a=>:A,:b=>:B,:c=>:C}berson={:a=>:A1,:b=>:B1,:c=>:C1}kerson=person.merge(berson

  6. ruby - 安装 tiny_tds 在 mac os 10.10.5 上出现错误 - 2

    我正在使用macos,我想使用ruby​​驱动程序连接到sqlserver。我想使用tiny_tds,但它给出了缺少free_tds的错误,但它已经安装了。怎么能过这个?~brewinstallfreetdsWarning:freetds-0.91.112alreadyinstalled~sudogeminstalltiny_tdsBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtiny_tds:ERROR:Failedtobuildgemnativeextension.完整日志如下:/System

  7. ruby - 如何让几条 haml 线出现在同一行上? - 2

    我有以下haml:9%strongAskedby:10=link_to@user.full_name,user_path(@user)11.small="(#{@question.created_at.strftime("%B%d,%Y")})"这当前将链接和日期放在不同的行上,当它看起来像“链接(日期)”并且日期的类跨度为小...... 最佳答案 您的代码将生成类似这样的html:Askedby:UsernameApril26,2011当您使用类似.small的东西(即使用点而不指定元素类型)时,haml会创建一个implicit

  8. ruby - 获取数组中值的最大连续出现次数 - 2

    下面有没有更优雅的方法来实现这个:输入:array=[1,1,1,0,0,1,1,1,1,0]输出:4我的算法:streak=0max_streak=0arr.eachdo|n|ifn==1streak+=1elsemax_streak=streakifstreak>max_streakstreak=0endendputsmax_streak 最佳答案 类似于w0lf'sanswer,但通过从chunk返回nil来跳过元素:array.chunk{|x|x==1||nil}.map{|_,x|x.size}.max

  9. ruby - 按数组中出现的频率排序 - 2

    有没有一种有效的方法来做到这一点。我有一个数组a=[1,2,2,3,1,2]我想按升序输出出现的频率。示例[[3,1],[1,2],[2,3]]这是我的ruby​​代码。b=a.group_by{|x|x}out={}b.eachdo|k,v|out[k]=v.sizeendout.sort_by{|k,v|v} 最佳答案 a=[1,2,2,3,1,2]a.each_with_object(Hash.new(0)){|m,h|h[m]+=1}.sort_by{|k,v|v}#=>[[3,1],[1,2],[2,3]]

  10. ruby - 使用 SizedQueue 在 ruby​​ 代码中出现死锁 - 2

    我认为我对线程在ruby​​中的工作原理存在根本性的误解,我希望获得一些见解。我想要一个简单的生产者和消费者。首先,生产者线程从文件中提取行并将它们粘贴到SizedQueue中;当那些用完时,在末端贴上一些token,让消费者知道事情已经完成。require'thread'numthreads=2filename='edition-2009-09-11.txt'bq=SizedQueue.new(4)producerthread=Thread.new(bq)do|queue|File.open(filename)do|f|f.eachdo|r|queue现在有几个消费者。为简单起见,让

随机推荐