1. 程式人生 > >關於Dagger2 android 動態生成Fragment的問題

關於Dagger2 android 動態生成Fragment的問題

其實動態建立Fragment的流程並不複雜。

1. 網路獲取Fragment的個數。
2. 通過Component建立Fragment。

主要的問題在於:

1. 我想使用全域性AppComponent 中的ApiService等物件
2. 我的BaseFragment預設使用了Dagger android的注入處理,導致編譯失敗

解決方案

1.使用@SupComponent來提供構建,將subComponent放到AppComponent中即可
2.去掉子類Fragment中對 Dagger 的使用 ,去除掉Presenter中Dagger的使用。
    Fragment中需要的物件通過在Module中setter過來。
    Presenter中需要的物件,同理也是通過Module中傳遞過來。

使用@SubComponent 的原因是希望可以使用AppComponent中構建的  全域性物件 如:
     - ApiService 全域性的網路請求
     - Repository 全域性的資料庫操作等。

原理:在Module中可以使用Dagger,通過註解直接獲取需要物件(AppComponent中的全域性物件),
並通過setter方法設定給Fragment或Presenter。

更改如下:

1. 去除了子類Fragment中Dagger2 android的注入

BaseFragment中程式碼如下:


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

protected void inject() {
    //主要就是去掉這行程式碼
    AndroidSupportInjection.inject(this);
}

在NewsFragment 中如下處理

 @Override
protected void
inject() { //去除 Dagger 的inject 通過 newInstance() 建立 Fragment // super.inject(); }

2. 改變Presenter的注入方式

將原來的Dagger Inject的方式改為通過 Setter方法設定
- 改前

@Inject
NewsListContract.Presenter mPresenter;
  • 改後
NewsListContract.Presenter mPresenter;

public void setPresenter(NewsListContract.Presenter presenter) {
        this
.mPresenter = presenter; }

同理對於Fragment和Presenter中其他@Inject的物件也要使用Setter方法設定

3. 使用subComponent構建

   使用@SubComponent 的原因是希望可以使用AppComponent中構建的  全域性物件 如:
      - ApiService 全域性的網路請求
      - Repository 全域性的資料庫操作等。

NewsComponent.java

@NewsScope 
@Subcomponent(
        modules = {
                NewsListModule.class
        }
)
public interface NewsComponent {
    //想要的就是這個
    List<NewsListFragment> getFragment();
}

注意subComponent使用要有 Scope

NewsScope.java

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface NewsScope {
}

NewListModule.java


@Module
public class NewsListModule {
    //通過從Activity中構建NewsComponent時傳入
    private List<NewsClassify> classifyList;

    public NewsListModule(List<NewsClassify> classifyList) {
        this.classifyList = classifyList;
    }

    @Provides
    @NewsScope
    public List<NewsClassify> provideClassifyList() {
        return classifyList;
    }

    @Provides
    @NewsScope
    List<NewsListFragment> getFragmentList(List<NewsClassify> classifyList, ApiService apiService) {
        List<NewsListFragment> fragmentList = new ArrayList<>();
        if (classifyList != null) {
            for (int i = 0; i < classifyList.size(); i++) {
                //手動建立Fragment,並設定 Presenter
                String id = classifyList.get(i).getAcId();
                NewsListFragment fragment = NewsListFragment.newInstance(id);
                fragment.setPresenter(new NewsListPresenter(apiService,id));
                fragmentList.add(fragment);
            }
        }
        return fragmentList;
    }
}

4. 在全域性的AppComponent中提供 NewsComponent

subComponent ( NewsComponent ) 就是在這個地方提供用於建立。

AppComponent.java

@Singleton
@Component(modules = {
        ApplicationModule.class, //application
        DBModule.class, //db
        ActivityBindingModule.class, //activity 註冊類
        AndroidSupportInjectionModule.class, //..
        RetrofitModule.class,    // 提供retrofit
        RepositoryModule.class, //倉庫相關的類
        ApiServiceModule.class // ApiService 提供類
})
public interface AppComponent extends AndroidInjector<BaseApplication> {

    @Component.Builder
    interface Builder {

        @BindsInstance
        AppComponent.Builder application(Application application);

        AppComponent build();
    }

    NewsComponent plus(NewsListModule newsListModule);

}

5. Activity中的呼叫方式

  • 網路獲取成功後根據獲取的 List <NewsClassify> newsClassifies 構建NewsListModule

  • 再呼叫newsComponent的getFragment就可以獲取到 List <Fragment> 了

AppComponent appComponent = BaseApplication.getInstance().getAppComponent();
NewsComponent newsComponent = appComponent.plus(new NewsListModule(newsClassifies));
mFragments.addAll(newsComponent.getFragment());
mAdapter.notifyDataSetChanged();