IOC (Inversion of Control) 控制反转,熟悉Java后台开发一定知道Spring就是基于IOC/AOP技术设计的,那么在Android中的Dagger2是基于IOC来实现的。IOC技术说白了就是将对象的创建和获取交给Dagger2(特定的类)进行创建并且将创建的对象存储到容器中。 需要注意Hilt和Dagger2的区别,其实Hilt就是封装了Dagger2,使用过Dagger2的同学肯定清楚Dagger2使用起来特别繁琐。hilt恰恰简化了这些繁琐的操作。
下面先学习IOC注入的原理以及Dagger2再学习Hilt.
什么是IOC注入
在开始学习Dagger2之前,必须先搞懂什么是IOC技术,IOC是控制反转。
通俗来讲:一般创建一个对象需要手动new
来创建,然后拿到对象的实例进行操作,那么IOC将这些操作进行了反转,创建对象和获取对象实例由IOC容器帮你完成,不需要手动去创建。
/**
* 在没有dagger之前 我们是这样写代码的
*/
fun test(){
//项目中的常用操作:网络请求和数据库操作
val httpObject = HttpObject()
val databaseObj = DatabaseObj()
//发送post请求
httpObject.post()
//从数据库获取数据
databaseObj.getData()
}
Dagger2使用及原理
Dagger2的使用及API
dagger2的使用:
- 通过Module提供对象
- 将module放到Component
- 编译通过生产的类注入到目标类中
引入dagger2依赖:
implementation 'com.google.dagger:dagger:2.36'
kapt 'com.google.dagger:dagger-compiler:2.36'
创建module,提供对象的实例化 可以根据业务创建多个module ```kotlin /**
- Module 用于提供对象
- @Author sufulu
- @Date 2021/5/27-16:51
- @Email sufululove@gmail.com */ @Module class HttpModule { //提供HttpObject对象实例 @Provides fun httpProvider():HttpObject{ return HttpObject() } }
@Module class DataModule { @Provides fun dataProvider():DatabaseObj{ return DatabaseObj() } }
3. 创建组件存放Module, 注意:一个组件可以存放多个Module,但是一个类只能有一个组件及其依赖组件注入对象(后续会讲到)。
```kotlin
@Component(modules = [HttpModule::class,DataModule::class])
interface MyComponent {
/**
* 讲对象注入到MainActivity 这里参数不能用多态
*/
fun injectMainActivity(activity:MainActivity)
}
- build编译项目工程生成如下代码
(之前发现很多人都会吐槽,会影响编译速度,没错确实会影响编译速度,但是更背后的原因不知道大家有没有思考过)
思考:在Android中的IOC和Java后台的IOC是不一样的,Java后台是服务器它在启动服务器的时候慢慢进行初始化和编译、反射等。但是Android不一样,Android必须提前编译好,为什么呢?因为如果在Android App启动的时候进行编译,项目量很大的情况下启动会非常慢,这是不能接受的。这也是为什么Android 采用了大量的APT技术,在项目bulid的时候去提前生成好代码,打包到apk中,那么在App启动的时候就可以直接使用了。其实很多第三方框架ARouter、Dagger2、ViewBinding、DataBinding等等都会采用APT技术在编译时扫描注解,生成class文件,这些框架的诞生都是有原因的。作为一名工程师我们必须学会思考去学习优秀的代码设计和背后的思想,这才是最重要的。(少一些吐槽,多一些思考) APT 实战
生成如下的代码:一定要注意两个方法,他是实现IOC的关键,一个是injectMainActivity
和 build
方法
public final class DaggerMyComponent implements MyComponent {
private final HttpModule httpModule;
private final DaggerMyComponent myComponent = this;
private DaggerMyComponent(HttpModule httpModuleParam) {
this.httpModule = httpModuleParam;
}
public static Builder builder() {
return new Builder();
}
public static MyComponent create() {
return new Builder().build();
}
@Override
public void injectMainActivity(MainActivity activity) {
//注意:这个方法是空的 这个方法是实现注入的关键地方
}
public static final class Builder {
private HttpModule httpModule;
private Builder() {
}
public Builder httpModule(HttpModule httpModule) {
this.httpModule = Preconditions.checkNotNull(httpModule);
return this;
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://dagger.dev/unused-modules.
*/
@Deprecated
public Builder dataModule(DataModule dataModule) {
Preconditions.checkNotNull(dataModule);
return this;
}
//通过build创建module和component
public MyComponent build() {
if (httpModule == null) {
this.httpModule = new HttpModule();
}
return new DaggerMyComponent(httpModule);
}
}
}
通过Factory获取module中的对象实例,可以看到httpProvider调用了Module的instance.httpProvider
正是我们提供对象实例的方法
public final class HttpModule_HttpProviderFactory implements Factory<HttpObject> {
private final HttpModule module;
public HttpModule_HttpProviderFactory(HttpModule module) {
this.module = module;
}
//get 获取对象
@Override
public HttpObject get() {
return httpProvider(module);
}
public static HttpModule_HttpProviderFactory create(HttpModule module) {
return new HttpModule_HttpProviderFactory(module);
}
public static HttpObject httpProvider(HttpModule instance) {
return Preconditions.checkNotNullFromProvides(instance.httpProvider());
}
}
在上述代码,生成了一个
DaggerMyComponent
类和module
工厂类,通过生成的DaggerMyComponent
将对象注入到MainActivity
通过@Inject
注解给对象的实例赋值class MainActivity : AppCompatActivity() {
/**
* 注入对象
* 注意kotlin 注入对象要使用lateinit 转换为Java public,否则默认是private不能注入
*/
@Inject
lateinit var httpObj:HttpObject
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//自动生成的Component
DaggerMyComponent.create() //create方法
.injectMainActivity(this)
//或者通过如下方法 这样的好处就是如果module的构造函数需要传递某些参数
DaggerMyComponent.builder().httpModule(HttpModule())
.dataModule(DataModule())
.build() //初始化了module和component
.injectMainActivity(this)//
}
}
注入对象:
在我们运行项目,进行编译的时候,扫描@Inject
注解如下:又会生成如下的class文件,当调用component的injectMainactivity
明显直接可以看到injectHttpObj()
正是将MainActivity.httpObject = httpObj
将之前编译生成的httpObject
对象实例赋值给了MainActivity
的httpObject
属性,实现了对象注入。
在观察Comonent的变化,在injectMainActivity多了如下代码:
@Override
public void injectMainActivity(MainActivity activity) {
injectMainActivity2(activity);
}
private MainActivity injectMainActivity2(MainActivity instance) {
MainActivity_MembersInjector.injectHttpObj(instance, HttpModule_HttpProviderFactory.httpProvider(httpModule));
return instance;
}
//生成如下class
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<HttpObject> httpObjProvider;
public MainActivity_MembersInjector(Provider<HttpObject> httpObjProvider) {
this.httpObjProvider = httpObjProvider;
}
public static MembersInjector<MainActivity> create(Provider<HttpObject> httpObjProvider) {
return new MainActivity_MembersInjector(httpObjProvider);
}
@Override
public void injectMembers(MainActivity instance) {
injectHttpObj(instance, httpObjProvider.get());
}
@InjectedFieldSignature("com.example.daggerdemo.MainActivity.httpObj")
public static void injectHttpObj(MainActivity instance, HttpObject httpObj) {
instance.httpObj = httpObj;
}
}
ok,上述就是一个完整的Dagger2的IOC的实现代码。可以看到,Dagger2实现原理非常简单,它其实就是通过APT技术扫描注解,生成相应的class文件,然后直接对属性进行赋值。这样一想就会简单很多,Dagger
设置为单例模式获取对象:
局部单例:代码如下
public final class DaggerMyComponent implements MyComponent {
private final DaggerMyComponent myComponent = this;
private Provider<HttpObject> httpProvider;
private DaggerMyComponent(HttpModule httpModuleParam) {
initialize(httpModuleParam);
}
public static Builder builder() {
return new Builder();
}
public static MyComponent create() {
return new Builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final HttpModule httpModuleParam) {
this.httpProvider = DoubleCheck.provider(HttpModule_HttpProviderFactory.create(httpModuleParam));
}
@Override
public void injectMainActivity(MainActivity activity) {
injectMainActivity3(activity);
}
@Override
public void injectMainActivity2(MainActivity2 activity) {
injectMainActivity22(activity);
}
private MainActivity injectMainActivity3(MainActivity instance) {
MainActivity_MembersInjector.injectHttpObj(instance, httpProvider.get());
MainActivity_MembersInjector.injectHttpObj2(instance, httpProvider.get());
return instance;
}
private MainActivity2 injectMainActivity22(MainActivity2 instance) {
MainActivity2_MembersInjector.injectHttpObj(instance, httpProvider.get());
MainActivity2_MembersInjector.injectHttpObj2(instance, httpProvider.get());
return instance;
}
}
通过DoubleCheck:
private static final Object UNINITIALIZED = new Object();
private volatile Provider<T> provider;
private volatile Object instance = UNINITIALIZED;
public static <P extends Provider<T>, T> Provider<T> provider(P delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
/* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
* binding, we shouldn't cache the value again. */
return delegate;
}
return new DoubleCheck<T>(delegate);
}
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();//只执行一次
instance = reentrantCheck(instance, result);
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
为什么换了一个Activity就不一样了呢?因为对象变了Component重新创建了。
如果要实现全局的单例的Component就需要在Application中进行创建。
class App : Application(){
var myComponent:MyComponent? = null
override fun onCreate() {
super.onCreate()
//app级别的组件
myComponent = DaggerMyComponent
.builder()
.httpModule(HttpModule())
.dataModule(DataModule())
.build()
}
fun getAppComponent():MyComponent?{
return myComponent
}
}
Compoent 组合在一起使用:
@Component(modules = [PresenterModule::class])
interface PresenterComponent {
/**
* 同一个类 只能注入一个Component,不能在这样写了
*/
// fun inject(activity: MainActivity)
/**
* 返回presenter对象,依赖到其他的Component中 但是该component设置为单例模式就会有问题了
*/
fun providerPresenter():Presenter
}
//通过dependencies依赖其他的component
@Singleton
@Component(modules = [HttpModule::class,DataModule::class],
dependencies = [PresenterComponent::class]//依赖其他的component 多个component组合在一起
)
interface MyComponent {
/**
* 讲对象注入到MainActivity 这里参数不能用多态
*/
fun injectMainActivity(activity:MainActivity);
fun injectMainActivity2(activity:MainActivity2);
fun injectApplication(application: Application)
}
初始化代码如下,编译项目就会看到一个presenterComponent
方法,该方法用于初始化依赖的组件component:
class App : Application(){
var myComponent:MyComponent? = null
override fun onCreate() {
super.onCreate()
//app级别的组件
myComponent = DaggerMyComponent
.builder()
.httpModule(HttpModule())
.dataModule(DataModule())
.presenterComponent(DaggerPresenterComponent.create()) //将其他组件拼接在一起 因为一个类只能有一个component注入所以如果有多个component就需要添加依赖
.build()
}
fun getAppComponent():MyComponent?{
return myComponent
}
}
但是问题又出现了,MyComponent是一个单例但是如果将PresenterComponent也设置为单例就会报错
This @Singleton component cannot depend on scoped components:
@dagger.Component(modules = {com.example.daggerdemo.module.HttpModule.class, com.example.daggerdemo.module.DataModule.class}, dependencies = {com.example.daggerdemo.di.component.PresenterComponent.class})
^
@Singleton com.example.daggerdemo.di.component.PresenterComponent
这段话的意思是说:这个单例的组件不能依赖于作用域组件
因为PresenterComponent
组件依赖与MyComponent
组件,我们需要改变PresenterComponent
的作用域。
使用自定义的Scope方式进行设置,如下实现方式非常简单:创建一个注解类,一定要有标识@Scope
注解
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class AppScope()
/**
* 用户级别的作用域
* @Author sufulu
* @Date 2021/5/28-11:26
* @Email sufululove@gmail.com
*/
@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class UserScope()
注意
- 多个
component
上面的scope不能相同 - 没有
scope
的组件不能去依赖有scope
的组件
@Subcomponent
的使用