【问题标题】:Dagger 2 on Android: inject same dependency in Activity and retained FragmentAndroid 上的 Dagger 2:在 Activity 中注入相同的依赖项并保留 Fragment
【发布时间】:2015-08-20 09:01:36
【问题描述】:

我有 F1F2 类的对象,我想将它们注入到保留的 Fragment 中。我还有一个 A 类的对象,它依赖于 Activity,我希望它被注入到该 Activity 和附加到该 Activity 的片段管理器的保留片段中。我编写以下代码。一、Activity依赖的模块:

@Module
public class MainActivityModule {
    private Activity mActivity;

    public MainActivityModule(Activity activity) {
        mActivity = activity;
    }

    @Provides
    @ActivityScope
    public A provideA() {
        return new A(mActivity);
    }
}

然后,相应的组件必须使A 对象对其依赖组件可用:

@ActivityScope
@Component(modules = {MainActivityModule.class})
public interface MainActivityComponent {
    void inject(MainActivity activity);

    // make the A object available to dependent components
    A getA();
}

我也写了Fragment相关的模块:

@Module
public class FragmentModule {
    @Provides
    @FragmentScope
    public F1 provideF1() {
        return new F1();
    }

    @Provides
    @FragmentScope
    public F2 provideF2() {
        return new F2();
    }
}

及其对应的组件:

@FragmentScope
@Component(modules = {FragmentModule.class}, dependencies = {MainActivityComponent.class})
public interface FragmentComponent {
    void inject(MyFragment presenter);
}

最后,我在Activity中注入了对A的依赖,在这里我还需要调用具体的生命周期方法。 Activity 还提供了获取组件的方法,以便 Fragment 在构建自己的组件时能够使用它:

// in MainActivity.onCreate
mActivityComponent = DaggerMainActivityComponent.builder()
        .mainActivityModule(new MainActivityModule(this))
        .build();
mActivityComponent.inject(this);
mA.onCreate();

我也尝试在 Fragment 中注入对 AF1F2 的依赖项:

// in MyFragment.onCreate
FragmentComponent component = DaggerFragmentComponent.builder()
        .fragmentModule(new FragmentModule())
        .mainActivityComponent(((MainActivity) getActivity()).getComponent())
        .build();
component.inject(this);

但是,由于 Fragment 被保留,当系统响应配置更改(例如设备旋转)而销毁并重新创建 Activity 时,Fragment 保持对旧 A 实例的引用,而新 Activity已经正确地重新创建了一个新的 A 实例来配合它。要解决这个问题,我必须创建FragmentComponent 并将依赖项注入MyFragment.onActivityCreated 而不是MyFragment.onCreate。另一方面,这意味着每次销毁和重新创建活动时都会重新创建F1F2 依赖项;但它们是 Fragment 范围的依赖项,因此它们应该遵循 Fragment 生命周期而不是 Activity 的。

因此,我的问题如下:是否可以在保留的 Fragment 中注入不同范围的依赖项?理想情况下,F1F2 依赖项应该注入MyFragment.onCreate,而A 依赖项应该注入MyFragment.onActivityCreated。我尝试使用两个不同的组件,但似乎无法执行部分注入。目前,我最终在MyFragment.onActivityCreated 中添加了对片段A 依赖项的显式重新分配,但这并不是真正的注入,你知道的。能否以更好的方式完成?

【问题讨论】:

  • 注入不能很好地处理生命周期不受您控制的对象(如活动)。通常一个片段只能与它的父活动一起工作,所以你并没有真正从注入中受益。但是,对于单元测试,您可以提供 setA(A a) 方法,以便您可以使用模拟/测试对象覆盖该值。另外:你为什么使用 2 个独立的组件并分别注入图表(而不是一个应用程序范围的组件,因此在应用程序启动时只有一个图表构建)?
  • @Ogre_BGR 我知道这很难,这就是我在这里寻求帮助的原因。我看到的好处与减少进入 Activity 的代码量有关;它需要在其A 对象上包含与生命周期相关的调用,但由于该对象确实由 Fragment 使用,因此该代码应该保留在 Fragment 中,而不是 Activity 中。所以它与代码组织有关,而不是测试。我不能使用应用程序范围的组件,因为A(我没有完全控制)严格依赖于 Activity 实例(而不仅仅是 Context)。
  • 你可以制作一个应用模块并从应用模块中获取所有实例
  • @GiulioPiancastelli 你能解决你的问题吗?我有同样的问题
  • 我想你误解了我的意思。 Fragment 组件当然应该是更高级别范围的子组件,因此应该从其自身或更高范围注入它需要的任何内容。但是,如果您要使用保留的片段,那么片段将不再属于单个活动,因此将它们作为活动的子组件而是作为同级可能不合适。如果您不使用保留的片段,那么将片段作为活动的子组件非常适合。否则,请小心。

标签: android android-fragments dependency-injection dagger dagger-2


【解决方案1】:

