【问题标题】:Dagger2: no injector factory bound for fragmentDagger2:没有注入器工厂绑定到片段
【发布时间】:2018-02-19 19:32:56
【问题描述】:

我正在尝试将我正在构建的项目转换为将 dagger-android API 用于 DI 框架,但是在尝试使用 @ContributesAnroidInjector 注入片段时,我遇到了 IllegalArgumentException 的死胡同。

相关模块和组件如下:

ApplicationComponent.java

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class,
    ApplicationModule.class,
    ActivityBindingModule.class,
    DataManagerModule.class})
public interface ApplicationComponent extends AndroidInjector<MyApplication> {

DataManagerContract getDataManager();

void inject(MyApplication application);

@Component.Builder
interface Builder {

    @BindsInstance
    ApplicationComponent.Builder application(Application application);

    ApplicationComponent build();
    }
}

我的ActivityBindingModule.java

@Module
public abstract class ActivityBindingModule {

    @ActivityScope
    @ContributesAndroidInjector(modules = MainActivityModule.class)
    abstract MainActivity bindMainActivity();

    @ActivityScope
    @ContributesAndroidInjector(modules = SplashActivityModule.class)
    abstract SplashActivity bindSplashActivity();

    @ActivityScope
    @ContributesAndroidInjector(modules = LoginActivityModule.class)
    abstract LoginActivity bindLoginActivity();
}

MainActivityModule.java

@Module
public abstract class MainActivityModule {

    @ActivityScope
    @Binds
    abstract MainActivityContract.Presenter provideMainActivityPresenter(MainActivityPresenter presenter);

    @FragmentScope
    @ContributesAndroidInjector
    abstract HomeFragment provideHomeFragment();

    @FragmentScope
    @Binds
    abstract HomeFragmentContract.Presenter provideHomeFragmentPresenter(HomeFragmentPresenter presenter);

    // Inject other fragments and presenters
}

SplashActivity 和 LoginActivity 仅取决于它们各自的演示者,而 dagger 在这些演示者中可以正常工作。但是我的 MainActivity 可以包含许多片段,并在尝试使用以下方法注入其中一个片段时导致崩溃:

HomeFragment.java

public class HomeFragment extends Fragment {
    ....
    @Override
    public void onAttach(Context context) {
        AndroidSupportInjection.inject(this);
        super.onAttach(context);
    }
    ....
}

这是我这次崩溃的日志:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp/com.myapp.main.MainActivity}: java.lang.IllegalArgumentException: No injector factory bound for Class<com.myapp.ui.main.Home.HomeFragment>
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2665)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Caused by: java.lang.IllegalArgumentException: No injector factory bound for Class<com.myapp.ui.main.Home.HomeFragment>
        at dagger.android.DispatchingAndroidInjector.inject(DispatchingAndroidInjector.java:104)
        at dagger.android.support.AndroidSupportInjection.inject(AndroidSupportInjection.java:74)
        at com.myapp.ui.main.Home.HomeFragment.onAttach(HomeFragment.java:65)
        at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1363)
        at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1109)
        at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:996)
        at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:99)
        at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2364)
        at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322)
        at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2229)
        at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3221)
        at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3171)
        at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:192)
        at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:560)
        at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:177)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1248)
        at android.app.Activity.performStart(Activity.java:6696)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2628)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2726) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6119) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

我不确定代码中的问题出在哪里。如果我将 HomeFragment 的绑定移动到 ActivityBindingModule,应用程序运行良好,但如果我将这些绑定带回 MainActivityModule,则崩溃会再次出现。我在这里做错了什么?

编辑:

public class MyApp extends DaggerApplication {

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerApplicationComponent.builder().application(this).build();
    }
}

和我的主要活动:

public class MainActivity extends AppCompatActivity
    implements MainActivityContract.View,
    NavigationView.OnNavigationItemSelectedListener {

@Inject
MainActivityContract.Presenter mPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    AndroidInjection.inject(this);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Open home fragment on first start
    if (savedInstanceState == null) {
        // Create new instance of HomeFragment
        HomeFragment homeFragment = HomeFragment.newInstance();

        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.content_main, homeFragment)
                .commit();
    }

// Other logic
}

【问题讨论】:

  • 你的Activity是否实现HasFragmentInjector?能否附上代码?
  • 我编辑了我的问题以包含扩展 DaggerApplication 的自定义应用程序和包含我的片段的主要活动。该活动未实现HasFragmentInjector。我的印象是,通过扩展DaggerApplication,我不必在我的活动中实现HasFragmentInjector

标签: java android dagger-2


【解决方案1】:

可能还有其他场景,我遇到了类似的错误:

可能的情况 1:
当您从 Fragment 显示 DialogFragment 时。
使用相同的 FragmentManager 很重要。

