【Android 進階】Dagger 封裝與實戰演練

版權聲明:本文爲博主原創文章,轉載請註明出處。 https://blog.csdn.net/leaf_130/article/details/72813152

前言

A fast dependency injector for Android and Java. 一個在 Android 和 Java 平臺上使用的快速的依賴注入框架。 類似 java 開發中的 spring 框架,但使用難度比 spring 大一點點。 依賴注入框架主要用於模塊間解耦,提高代碼的健壯性和可維護性。

關於 degger2 的簡單介紹請看 degger2 入門例子 或者學習 菜鳥窩 官方推出的 Dagger2 從基礎到高級 教程。
本文不介紹基礎概念,主要介紹 Dagger2 的封裝使用。

介紹分包

一般我們習慣把 Dagger2 依賴注入相關的類放在 di 包下。
根據 dagger2 的風格,一般有 module 和 component 模塊
如下圖所示:

image

自定義 Application

我們都知道,自定義 Application 類,可以方便的設置初始化的工作,Gson 對象,DB 對象,單例的對象,開銷比較大但是只需要初始化一次的對象等等。
而使用 dagger2 實例化管理我們的類,還可以對生命週期進行管理,將顯得更加方便實用。
要在 Application 中使用依賴注入的對象,那麼 Application 就充當了 Dagger2 三個元素中的 Container 對象。

/**
 * Created by Veyron on 2017/5/9.
 * Function:在 AppApplication 使用依賴注入對象
 */
public class AppApplication extends Application {

    private AppComponent mAppComponent;
    public static AppApplication get(Context context){
        return (AppApplication)context.getApplicationContext();
    }

    public AppComponent getAppComponent(){

        return mAppComponent;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        mAppComponent= DaggerAppComponent.builder().appModule(new AppModule(this))
                .httpModule(new HttpModule()).build();
    }

定義 APP 級別的 AppModule

AppModule 提供最常用的對象,如 Gson,Application
單例對象,單例對象需要用 @Singleton 聲明。


@Module
public class AppModule {

    private Application mApplication;
    // Application 不能 new ,這裏通過構造方法傳遞過來
    public AppModule(Application application){

        this.mApplication = application;
    }

    @Provides
    @Singleton
    public Application provideApplication(){

        return  mApplication;
    }

    @Provides
    @Singleton
    public Gson provideGson(){

        return  new Gson();
    }
}

定義全局 AppComponent

引用 AppModule、HttpModule 兩個 Module .因爲裏面聲明的是單例對象,所以這裏也需要用 @Singleton 註釋。

@Singleton
@Component(modules = {AppModule.class, HttpModule.class})
public interface AppComponent {

    //最後加上這個
    public ApiService getApiService();
}

定義 Http 的 Module

提供 Http 操作相關的對象,這裏是三個個單例對象 OkHttpClient、Retrofit、ApiService

@Module
public class HttpModule {
    @Provides
    @Singleton
    public OkHttpClient provideOkHttpClient(){


        // log用攔截器
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

        // 開發模式記錄整個body,否則只記錄基本信息如返回200,http協議版本等
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);

        // 如果使用到HTTPS,我們需要創建SSLSocketFactory,並設置到client
//        SSLSocketFactory sslSocketFactory = null;

        return new OkHttpClient.Builder()
                // HeadInterceptor實現了Interceptor,用來往Request Header添加一些業務相關數據,如APP版本,token信息
//                .addInterceptor(new HeadInterceptor())
                .addInterceptor(logging)
                // 連接超時時間設置
                .connectTimeout(10, TimeUnit.SECONDS)
                // 讀取超時時間設置
                .readTimeout(10, TimeUnit.SECONDS)

                .build();
    }
    @Provides
    @Singleton
    public Retrofit provideRetrofit(OkHttpClient okHttpClient){

        Retrofit.Builder builder = new Retrofit.Builder()
                .baseUrl(ApiService.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(okHttpClient);

        return builder.build();
    }
    @Provides
    @Singleton
    public ApiService provideApiService(Retrofit retrofit){
        //這裏使用的 retrofit 是上面提供的
        return  retrofit.create(ApiService.class);
    }
}

其中 ApiService 如下

使用過 Retrofit 的都知道這個的作用哈,不懂的可以看我另一篇文章: Retrofit2 目前最優雅的網絡請求框架

public interface ApiService {
    public static final String BASE_URL = "http://112.124.22.238:8081/course_api/cniaoplay/";

    @GET("featured")
    public Call<PageBean<AppInfo>> getApps(@Query("p") String jsonParam);
}

實際代碼中使用 apiservice 對象

仔細看下面代碼就發現了一個神奇的現象:在使用 Dagger2 之前或者說在 HttpModule 沒有提供 ApiService 對象之前,需要先 new 出 HttpManager 對象,通過該 對象 獲得
ApiService 對象。而使用 Dagger2 在 HttpModule 中提供了 ApiService 對象之後,在這裏就可以直接使用了。當然,該 ApiService 對象是通過 構造函數傳過來的。

public class RecommendModel {

    private  ApiService mApiService;

    public RecommendModel(ApiService apiService){

        this.mApiService  =apiService;
    }

    public  void getApps(Callback<PageBean<AppInfo>> callback){

//       使用 Dagger2 之前
//        HttpManager manager = new HttpManager();
//
//        ApiService apiService =manager.getRetrofit(manager.getOkHttpClient()).create(ApiService.class);

       // 使用 Dagger2 之後,因爲 HttpModule 中已經提供了 ApiService 對象
        mApiService.getApps("{'page':0}").enqueue(callback);

    }

}

子 Component:RecommendComponent

需要自定義 Scope,因爲依賴的 AppComponent 爲單例,級別不能高過 singleton

inject(RecommendFragment fragment); 意思是向 RecommendFragment 中注入對象。

@FragmentScope
@Component(modules = RemmendModule.class,dependencies = AppComponent.class)
public interface RecommendComponent {