考虑到您保留的片段的寿命比您的活动长,我敢打赌,正确的做法是让FragmentScope 包含ActivityScope,反之亦然。

意味着你的 FragmentComponent 会有

@FragmentScope
@Component(modules = {FragmentModule.class})
public interface FragmentComponent {
    void inject(MyFragment presenter);
}

你的 Activity 组件会有

@ActivityScope
@Component(dependencies = {FragmentComponent.class}, modules = {MainActivityModule.class})
public interface MainActivityComponent extends FragmentComponent { //provision methods
    void inject(MainActivity activity);

    // make the A object available to dependent components
    A getA();
}

如果您的Fragment 注入类不依赖 Activity 模块作为依赖项,这是可能的。

这可以用类似于

的东西来完成
public class MainActivity extends AppCompatActivity {

    private MainActivityComponent mainActivityComponent;

    private MyFragment myFragment;

    @Override
    public void onCreate(Bundle saveInstanceState) {
         super.onCreate(saveInstanceState);
         setContentView(R.layout.activity_main);

         if(saveInstanceState == null) { // first run
             myFragment = new MyFragment(); //headless retained fragment
             getSupportFragmentManager()
                .beginTransaction()
                .add(myFragment, MyFragment.class.getName()) //TAG
                .commit();
         } else {
             myFragment = (MyFragment)(getSupportFragmentManager()
                               .findFragmentByTag(MyFragment.class.getName()));
         }
    }

    @Override
    public void onPostCreate() {
         mainActivityComponent = DaggerMainActivityComponent.builder()
              .fragmentComponent(myFragment.getComponent())
              .build();
    }
}

public class MyFragment extends Fragment {
    public MyFragment() {
         this.setRetainInstance(true);
    }

    private FragmentComponent fragmentComponent;

    @Override
    public void onCreate(Bundle saveInstanceState) {
        super.onCreate(saveInstanceState);
        this.fragmentComponent = DaggerFragmentComponent.create();
    }

    public FragmentComponent getFragmentComponent() {
        return fragmentComponent;
    }
}

编辑:

public class MyFragment extends Fragment {
    public MyFragment() {
         this.setRetainInstance(true);
         this.fragmentComponent = DaggerFragmentComponent.create();
    }

    private FragmentComponent fragmentComponent;

    public FragmentComponent getFragmentComponent() {
        return fragmentComponent;
    }
}

public class MainActivity extends AppCompatActivity {

    private MainActivityComponent mainActivityComponent;

    private MyFragment myFragment;

    @Inject
    A mA;

    @Override
    public void onCreate(Bundle saveInstanceState) {
         super.onCreate(saveInstanceState);
         setContentView(R.layout.activity_main);

         if(saveInstanceState == null) { // first run
             myFragment = new MyFragment(); //headless retained fragment
             getSupportFragmentManager()
                .beginTransaction()
                .add(myFragment, MyFragment.class.getName()) //TAG
                .commit();
         } else {
             myFragment = (MyFragment)(getSupportFragmentManager().findFragmentByTag(MyFragment.class.getName()));
         }
         mainActivityComponent = DaggerMainActivityComponent.builder()
              .fragmentComponent(myFragment.getComponent())
              .build();
         mainActivityComponent.inject(this);
         mA.onCreate();
    }
}

【讨论】:

  • 这是一个非常有趣的观点,谢谢。目前(应用程序尚未完成)“我的Fragment 注入的类确实不依赖 Activity 模块作为依赖项”;但是,我仍然需要在MainActivity.onCreate 中调用mA.onCreate,而且我不确定是否可以将该调用延迟到MainActivity.onPostCreate。即使使用巧妙的解决方法,这似乎也是不可能的,不是吗?哦,还有一个问题:为什么我需要用MainActivityComponent 扩展FragmentComponent
  • 你需要指定这个mA.onCreate是你需要调用的,因为我目前没有看到它。 onPostCreate() 用于确保在为 Activity 创建组件之前运行片段的 onCreate() 方法(在每个片段初始化后调用onPostCreate())。我扩展了组件以获取它的提供方法,以防它被子类化。
  • “我目前看不到”是什么意思?它在问题的最后一个代码 sn-p 中。我刚刚编辑了一条评论(我写了MyActivity,我的意思是MainActivity),但电话一直在那里。
  • 哦。你是对的,它一直都在那里,我不知道为什么我没有注意到。我认为在onPostCreate() 中调用它可以工作,但如果没有,那么你可以在片段的构造函数中构造FragmentComponent,然后你不需要将它放在onPostCreate()
  • 谢谢。最后,我有机会根据您的解决方案重新审视自己的代码,但仍然存在至少一个差异:在我的情况下,保留的片段没有立即构建,也没有添加到片段管理器中;如果用户不使用片段帮助实现的特定功能,则保留的片段甚至可能永远不会构建。无论如何,您的解决方案将迫使我创建它。我想知道这是否真的可行,或者我最终会在 Android 或 Dagger 端偶然发现某种反模式。
猜你喜欢
  • 1970-01-01
  • 2017-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多