例如,您有一个“片段范围的屏幕”:

@FragmentScope
@ContributesAndroidInjector(modules = [HomeInjectors::class])
abstract fun provideHomeFragment() HomeFragment

带有子组件

@Module
abstract class HomeInjectors {

    @ChildFragmentScope
    @ContributesAndroidInjector(modules = [DetailsModule::class])
    abstract fun provideDetailsFragment(): DetailsDialogFragment

}

需要注意的是,当您显示对话片段时, 您应该使用 子片段管理器 而不是 Activity 的。

在这种情况下,如果您从 HomeFragment 显示对话框,

detailsDialog.show(activity.supportFragmentManager, "some tag)

detailsDialog.show(requireFragmentManager(), "some tag)

不会工作。

你应该这样做:

detailsDialog.show(childFragmentManager, "some tag)

可能的情况 2: 带有子片段的父片段。

为了使子片段具有“更小”的范围(示例代码与上面相同,但将 DetailsDialogFragment 视为常规片段和 HomeFragment 的子片段)。

就我而言,子片段无法找到父片段注入器。

原因是在提供子片段注入器时,我错误地将我的BaseFragment implement HasFragmentInjector.
但是,由于我使用支持片段(AndroidX 或其他),我应该制作BaseFragment implement HasSupportFragmentInjector

所以 BaseFragment 可能看起来像:

import androidx.fragment.app.Fragment

abstract class BaseFragment : SometFragment(), HasSupportFragmentInjector {

    @Inject lateinit var childFragmentInjector: DispatchingAndroidInjector<Fragment>

    override fun supportFragmentInjector(): AndroidInjector<Fragment> {
        return childFragmentInjector
    }

    override fun onAttach(context: Context) {
        AndroidSupportInjection.inject(this)
        super.onAttach(context)
    }
}

当由于某些原因您的“BaseFragment”必须具有 DaggerFragment 父级以外的情况时,它很有用

【讨论】:

    【解决方案2】:

    当您使用AndroidSupportInjection.inject(this)HomeFragment 注入时,Dagger 将遍历父片段层次结构以找到实现HasSupportFragmentInjector 的人。为了让它工作,让你的 MainActivity 扩展 DaggerAppCompatActivity 实现 HasSupportFragmentInjector

    来自AndroidSupportInjection.inject(Fragment fragment)的文档:

    如果有关联的 {@link,则注入 {@code fragment} dagger.android.AndroidInjector} 实现可以找到,否则 抛出 {@link IllegalArgumentException}。

    使用以下算法找到合适的 {@code 用于注入 {@code fragment} 的 AndroidInjector}:

    1. 遍历父片段层次结构以找到实现 {@link 的片段 HasSupportFragmentInjector},如果没有
    2. 如果实现,则使用 {@code fragment} 的 {@link Fragment#getActivity() 活动} {@link HasSupportFragmentInjector},如果没有
    3. 如果实现了 {@link,则使用 {@link android.app.Application} HasSupportFragmentInjector}。

    如果它们都没有实现 {@link HasSupportFragmentInjector},则 {@link 抛出 IllegalArgumentException}。

    @throws IllegalArgumentException 如果没有父片段、活动或 应用程序实现 {@link HasSupportFragmentInjector}。

    有了这个,Dagger 将使用

    @FragmentScope
    @ContributesAndroidInjector
    abstract HomeFragment provideHomeFragment();
    

    从您的MainActivityModule 注入您的HomeFragment

    【讨论】:

    • 谢谢,这很有意义。您使用@Inject HomeFragment homeFragment 的解决方法似乎是仅包含单个片段的活动的明显解决方案,但在活动可以包含大量片段的情况下,将每个片段的实例注入其中似乎效率低下MainActivity,不是吗?有没有办法做类似的事情,但不注入每个片段的实例,或者只在需要时注入片段?
    • 只在需要的时候注入片段,你可以使用惰性注入,见这里:google.github.io/dagger/api/2.10/dagger/Lazy.html
    • @C.Marr 查看我的更新答案,以使其按照您真正想要的方式工作
    • 对于那些使用 DaggerFragment 并面临这个问题的人来说,对我有用的是,即使我没有在相关的 Activity 中注入任何东西,但我必须从 DaggerAppCompactActivity 和还需要向它提供@ContributesAndroidInjector(),就像我已经为 Fragment 所做的那样。
    【解决方案3】:

    就我而言,我没有在@Module 类中定义Fragment!所以当我像这样添加它时:

    @ActivityScope
    @ContributesAndroidInjector(modules = {ActivityModule.class})
    abstract InvestmentDetailsFragment bindInvestmentDetailsFragment();
    

    我的问题解决了!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-22
      • 2018-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多