文章索引
dagger2 循序渐进学习(一)依赖注入基础知识(包会)
dagger2 循序渐进学习(二)
dagger2 循序渐进学习(三) 实例1,application中的应用
前两篇dagger2的文章介绍了其基本的使用方法,但其使用中还有很多重要的细节需要我们掌握,我认为在实例中学习比罗列一大堆的理论名称解释要来的实在。所以接下来每篇文章主要以一个马上能应用起来的小实例去解释一个个知识点,文章在精不在多,希望我写的文字能达到这样的效果吧!
dagger2 知识点
一、@Inject
先来说下上一篇文章dagger2 循序渐进学习(二) ,文中的activity
public class LoginActivity extends AppCompatActivity implements ILoginContract.ILoginView{
@Inject//这里加注解
LoginPresenter presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
}
@Override
public void loginok() {
}
@Override
public void loginErro() {
}
}
文中的presenter:
public class LoginPresenter implements ILoginContract.ILoginPresenter {
ILoginContract.ILoginView view;
@Inject//这里注解
public LoginPresenter(ILoginContract.ILoginView view) {
this.view = view;
}
@Override
public void login(String name, String pwd) {
}
}
有没有发现
presenter注入的类并没有遵循依赖倒置的原则,就是说没有依赖presenter的接口类,如果我们改成其接口类之后运行就会报错
那这是为什么呢,可以看出原因就在@Inject
依赖注入中第一个并且是最重要的就是 @Inject 注解。JSR-330标准中的一部分,标记那些应该被依赖注入框架提供的依赖。在Dagger 2中有3种不同的方式来提供依赖:
构造器注入,
@Inject标注在构造器上其实有两层意思。
①告诉Dagger2可以使用这个构造器构建对象。如 LoginPresenter 类构造方法上的@Inject
②注入构造器所需要的参数的依赖。 如 Pot 类,构造上的Rose会被注入。又如 LoginPresenter 类构造方法上的@Inject实现①的功能外,它本身还需要ILoginContract.ILoginView的依赖,此时的注入实际就在dagger2框架中 LoginModule中提供的,只是什么时候怎么提供的我们暂时不得而知
构造器注入的局限:如果有多个构造器,我们只能标注其中一个,无法标注多个。
属性注入
如 LoginActivity 类,标注在属性上。被标注的属性不能使用 private 修饰,否则无法注入。
属性注入也是Dagger2中使用最多的一个注入方式。
看到这里应该明白了LoginPresenter是如何注入到LoginActivity中的了:就是1在LoginActivity属性上标注@Inject ,2在LoginPresenter的构造方法上同时标注@Inject,编译过程中,dagger2就知道了LoginActivity的presenter属性需要注入,接下来就去寻找哪里可以提供,其中一项就是就是找有没有相应类的构造器用@Inject标注的,如果有就用这个构造器构造后注入activity。所以当我们把LoginActivity中的属性改成ILoginPresenter接口类型的时候,dagger2就找不到了他的构造器了,所以就报错了。
方法注入
标注在public方法上,Dagger2会在构造器执行之后立即调用这个方法。
方法注入和属性注入基本上没有区别, 那么什么时候应该使用方法注入呢?
比如该依赖需要this对象的时候,使用方法注入可以提供安全的this对象,因为方法注入是在构造器之后执行的。
比如google mvp dagger2中,给View设置Presenter的时候可以这样使用方法注入。
/**
* Method injection is used here to safely reference {@code this} after the object is created.
* For more information, see Java Concurrency in Practice.
*/
@Inject
void setupListeners() {
mTasksView.setPresenter(this);
}
二、@Component
@Inject 注解是JSR-330中定义的注解,在 javax.inject 包中。
这个注解本身并没有作用,它需要依赖于注入框架才具有意义,用来标记需要被注入框架注入的方法,属性,构造。
而Dagger2则是用 Component 来完成依赖注入的, @Component 可以说是Dagger2中最重要的一个注解。
@Componentpublic interface MainActivityComponent { void inject(MainActivity activity);}
以上是定义一个Component的方式。使用接口定义,并且 @Component 注解。
命名方式推荐为: 目标类名+Component ,在编译后Dagger2就会为我们生成 DaggerXXXComponent 这个类,它是我们定义的 xxxComponent 的实现,在目标类中使用它就可以实现依赖注入了。
Component中一般使用两种方式定义方法。
void inject(目标类 obj); Dagger2会从目标类开始查找@Inject注解,自动生成依赖注入的代码,调用inject可完成依赖的注入。
Object getObj(); 如: LoginPresenter getLoginPresentert();Dagger2会到LoginPresenter类中找被@Inject注解标注的构造器,自动生成提供LoginPresenter依赖的代码,这种方式一般为其他Component提供依赖。(一个Component可以依赖另一个Component,后面会说)
Component和Inject的关系如下:
Dagger2框架以Component中定义的方法作为入口,到目标类中寻找JSR-330定义的@Inject标注,生成一系列提供依赖的Factory类和注入依赖的Injector类。
而Component则是联系Factory和Injector,最终完成依赖的注入。
三、@Module和@Provides
使用@Inject标记构造器提供依赖是有局限性的,比如说我们需要注入的对象是第三方库提供的,我们无法在第三方库的构造器上加上@Inject注解。
或者,我们使用依赖倒置的时候,因为需要注入的对象是抽象的,@Inject也无法使用,因为抽象的类并不能实例化比如咱们需要ILoginPresenter的接口类的依赖,会出现文中最开始的那个错误;
这个时候就需要Module了。
先清除LoginPresenter中的@Inject
并把LoginAcitivity中的依赖改成接口类型
@Module标记在LoginModule类上面,@Provodes标记在方法上,表示可以通过这个方法获取依赖。
在@Component中指定Module
这样就完了,ok了。测测,没有问题;
@Module和@Provides的作用:
@Module需要和@Provide是需要一起使用的时候才具有作用的,并且@Component也需要指定了该Module的时候。
@Module是告诉Component,可以从这里获取依赖对象。Component就会去找被@Provide标注的方法,相当于构造器的@Inject,可以提供依赖。
还有一点要说的是,@Component可以指定多个@Module的,如果需要提供多个依赖的话。
并且Component也可以依赖其它Component存在。
如此便解决上面提到的问题;回顾一下这四个最重要的注解的用法。接下来我们研究个小实例。
实例
这个实例就是我们经常要实现的application中retrofit的应用。为了节省资源提高性能,要求我们在使用retrofit时候,整个app中retrofit是单例的,然后再用他create相应的api的接口类实例以往我们经常会写一大堆代码和模式来实现其单例,在dagger中这一切变得简单。下面开始撸代码:
相关的第三方库的依赖就不写了;
api的请求接口是这样婶儿滴
public interface ApiService {
@GET("/users/{user}/repos")
Observable<ArrayList<String>> getRepoData(@Path("user") String user);
}
再上个module
@Module
public class ServiceModule {
@Provides
public OkHttpClient provideOkHttpClient() {
OkHttpClient okHttpClient ;
OkHttpClient.Builder builder= new OkHttpClient.Builder();
okHttpClient=builder.readTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.build();
return okHttpClient;
}
@Provides
public Retrofit provideRetrofit(Application application, OkHttpClient okHttpClient){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(application.getString(R.string.api_host))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()) // 添加Rx适配器
.addConverterFactory(GsonConverterFactory.create()) // 添加Gson转换器
.client(okHttpClient)
.build();
return retrofit;
}
@Singleton
@Provides
protected ApiService provideGitHubService(Retrofit retrofit) {
return retrofit.create(ApiService.class);
}
}
再来个module
@Module
public class AppModule {
private final Application application;
public AppModule(Application application) {
this.application = application;
}
@Provides
public Application provideApplication() {
return application;
}
}
那么再来看我们的application类中
public class App extends Application {
private AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent=
DaggerAppComponent.builder()
.appModule(new AppModule(this))
.serviceModule(new ServiceModule())
.build();
}
public AppComponent getAppComponent() {
return appComponent;
}
}
眼尖的同学看到了@Singleton,这个先不说,其他的不用我多说,相信前面都看懂了的同学,这几个类完全能看懂。
@Singleton顾名思义,就是单例啊,但是这个单例可不是怎么用都是单例。通过测试,我们发现当重新build一个Component的时候,这个单例也是新的,所以准确的说 ,它的作用只是保证依赖在@Component中是唯一的,可以理解为“局部单例”。所以在application中实例化Component,其他地方用到它的时候就需要先在application获取appComponent进行注操作。
通过这个方法,当然如果也可以把他定义为static方法
public AppComponent getAppComponent() {
return appComponent;
}
接下来我们遵循循序渐进的原则dagger2还有部分知识点会在后面的文章继续和大家分享,相信通过循序渐进的边学边实践的方式,学习起来更扎实,不像一口吞个胖子样!!