Dagger2 封装与实战演练

作者: 菜鸟窝 | 来源:发表于2017-07-18 14:54 被阅读173次
    0.jpg

    本文为菜鸟窝作者 吴威龙 的连载

    菜鸟窝是专业的程序猿在线学习平台,提供最系统的 Android 项目实战课程

    如需转载,请联系菜鸟窝公众号(cniao5),并注明出处。

    [toc]

    前言

    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 所提供的对象了。


    有图有真相
    关注菜鸟窝官网免费领取140套开源项目
    菜鸟窝官网公号二维码.png
    扫码进菜鸟手机助手—专属Appstore学习群,
    与大咖交流
    手机助手.png

    相关文章

      网友评论

        本文标题:Dagger2 封装与实战演练

        本文链接:https://www.haomeiwen.com/subject/zshhkxtx.html