Dagger2进阶使用
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 注解的使用的注意事项。
- module 的 provide 方法使用了 Singleton ,那么 component 就必须使用同一个注解
- 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一下,很好报错了,这是为什么呢?下面我再告诉你们一些注意事项
- 没有@scope的component不能依赖有@scope的component
- 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的使用注意事项
- componet 的 inject 方法接收父类型参数,而调用时传入的是子类型对象则无法注入
- component关联的modules中不能有重复的provide
- module 的 provide 方法使用了 scope ,那么 component 就必须使用同一个注解
- module 的 provide 方法没有使用 scope ,那么 component 和 module 是否加注解都无关紧要,可以通过编译
- component的dependencies与component自身的scope不能相同,即组件之间的scope不同
- 没有scope的component不能依赖有scope的component
- @Singleton 的生命周期依附于component,同一个module provide singleton ,不同component 也是不一样