美文网首页
Dagger2进阶使用

Dagger2进阶使用

作者: notrynobug | 来源:发表于2017-02-15 20:58 被阅读0次

    1 知识回顾

    先来回顾之前将的四个注解

    • @Inject:
      通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。
    • @Module:
      Modules类里面的方法专门提供依赖,所以我们定义一个类,用@Module注解,这样Dagger在构造类的实例时候,就知道从哪里去找到需要的依赖。
    • @Provide:
      在Modules中,我们定义的方法用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。
    • @Component
      Components从根本上来说就是一个注入器,也可以说是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分。Components可以提供所有定义了的类型的实例。

    2 模块化

    就好比平时我们都需要对Http请求进行封装,在Dagger里呢,我们就要对Http进行模块化。 这里就举个例子,我们使用Retrofit网络请求框架,去请求天气Json数据。

    我们先去写一个HttpModule

    //HttpModule.java
    @Module
    public class HttpModule {
        @Singleton
        @Provides
        public OkHttpClient getOkHttpClient() {
            Log.i("http", "getOkHttpClient");
            return new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).readTimeout(10, TimeUnit.SECONDS).build();
        }
        @Provides
        @Singleton
        public Retrofit getRetrofit(OkHttpClient okHttpClient) {
            Log.i("http", "getRetrofit");
            return new Retrofit.Builder().addConverterFactory(GsonConverterFactory.create()).baseUrl("http://www.weather.com.cn/data/sk/").client(okHttpClient).          addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
        }
    }
    //HttpApi.java
    public interface HttpApi {
        //http://www.weather.com.cn/data/sk/101010100.html
        @GET("101010100.html")
        Call<WeatherBean> getweather();
    }
    

    有人肯定会问 @Singleton是什么意思,这个注释表示单例使用的意思。为什么我们要进行实例化呢?因为我们每次网络请求都要对OkHttpClient和Retrofit进行实例化,这样会造成资源的浪费,可以使单例模式下这是问程序的一种优化吧。 @Singleton的使用后面会讲。
    接着我们来看看Component的代码

    @Singleton
    @Component(modules = HttpModule.class)
    public interface ApplicationComponent {
        void inject(MainActivity mainActivity);
    }
    

    再来看看MainActivity中使用Dagger2

    public class MainActivity extends AppCompatActivity {
        String TAG = "MainActivity";
        TextView textView;
        @Inject
        Retrofit retrofit;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.tv);
            DaggerApplicationComponent.create().inject(this);
            retrofit.create(HttpApi.class).getweather().enqueue(new Callback<WeatherBean>() {
                @Override
                public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
                    Log.i("http", response.isSuccessful() + "" + response.body().getWeatherinfo().getCity());
                    textView.setText(response.body().getWeatherinfo().getCity());
                }
                @Override
                public void onFailure(Call<WeatherBean> call, Throwable t) {
                }
            });
        }
    }
    
    

    3 Singleton 单例讲解

    现在我们来讲解一下@Singleton 注解的使用的注意事项。

    1. module 的 provide 方法使用了 Singleton ,那么 component 就必须使用同一个注解
    2. Singleton 的生命周期依附于component,同一个module provide singleton ,不同component 也是不一样

    第一点也就是为什么我要在ApplicationComponent中去使用Singleton注解
    第二点大家可以自行去尝试。

    4 升级用法

    但是会有一个问题,如果我按照上面的这种方法去使用Http请求,还用上了单例模式,然而却体现不出单例模式的好处,按照上面的做法,我每个Activity使用 @Inject Retrofit retrofit;这个注解相对应的,我要对每个Activity创建对应的comoponent,这和单例有什么关系呢?
    所以我们要在上述代码上进行修改,进行改进。我们需要将HttpModule 使用的对象提高一个档次,不是面向Activity这个级别而是面向Application这个级别。Application和Activity显然不是一个级别,通过Application再结合单例模式,这才是HttpModule真正使用的用处。
    为此我们先来修改ApplicationComponent

    @Singleton
    @Component(modules = HttpModule.class)
    public interface ApplicationComponent {
    }
    

    看到这个大家可能会有疑问,为什么不是像上面那样
    void inject(Application application);?
    因为如果我们要使用 void inject(MainActivity mainActivity)的话,表示Component将该module和MainActivity 连接,若要和Application连接不需要写。
    然后我们就需要在Application中声明。

    public class Application extends android.app.Application {
        @Override
        public void onCreate() {
            super.onCreate();
             DaggerApplicationComponent.create();
        }
    }
    

    这样就完成将Httpmodule和Application连接。
    接下来我们还需要写一个Module类去返回HttpApi

    @Module
    public class ApiModule {
        @Provides
        HttpApi provideHttpapi(Retrofit retrofit) {
            return retrofit.create(HttpApi.class);
        }
    }
    

    但是Retrofit 这个对象在HttpModule中有返回,可是HttpModule已经和Application有连接了和ApiModule 没有任何关系。但是没关系,我们可以通过component去依赖(换句话说去继承可能会更好理解)

    @Component(modules = ApiModule.class, dependencies = ApplicationComponent.class)
    public interface HttpCompotent {
        void getactivity(MainActivity mainActivity);
    }
    

    然后在ApplicationComponent中加入一句话

    @Singleton
    @Component(modules = HttpModule.class)
    public interface ApplicationComponent {
        Retrofit retrofit();
    }
    

    添加这句话的目的是为了然会返回Retrofit ,以至于让ApiModule 的provideHttpapi方法能过找到Retrofit 。
    这个时候我们 Rebuild Project一下,很好报错了,这是为什么呢?下面我再告诉你们一些注意事项

    1. 没有@scope的component不能依赖有@scope的component
    2. component的dependencies与component自身的@scope不能相同,即组件之间的@scope不同

    对于突然看到@scope一定很眼生,@scope就是作用域的意思@Singleton是里面的一种。


    参照上面两点注意事项我们来修改HttpCompotent

    @Applicaton_Annotation
    @Component(modules = ApiModule.class, dependencies = ApplicationComponent.class)
    public interface HttpCompotent {
        void getactivity(MainActivity mainActivity);
    }
    
    //Applicaton_Annotation .java
    @Scope
    @Documented
    @Retention(RUNTIME)
    public @interface Applicaton_Annotation {
    }
    

    因为ApplicationComponent有依赖@scope也就是@Singleton,使用HttpCompotent 也需要依赖@scope,但又不能和ApplicationComponent一样,使用我们就参照@Singleton的格式复制了一份名叫@Applicaton_Annotation。
    这样Rebuild Project就不会报错了,说明Dagger注入成功。接下来我们来使用Dagger

    public class MainActivity extends AppCompatActivity {
        String TAG = "MainActivity";
        TextView textView;
        @Inject
        HttpApi httpApi;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = (TextView) findViewById(R.id.tv);
            DaggerHttpCompotent.builder()
                    .applicationComponent(((Application) getApplication()).getApplicationComponent()).build().getactivity(this);
    //因为HttpCompotent依赖了ApplicationCompotent,所以需要传入ApplicationCompotent对象,我们需要在Application里创建一个ApplicationCompotent对象
            httpApi.getweather().enqueue(new Callback<WeatherBean>() {
                @Override
                public void onResponse(Call<WeatherBean> call, Response<WeatherBean> response) {
                    Log.i("http", response.isSuccessful() + "" + response.body().getWeatherinfo().getCity());
                    textView.setText(response.body().getWeatherinfo().getCity());
                }
    
                @Override
                public void onFailure(Call<WeatherBean> call, Throwable t) {
                }
            });
        }
    }
    
    //Application .java
    public class Application extends android.app.Application {
        ApplicationComponent applicationComponent;
    
        public ApplicationComponent getApplicationComponent() {
            return applicationComponent;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            applicationComponent=   DaggerApplicationComponent.create();
        }
    }
    
    

    这样我们就完成了Dagger的更高级的用法,就是对Retrofit的单例使用,可以避免重复的实例Retrofit。
    最后我们来总结一下Dagger的使用注意事项

    1. componet 的 inject 方法接收父类型参数,而调用时传入的是子类型对象则无法注入
    2. component关联的modules中不能有重复的provide
    3. module 的 provide 方法使用了 scope ,那么 component 就必须使用同一个注解
    4. module 的 provide 方法没有使用 scope ,那么 component 和 module 是否加注解都无关紧要,可以通过编译
    5. component的dependencies与component自身的scope不能相同,即组件之间的scope不同
    6. 没有scope的component不能依赖有scope的component
    7. @Singleton 的生命周期依附于component,同一个module provide singleton ,不同component 也是不一样

    相关文章

      网友评论

          本文标题:Dagger2进阶使用

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