系列文章目录
前言
上一篇文章介绍了如何使用IOC和依赖注入,以及如何使用Dagger2注入对象,但是只能为项目中创建的类提供依赖注入。那么问题来了,我们在做项目开发时,经常需要引入第三方框架,那么我们想要使用Dagger2注入第三方框架的对象时应该怎么做呢?我们如何使用Dagger2去实现单例呢?本篇文章会给你答案。
一、通过Dagger2注入第三方框架的对象
当我们的项目中存在类似于Retrofit、OkHttp等第三方框架时,我们就不能像上一章那样直接提供一个方法,返回一个新建的对象。我们需要使用Dagger2提供的@Provides注解提供对象的方法,然后就可以使用@Inject来注解生成对象了。我们继续使用上篇文章的demo,下面是具体的步骤:
为了演示,引入Dagger2和Retrofit的依赖:
(1)引入库
//引如Dagger2
implementation('com.google.dagger:dagger:2.42')
annotationProcessor('com.google.dagger:dagger-compiler:2.42')
//为了演示创建第三方框架中的对象引入
implementation('com.squareup.retrofit2:retrofit:2.9.0')
implementation('com.squareup.retrofit2:converter-gson:2.9.0')
implementation('com.squareup.retrofit2:converter-scalars:2.9.0')
(2)创建一个类NetModule,用@Module注解,然后定义生成对象的方法,用@Provides注解
代码如下:
@Module
public class NetModule {
//项目中的类不用@Provides
public User provideUser(){
return new User();
}
//告知dagger可以通过该方法来获取到要注入对象的实例
//@MyScope
@Provides
public Retrofit provideRetrofit(){
return new Retrofit.Builder()
.baseUrl("http://www.baidu.com")
.build();
}
//此处的retrofit就是provideRetrofit()提供的,它会优先在当前的module
//中查找已经存在的实例,然后使用
//@MyScope
@Provides
public ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
//@MyScope
@Provides
public OkHttpClient provideOkHttpClient(){
return new OkHttpClient.Builder().build();
}
}
(3)创建一个ApplicationComponent接口,使用@Component(modules = NetModule.class)注解,让其与NetModule建立联系,然后在这个接口中,定义注入的方法。
代码如下:
@Component(modules = NetModule.class)
public interface ApplicationComponent {
void inject(MainActivity mainActivity);
}
(4)使用Dagger2注解生成对象
public class MainActivity extends AppCompatActivity {
@Inject
Retrofit mRetrofit;
@Inject
ApiService mApiService;
@Inject
OkHttpClient mOkHttpClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//执行注入动作
DaggerApplicationComponent.create().inject(this);
Log.d("zhongxj:", "mRetrofit: " + mRetrofit.toString());
Log.d("zhongxj:", "mApiService: " + mApiService.toString());
Log.d("zhongxj:", "mOkHttpClient: " + mOkHttpClient.toString());
}
(5)运行结果:
2022-08-08 21:34:18.992 27109-27109/com.loveyoung.dagger2hilt D/zhongxj:: mRetrofit: retrofit2.Retrofit@ae8223f
2022-08-08 21:34:18.992 27109-27109/com.loveyoung.dagger2hilt D/zhongxj:: mApiService: retrofit2.Retrofit$1@afff70c
2022-08-08 21:34:18.992 27109-27109/com.loveyoung.dagger2hilt D/zhongxj:: mOkHttpClient: okhttp3.OkHttpClient@b8a0855
根据上面的步骤,就可以通过@Inject注解我们想要生成的对象,然后直接使用啦。
二、使用Dagger2实现单例
单例顾名思义思意就是程序中只有一个对象,不会创建多个对象,因为有些对象创建的过程比较耗时,就比如Okhttp的对象创建时就比较耗性能,我们看下Okhttp创建OkhttpClient对象的构造函数:
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
this.eventListenerFactory = builder.eventListenerFactory;
this.proxySelector = builder.proxySelector;
this.cookieJar = builder.cookieJar;
this.cache = builder.cache;
this.internalCache = builder.internalCache;
this.socketFactory = builder.socketFactory;
boolean isTLS = false;
for (ConnectionSpec spec : connectionSpecs) {
isTLS = isTLS || spec.isTls();
}
if (builder.sslSocketFactory != null || !isTLS) {
this.sslSocketFactory = builder.sslSocketFactory;
this.certificateChainCleaner = builder.certificateChainCleaner;
} else {
X509TrustManager trustManager = Util.platformTrustManager();
this.sslSocketFactory = newSslSocketFactory(trustManager);
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
}
if (sslSocketFactory != null) {
Platform.get().configureSslSocketFactory(sslSocketFactory);
}
this.hostnameVerifier = builder.hostnameVerifier;
this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
certificateChainCleaner);
this.proxyAuthenticator = builder.proxyAuthenticator;
this.authenticator = builder.authenticator;
this.connectionPool = builder.connectionPool;
this.dns = builder.dns;
this.followSslRedirects = builder.followSslRedirects;
this.followRedirects = builder.followRedirects;
this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
this.callTimeout = builder.callTimeout;
this.connectTimeout = builder.connectTimeout;
this.readTimeout = builder.readTimeout;
this.writeTimeout = builder.writeTimeout;
this.pingInterval = builder.pingInterval;
if (interceptors.contains(null)) {
throw new IllegalStateException("Null interceptor: " + interceptors);
}
if (networkInterceptors.contains(null)) {
throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
}
}
创建这样一个对象可以看出是很耗性能的。所以Dagger2提供了实现单例的方法,主要有两种方法。先不说这两种方法,先介绍Dagger的作用域。作用域就是将某个对象的生命周期限定为其组件的生命周期。咱们可以对比局部变量和全局变量的作用域理解,一个类中,我们定义的全局变量可以在这个类中使用,这时它的作用域就是这个类的范围。如果我们在类中定义一个方法,在方法中定义一个局部变量,这个局部变量只能在这个方法内使用,这时他们的作用域就是这个方法范围。所以由此引出了全局单例和局部单例。下面就看下如何使用Dagger2如何实现局部单例。
方法一:使用 @Singleton想要生命成单例的provideXXX()方法,也要注解ApplicationComponent的接口,否则会编译不通过
(1)使用@Singleton注解要声明成单例的对象提供方法
代码:
@Module
public class NetModule {
@Singleton
@Provides
public ApiService provideApiService(Retrofit retrofit){
return retrofit.create(ApiService.class);
}
}
(2)在ApplicationComponent上也使用@Singleton注解,必须加上,否则编译不通过
代码:
@Singleton
@Component(modules = NetModule.class)
public interface ApplicationComponent {
void inject(MainActivity mainActivity);
}
(3)使用单例:
代码:
public class MainActivity extends AppCompatActivity {
@Inject
ApiService mApiService;
@Inject
ApiService mApiService1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//执行注入动作
DaggerApplicationComponent.create().inject(this);
Log.d("zhongxj:", "mApiService: " + mApiService.toString());
Log.d("zhongxj:", "mApiService1: " + mApiService1.toString());
}
运行结果
2022-08-09 13:17:38.861 24934-24934/com.loveyoung.dagger2hilt D/zhongxj:: mApiService: retrofit2.Retrofit$1@ae8223f
2022-08-09 13:17:38.861 24934-24934/com.loveyoung.dagger2hilt D/zhongxj:: mApiService1: retrofit2.Retrofit$1@ae8223f
我们可以看到结果中mApiService和mApiService1的对象是同一个,这里是局部单例子,也就是说这里的单例作用域范围就是当前的类,如果此时再创建一个类,注解生成新的mApiService对象,我们会发现和和咱们开始定义类中的对象不是同一个。读者可以去尝试下。
进入@Singleton注解声明的地方我们可以看到这个地方是一个标识注解。
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
所以我们也可以参照这个@Singleton自己定义一个注解来完成单例。
如下所示
//自定义作用域
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface MyScope {
}
然后使用MyScope去替换咱们上面的@Singleton注解后就可以了,结果是一样的
这里实现的是局部单例子,这时有人可能会问,我要实现全局单例怎么做呢,其实很简单,咱们提升作用域就可以啦,我们直接在Application中去获取ApplicationComponent的实现类的对象,在Activity中获取这个对象执行注入动作就可以啦
代码如下:
public class MyApplication extends Application {
private static ApplicationComponent mApplicationComponent = DaggerApplicationComponent.create();
public static ApplicationComponent getApplicationComponent(){
return mApplicationComponent;
}
}
使用示例
MyApplication.getApplicationComponent().inject(this);
这里的this指的是当前的Activity,需要注意的是,如果我们要在新的Activity使用依赖注入,还需在ApplicationComponent的接口中定义注入的方法:
假如咱们要在MainActivity2中使用依赖注入,需要定义注入的方法,如下所示:
@Component(modules = NetModule.class)
public interface ApplicationComponent {
void inject(MainActivity mainActivity);
void inject(MainActivity2 mainActivity2);
}
然后就可以使用啦。
总结
以上就是今天要讲的内容,看完这两篇博客,相信读者已经可以使用Dagger2的依赖注入去实现注入本项目中类的对象以及第三方框架中类的对象了。并且可以利用Dagger2的作用域实现单例,但是目前我们发现所有的这些注入动作都是在ApplicationComponent接口中去定义注入动作,即所有的依赖注入的配置都是一套,这样的话会显得比较不合理,因为我们可能有的情景我们使用全局单例,有的使用局部单例。这时候使用咱们一套组件就无法完成需求了,所以引出了组件依赖,主要是为了解决不同作用域组件之间的服用问题。下篇咱们会讲,今天先写这么多。敬请期待