【问题标题】:What is actual usage of "HasFragmentInjector" in Dagger 2Dagger 2中“HasFragmentInjector”的实际用法是什么
【发布时间】:2017-06-07 09:47:46
【问题描述】:

我之前已经实现了 dagger2 v2.2,但现在他们也添加了 dagger.android 部分。所以我正在用它创建示例项目。

我知道 @Provide@Modules@Components 等注释的旧方法,但是从 Dagger 2.8+ 开始,他们添加了这个android-support 库也有一些新的注入,如 @ActivityKey@ContributesAndroidInjector@Subcomponent.Builder 等。

所以我的问题是它给桌面带来了什么好处。

它是否解决了诸如基类的 Inject 方法适用于所有子类的问题?或任何其他好处?

第二个问题 - HasFragmentInjector 只是像我们以前使用片段管理器那样在活动中加载片段?还是我错过了什么?

【问题讨论】:

  • 根据我对 dagger2 的非常有限的了解,尤其是这个新东西,我可以告诉你,您可以使用基本活动和基本片段类来为其子类注入依赖项。至于第二个问题,匕首获取片段注入器,用于将依赖项注入到创建的片段中。但是你仍然必须使用 FragmentManager。
  • 这里我提取了我的匕首相关代码,我不确定它是否全部正确,但它正在工作。 gist.github.com/anonymous/45446b3bf2100d1c22520ebf82f0bff3
  • @KrystianP。感谢您的信息,但我仍在等待明确的答案。这有助于我清楚地理解这个概念。
  • 您在文档中看到 this page 了吗?
  • @MidasLefko 是的,我已经看过这些页面,但无法理解它的确切用法。

标签: android dependency-injection dagger-2 dagger


【解决方案1】:

第一个问题

在 Dagger 2.8+ 中,他们添加了这个 android-support 库,其中还添加了一些新的注释,如 @ActivityKey@ContributesAndroidInjector@Subcomponent.Builder 等。所以我的问题是它给桌面带来了什么好处。

What are the advantages of using DispatchingAndroidInjector and the other dagger-android classes?已经回答了这个问题

它是否解决了诸如没有适用于所有子类的基类的注入方法之类的问题?

Dagger 2 在编译时使用代码生成来进行依赖注入。在这一点上,它与其他依赖注入框架(如 Guice)在运行时检查注入站点不同。为了让 Dagger 2 工作,您必须在某些时候指定注入位点的不变量。因此,永远不可能写出这样的东西:

void inject(Activity activity);

在 Dagger 2 组件中并让它注入所有活动。

但是,dagger-android 中的新类有很多改进。而之前你必须写:

void inject(MainActivity mainActivity);

对于每个不同的注射部位,您现在可以编写以下代码:

@Module(subcomponents = MainActivitySubcomponent.class)
public abstract class MainActivityModule {

    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> mainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
 }

然后:

AndroidInjection.inject(this);

在您的 MainActivity 中的适当位置。

第二个问题

HasFragmentInjector 只是像我们以前使用 FragmentManager 那样在 Activity 中加载 Fragment?还是我错过了什么?

HasFragmentInjector 只是标记了 Fragment 应该从哪个类获取其AndroidInjector。您可以在代码on GitHub 中亲自查看AndroidInjection#inject(Fragment fragment)

public static void inject(Fragment fragment) {
    checkNotNull(fragment, "fragment");
    HasFragmentInjector hasFragmentInjector = findHasFragmentInjector(fragment);
    Log.d(TAG, String.format(
        "An injector for %s was found in %s",
        fragment.getClass().getCanonicalName(),
        hasFragmentInjector.getClass().getCanonicalName()));

    AndroidInjector<Fragment> fragmentInjector = hasFragmentInjector.fragmentInjector();
    checkNotNull(fragmentInjector,"%s.fragmentInjector() returned null",
    hasFragmentInjector.getClass().getCanonicalName());
    fragmentInjector.inject(fragment);
} 

从 javadoc 中,该方法首先遍历 parent-fragment,然后是 Activity,最后是 Application 找到 HasFragmentInjector 并使用 AndroidInjector&lt;Fragment&gt; 注入 Fragment 的字段。

但是,HasFragmentInjector 的存在并不意味着您应该开始使用 Dagger 2 管理 Fragments:

public class MainActivity {

