Что такое фактическое использование «HasFragmentInjector» в кинжале 2

Я уже реализовал dagger2 v2.2, но теперь они добавили часть кинжала. Поэтому я создаю образец проекта с этим.

Я знаю о старой методологии @Provide и @Modules и @Components и т. Д., Но из Dagger 2.8+ они добавили эту библиотеку поддержки Android, которая также имеет некоторые новые инъекции, такие как @ActivityKey , @ContributesAndroidInjector , @ Subcomponent.Builder и т. Д.

Поэтому мой вопрос заключается в том, какие преимущества он приносит в таблицу.

Устраняет ли он проблемы, такие как метод Inject с базовым классом, может работать для всех дочерних классов? Или любые другие преимущества?

Второй вопрос – HasFragmentInjector – это просто загрузить фрагмент внутри действия, как мы делали с помощью диспетчера фрагментов? Или я что-то упускаю?

Пожалуйста, не уменьшайте его более информативный вопрос для всех пользователей библиотеки, так как документация библиотеки не дает таких ответов.

    Первый вопрос

    В Dagger 2.8+ они также добавили эту библиотеку поддержки Android, которая имеет несколько новых аннотаций, таких как @ActivityKey , @ContributesAndroidInjector , @Subcomponent.Builder и т. Д. Поэтому мой вопрос в том, какие преимущества он приносит в таблицу.

    На это уже был дан ответ. Каковы преимущества использования DispatchingAndroidInjector и других классов кинжала-андроида?

    Решает ли он проблемы, такие как отсутствие метода инъекции для базового класса, который может работать для всего дочернего класса?

    Кинжал 2 использует генерацию кода во время компиляции для инъекции зависимостей. В этом он отличается от других схем инъекций зависимостей, таких как Guice, которые проверяют сайты инъекций во время выполнения. Для того, чтобы Кинжал 2 работал, вы должны в какой-то момент указать инвариант места инъекции. Поэтому никогда не удастся написать что-то вроде:

     void inject(Activity activity); 

    Внутри компонента Dagger 2 и приложить все действия.

    Тем не менее, есть много улучшений с новыми классами, доступными в кинжале-андроиде. Если раньше вам приходилось писать:

     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 – это просто загрузить Fragment внутри Activity, как мы это делали, используя FragmentManager? Или я чего-то не хватает?

    HasFragmentInjector просто отмечает класс, из которого Fragment должен получить свой AndroidInjector . Вы можете сами убедиться в коде 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 этот метод сначала проходит родительский фрагмент, затем Activity, затем, наконец, приложение, чтобы найти HasFragmentInjector и использует AndroidInjector<Fragment> для ввода полей фрагмента.

    Однако присутствие HasFragmentInjector не означает, что вы должны начать управлять фрагментами с помощью Dagger 2:

     public class MainActivity { @Inject CoffeeFragment coffeeFragment; //no! don't do this @Inject TeaFragment teaFragment; //no! 

    Вы все равно должны использовать идиоматический способ создания экземпляров фрагментов, использующих статические заводские методы. Кинжал 2 выполнит инъекцию для полей внутри Фрагментов, когда их onAttach(Context context) вызывается, когда, скажем, вы добавляете фрагмент, используя транзакцию, или делегируете в ViewPager. Вместо приведенного выше примера следующий код представляет собой очень простое действие с ViewPager и двумя фрагментами:

     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 правильно обрабатывает управление фрагментами, и мы не вводим их в качестве полей внутри 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!!!!"; } } 

    Официальная документация хорошо объясняет эту тему в моем выступлении.

    В любом случае главное преимущество заключается в том, что вместо чего-то подобного

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

    Вы можете просто написать это, что облегчит жизнь для всех.

     AndroidInjection.inject(this); 
    1. Меньше шаблона, удобство в обслуживании.

    2. Предыдущий подход является своего рода нарушением базовой концепции инъекции зависимости, класс не должен знать никаких подробностей о том, как вводятся зависимости.