    void inject(RecommendFragment fragment);
}

自定義的 scope

照貓畫虎的自定義如下

@Scope
@Documented
@Retention(RUNTIME)
public @interface FragmentScope {
}

RemcomendModule

提供的對象有:RecommendContract.View (先從構造函數傳入)、RecommendModel、ProgressDialog。


@Module
public class RemmendModule {

    private RecommendContract.View mView;

    public RemmendModule(RecommendContract.View view){


        this.mView = view;
    }

    @Provides
    public RecommendContract.View provideView(){

        return mView;
    }

    @Provides
    public RecommendModel privodeModel(ApiService apiService){

        return  new  RecommendModel(apiService);
    }

    @Provides
    public ProgressDialog provideProgressDialog(RecommendContract.View view){

        return new ProgressDialog(((RecommendFragment)view).getActivity());
    }

}

BaseFragment

關於 Fragment、Activity 的封裝下一篇文章再詳細介紹,現在貼出來的 BaseFragment,關鍵點是封裝了一下重要的方法:setupAcitivtyComponent() 獲得 AppComponent 對象。

public  abstract  class BaseFragment<T extends BasePresenter> extends Fragment {
    private Unbinder mUnbinder;
    private AppApplication mApplication;
    private View mRootView;

    @Inject
    T mPresenter ;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

         mRootView = inflater.inflate(setLayout(), container, false);
         mUnbinder=  ButterKnife.bind(this, mRootView);

        return mRootView;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        this.mApplication = (AppApplication) getActivity().getApplication();
        setupAcitivtyComponent(mApplication.getAppComponent());

        init();
    }
    @Override
    public void onDestroy() {
        super.onDestroy();

        if(mUnbinder != Unbinder.EMPTY){
            mUnbinder.unbind();
        }
    }
    public abstract int setLayout();
    public abstract  void setupAcitivtyComponent(AppComponent appComponent);
    public abstract void  init();
}

實際 View 中使用 Dagger2 依賴注入

這裏只演示依賴注入 ProgressDialog 對象。

public class RecommendFragment extends BaseFragment<RecommendPresenter>  implements RecommendContract.View {

    @BindView(R.id.recycle_view)
    RecyclerView mRecyclerView;

    private RecomendAppAdatper mAdatper;

    @Inject
    ProgressDialog mProgressDialog;

    @Override
    public int setLayout() {
        return R.layout.fragment_recomend;
    }

    @Override
    public void setupAcitivtyComponent(AppComponent appComponent) {

        //Rebuild 一下,會根據 RecommendComponent 類生成 DaggerRecommendComponent 類
        DaggerRecommendComponent.builder().appComponent(appComponent)
                .remmendModule(new RemmendModule(this)).build().inject(this);
    }

    @Override
    public void init() {
        mPresenter.requestDatas();
    }

    private void initRecycleView(List<AppInfo> datas){

        //爲RecyclerView設置佈局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));


        //爲RecyclerView設置分割線(這個可以對DividerItemDecoration進行修改,自定義)
        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));

        //動畫
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());


        mAdatper = new RecomendAppAdatper(getActivity(),datas);

        mRecyclerView.setAdapter(mAdatper);

    }


    @Override
    public void showResult(List<AppInfo> datas) {
        initRecycleView( datas);
    }

    @Override
    public void showNodata() {

        Toast.makeText(getActivity(),"暫時無數據,請吃完飯再來",Toast.LENGTH_LONG).show();
    }

    @Override
    public void showError(String msg) {
        Toast.makeText(getActivity(),"服務器開小差了:"+msg,Toast.LENGTH_LONG).show();
    }

    @Override
    public void showLodading() {

        mProgressDialog.show();
    }

    @Override
    public void dimissLoading() {

        if(mProgressDialog.isShowing()){
            mProgressDialog.dismiss();
        }
    }
}

總結

代碼貼了很多,估計有的朋友都看暈了吧。其實思想可以簡單歸納會如下幾點:

  • 自定義 APP 級別的 AppModule:提供 Gson、Application 全局對象。

  • 自定義專職的 Module —- HttpModule:主要負責提供 HTTP 相關的對象,如:OkHttpClient、Retrofit、ApiService。

  • 自定義 App 級別的 AppComponent:關聯 modules:AppModule、HttpModule。聲明 ApiService getApiService() 抽象方法。

  • 自定義 AppApplication:依賴注入 App 級別的 AppComponent,方便程序的使用,在程序的任何地方都可以 獲得 AppComponent 對象。這樣意味着在整個程序任何地方都可以很方便的使用 AppComponent 所關聯的 AppModule 所提供的 Gson、Application 對象,以及 HttpModule 所提供的 OkHttpClient、Retrofit、ApiService 對象。

  • 自定義 Module —- RecommendModule:主要負責提供 RecommendFragment 中需要用到的一些對象:RecommendContract.View、RecommendModel、ProgressDialog。

  • 自定義 Component —- RecommendComponent:關聯 mopdules:RemmendModule、依賴 AppComponent。把對象注入到 RecommendFragment 中。這樣在 RecommendFragment 中就可以很方便的使用 RecommendModule 所提供的對象了。




歡迎關注我的微信公衆號:

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章