     @Inject CoffeeFragment coffeeFragment; //no! don't do this
     @Inject TeaFragment teaFragment; //no!

您仍然应该使用使用静态工厂方法来实例化片段的惯用方式。当您使用事务添加片段或委托给 ViewPager 时,当调用它们的 onAttach(Context context) 时,Dagger 2 将为片段中的字段执行注入。代替上面的例子,下面的代码是一个非常简单的 Activity,有一个 ViewPager 和两个 Fragment:

public class MainActivity extends AppCompatActivity implements HasSupportFragmentInjector {

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentDispatchingAndroidInjector;

    ViewPager mViewPager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        BeveragesPagerAdapter beveragesPagerAdapter = new BeveragesPagerAdapter(getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.viewpager);
        mViewPager.setAdapter(beveragesPagerAdapter);
    }

    class BeveragesPagerAdapter extends FragmentStatePagerAdapter {

        public BeveragesPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int i) {
            switch (i) {
                case 0:
                    return TeaFragment.instantiate(new Bundle());
                case 1:
                    return CoffeeFragment.instantiate(new Bundle());
                default:
                    throw new IllegalStateException();
            }
        }

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return "tab " + (position + 1);
        }
    }

    @Override
    public AndroidInjector<Fragment> supportFragmentInjector() {
        return fragmentDispatchingAndroidInjector;
    }
}

FragmentStatePagerAdapter 正确处理 Fragment 的管理,我们不会在 MainActivity 中注入字段。

片段本身如下所示:

CoffeeFragment.java中:

public class CoffeeFragment extends Fragment {

    public static CoffeeFragment instantiate(@Nullable Bundle arguments) {
        CoffeeFragment coffeeFragment = new CoffeeFragment();
        coffeeFragment.setArguments(arguments);
        return coffeeFragment;
    }

    @Inject
    @Named("Coffee")
    Repository repository;

    TextView textView;

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

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_coffee, container, false);
        textView = (TextView) v.findViewById(R.id.coffee_textview);
        return v;
    }

    @Override
    public void onResume() {
        textView.setText(repository.retrieve());
    }
}

CoffeeFragmentModule.java中:

@Module(subcomponents = CoffeeFragmentSubcomponent.class )
public abstract class CoffeeFragmentModule {

    @Binds
    @Named("Coffee")
    abstract Repository repository(CoffeeRepository coffeeRepository);

    @Binds
    @IntoMap
    @FragmentKey(CoffeeFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> bindCoffeeFragmentInjectorFactory(CoffeeFragmentSubcomponent.Builder builder);
}

CoffeeFragmentSubcomponent.java中:

@Subcomponent
public interface CoffeeFragmentSubcomponent extends AndroidInjector<CoffeeFragment> {

    @Subcomponent.Builder
    abstract class Builder extends AndroidInjector.Builder<CoffeeFragment> {}
}

CoffeeRepository.java

public class CoffeeRepository implements Repository {

    @Inject
    public CoffeeRepository() {
    }

    @Override
    public String retrieve() {
        return "Coffee!!!!";
    }
}

【讨论】:

  • 感谢您的回答。在我的下一个项目中开始使用匕首之前,我仍在寻找一些答案。我将在接下来的几天内无法使用,所以如果您找到更多信息,请告诉我。我要赏金给你谢谢并继续努力......
  • 如果我有一页有 2 个碎片,另一页有 3 个碎片。我可以在两个活动中都实现 HasSupportFragmentInjector 吗?
【解决方案2】:

Official Documentation 在我看来很好地解释了这个话题。

无论如何,主要的好处是,而不是这样的东西

((SomeApplicationBaseType) getContext().getApplicationContext())
    .getApplicationComponent()
    .newActivityComponentBuilder()
    .activity(this)
    .build()
    .inject(this);

你可以简单地写这个,这让每个人的生活更轻松。

AndroidInjection.inject(this);
  1. 样板更少,更易于维护。

  2. 之前的方法有点打破了依赖注入的基本概念,一个类不应该知道依赖注入方式的任何细节。

【讨论】:

    猜你喜欢
    • 2019-05-04
    • 1970-01-01
    • 2017-05-19
    • 2019-03-03
    • 1970-01-01
    • 2018-11-04
    • 1970-01-01
    • 2019-12-18
    • 1970-01-01
    相关资源
    最近更新 更多