开源框架----IOC注入技术----Dagger2
2021-08-03 本文已影响0人
初夏的雪
一、什么是IOC注入?
IOC ( Inversion of Control ) :
原来是由程序代码中主动获取的资源,现在变成由第三方获取并使用原来的代码被动接受的方式,从而达到解耦的效果,也就是控制反转。
通俗易懂:
**之前我们将两个对象A与B产生关联,是在一个对象A中去new另外一个对象B,而IOC技术则是当对象A需要对象B时,IOC容器就会给A提供一个B对象,而B对象的创建和销毁全部交给IOC去处理,对象A就完全不用关心这些。**
IOC注入分三种:
1) 运行时注入:eventBus 等
2)源码是注入: android studio插件
3)编译期注入:butterknife ,dagger2
二、Dagger2 的介绍
Dagger2 是Java和Android的依赖注入的编译期框架。Dagger完成注入有三个角色:
1)生产者(Module): 主要负责对象的生产
2)桥梁(Component):将生产的对象与消费者进行对应关联
3)消费者(Inject):对象的使用
三、Dagger2 的基本使用
1、添加Dagger2的依赖
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-complier:2.4'
2、提供对象的@Module
- @Module注解负责给我们提供需要的 class 对象;这里可以提供的对象,我们在Inject的时候都可以取到;
- 在提供对象的方法上使用@Provides注解;
@Module
public class HttpModule {
@Provides
public HttpModule providerHttpModule(){
return new HttpModule();
}
}
@Module
public class DatabaseModule {
@Provides
public DatabaseModule providerDatabaseModule(){
return new DatabaseModule();
}
}
3、@Component组件
- 组件必须是interface ;
- 在注解中需要指明modules的类型有哪些,多个类型时用逗号隔开;
- Component 是一个接口,该接口方法中的参数是不能使用多态的,只能明确是哪一个类;例如MainActivity ,不能使用Activity来替换;
@Component(modules = {DatabaseModule.class, HttpModule.class})
public interface CustomComponent {
//此处的参数是不能多态的,即不能用Activity来偷懒,必须是明确的类,例如MainActivity
void injectMainActivity(MainActivity mainActivity);
}
以上当个Component的情况比较简单,但实际情况中,会出现一个Activity需要在多个Component上inject ,那么有下面的两种方式处理:
1)dependencies 依赖
//定义一个类型
public class Student {
}
//定义studentModule
@Module
public class StudentModule {
@Provides
Student providerStudent(){
return new Student();
}
}
//定义StudentComponent
@Component (modules = {StudentModule.class})
public interface StudentComponent {
// void inject(MainActivity mainActivity);//此处因为MainActivity已经在上面的CustomComponent中inject, 这里不能再次inject
Student getStudent();
}
//接下来就是在CustomComponent中添加StudentComponent依赖
Component(modules = {DatabaseModule.class, HttpModule.class},
dependencies = {StudentComponent.class})
public interface CustomComponent {
void injectMainActivity(MainActivity mainActivity);
void injectSecondActivity(SecondActivity secondActivity);
}
//在MainActivty中使用
public class MainActivity extends AppCompatActivity {
@Inject
HttpModule httpModule;
@Inject
HttpModule httpModule2;
@Inject
DatabaseModule databaseModule;
@Inject
Student student;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerCustomComponent.builder()
.studentComponent(DaggerStudentComponent.create())//这里添加了依赖后编译才会出现
.build()
.injectMainActivity(this);
Log.e("MainActivity", "MainActivity onCreate: 1----"+httpModule.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 2----"+httpModule2.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 3----"+databaseModule.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 4----"+student.hashCode());
}
}
2)subcomponent 子组件
在使用subComponent时,由从组件(子组件)负责与Activity的inject ,而主组件(父组件)负责提供获取子组件的对象;
2.1) 先来定义parentComponet:
- 使用subComponent时,ParentComponent中定义获取ChildComponent对象的接口;**
- ParentComponent和普通的Component 就一样了;
public class Parent {
}
@Module
public class ParentModule {
@Provides
Parent getParent(){
return new Parent();
}
}
@Component (modules = {ParentModule.class})
public interface ParentComponent {
ChildComponent getChildComponent();
}
2.2) 定义ChildComponent:
- 使用@SubComponent注解来定义子组件接口
- ChildComponent中提供inject的方法;
public class Child {
}
@Module
public class ChildModule {
@Provides
Child getChild(){
return new Child();
}
@Provides
ChildModule getChildModule(){
return new ChildModule();
}
}
@Subcomponent(modules = {ChildModule.class})
public interface ChildComponent {
void injectThirdActivity(ThirdActivity activity);
}
定义好了上面的组从组建后,就可以使用了:(这里需要注意的是在inject的时候,需要调用childComponet的inject方法来进行)
public class ThirdActivity extends AppCompatActivity {
@Inject
Child child;
@Inject
ChildModule childModule;
@Inject
Parent parent;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
DaggerParentComponent.create().getChildComponent().injectThirdActivity(this);
Log.e("ThirdActivity", "ThirdActivity onCreate: 1----" + child.hashCode());
Log.e("ThirdActivity", "ThirdActivity onCreate: 2----" + childModule.hashCode());
Log.e("ThirdActivity", "ThirdActivity onCreate: 3----" + parent.hashCode());
}
}
到此为止,相信对Module 、Component 他们之间的关系已经有所了解:Module就是我们的内容提供者,或者说就对象的提供者,他负责创建对象,而Component就像是在Module和Inject对象之间的一座桥梁,让他们二者产生关联。至于他们是怎么关联上的,我们再最后再细说,接着先说他们的使用。
4、单例Singleton 与 Scope
当我们需要获取某一个类的单例时,可以使用Singleton注解和自定义的Scope,
4.1)Singleton注解:
在Module中提供对象的方法上添加@Singleton注解,同时需要在该Module的Component上也需要添加@Singleton注解;
只要有一个Module是@Singleton注解过的,那么Component就必须要加上@Singleton注解;
Singleton注解的单例时局部单例,也就是说在第二个页面使用inject出来的HttpModule对象和在MainActivity中的对象时不相同的;
A、在同一个Activity中时:
inject第一个单例对象,会在调用Componet的get方法时进行双重检查(第一次获取对象,就会返回一个对象的实例,并将对象的provider设置为null ); inject第二个单例对象时,由于上一次已经将provider为null ,会直接将上一次的对象返回; 这样在同一个Activity中的inject的单例就是同一个对象。
B、当在不同的Activity中时:
由于两个Activity中的Component对象不是同一个对象,所以单例的module对象也不相同。
解决局部单例的方案:要实现全局Component对象,就需要将Component对象放到Application中去;
@Module
public class HttpModule {
@Singleton
@Provides
public HttpModule providerHttpModule(){
return new HttpModule();
}
}
@Singleton
@Component(modules = {DatabaseModule.class, HttpModule.class})
public interface CustomComponent {
void injectMainActivity(MainActivity mainActivity);
void injectSecondActivity(SecondActivity secondActivity);
}
public class MainActivity extends AppCompatActivity {
@Inject
HttpModule httpModule;
@Inject
HttpModule httpModule2;
@Inject
DatabaseModule databaseModule;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerCustomComponent.builder()
.studentComponent(DaggerStudentComponent.create())
.build()
.injectMainActivity(this);
Log.e("MainActivity", "MainActivity onCreate: 1----"+httpModule.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 2----"+httpModule2.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 3----"+databaseModule.hashCode());
}
}
全局单例:
//自定义的Application
public class BaseApplication extends Application {
CustomComponent customComponent;
@Override
public void onCreate() {
super.onCreate();
customComponent= DaggerCustomComponent.builder()
.httpModule(new HttpModule())
.databaseModule(new DatabaseModule())
.studentComponent(DaggerStudentComponent.create())
.build();
}
public CustomComponent getCustomComponent() {
return customComponent;
}
}
public class MainActivity extends AppCompatActivity {
@Inject
HttpModule httpModule;
@Inject
HttpModule httpModule2;
@Inject
DatabaseModule databaseModule;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((BaseApplication)this.getApplication()).getCustomComponent().injectMainActivity(this);
Log.e("MainActivity", "MainActivity onCreate: 1----"+httpModule.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 2----"+httpModule2.hashCode());
Log.e("MainActivity", "MainActivity onCreate: 3----"+databaseModule.hashCode());
}
public void onclick(View view) {
startActivity(new Intent(this,SecondActivity.class));
}
}
public class SecondActivity extends AppCompatActivity {
@Inject
HttpModule httpModule;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
((BaseApplication)this.getApplication()).getCustomComponent().injectSecondActivity(this);
Log.e("SecondActivity", "SecondActivity onCreate: 1----"+httpModule.hashCode());
}
}
//这样在两个Activity中inject的httpModule对象就是同一个对象了。
4.2)自定义Scope注解:
Singleton局部单例的问题,可以通过全局的Component来解决,那么如果有多个Module都是单例,这样用Sinleton就没办法处理了,这时候就需要自定义Scope注解了。
- 自定义Scope注解非常简单,就是将Singleton注解的定义复制一份,修改一下注解的名字。
- 在需要标识为单例的Module和Component的地方加上自定义的Scope就OK了;
- 多个component上面的scope不能相同,没有scope的component不能去依赖有scope的component;
@Scope
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomScope {
}
@Scope
@Documented
@Retention(RUNTIME)
public @interface StudentScope {
}
//下面是使用
@Module
public class StudentModule {
@StudentScope
@Provides
Student providerStudent(){
return new Student();
}
}
@StudentScope
@Component (modules = {StudentModule.class})
public interface StudentComponent {
// void inject(MainActivity mainActivity);
Student getStudent();
}
这样就解决了多个Module是单例的问题了。
5、标记 @Named 注解
当我们在Module中需要返回同一个类型的两个或多个对象时,如果按照上面的步骤,那么就会出现编译出错的问题,这是因为在Module中有两个或多个获取对象的方法,在inject的时候不知道该如何对应,这就需要使用@Named注解,其实说白了就是给返回相同类型对象的方法加上一个唯一的标记name,这样在inject的时候使用这个标记j就可以将对象对应起来了。
//////////////module
@Module
public class UseNamedModule {
@Named("C")
@Provides
Book getCBook(){
return new Book("C语言","c");
}
@Named("JAVA")
@Provides
Book getJavaBook(){
return new Book("JAVA语言","java");
}
public static class Book{
String name;
public String getName() {
return name;
}
public String getAuthor() {
return author;
}
String author;
public Book(String name, String author) {
this.name = name;
this.author = author;
}
}
}
////////component
@Component (modules = {UseNamedModule.class})
public interface UseNamedComponent {
void injectFourActivity(FourActivity activity);
}
//使用
public class FourActivity extends AppCompatActivity {
@Named("C")
@Inject
UseNamedModule.Book cBook;
@Named("JAVA")
@Inject
UseNamedModule.Book javaBook;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
DaggerUseNamedComponent.create().injectFourActivity(this);
Log.e("FourActivity", "FourActivity----"+cBook.getName()+"----"+cBook.getAuthor());
Log.e("FourActivity", "FourActivity----"+javaBook.getName()+"----"+javaBook.getAuthor());
}
}
说明:
- Named是用于区分在Module中提供了两个相同类型的对象时,给对象添加的一个标记,这样在Inject的时候同样使用Named标记来进行对应;
- 不使用Named,会导致编译的时候,不知道该使用Module的哪一个提供对象的方法;
6、懒加载 :Lazy , Provider
先来看看编译生成的代码:
public final class FiveActivity_MembersInjector implements MembersInjector<FiveActivity> {
private final Provider<ParamModule.Teacher> lazyProvider;
private final Provider<ParamModule.Teacher> providerProvider;
@Override
public void injectMembers(FiveActivity instance) {
injectLazy(instance, DoubleCheck.lazy(lazyProvider));
injectProvider(instance, providerProvider);
}
///。。。。此类省略了很多代码
}
//下面是DoubleCheck的lazy方法
public static <P extends Provider<T>, T> Lazy<T> lazy(P provider) {
if (provider instanceof Lazy) {
@SuppressWarnings("unchecked")
final Lazy<T> lazy = (Lazy<T>) provider;
// Avoids memoizing a value that is already memoized.
// NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
// are different types using covariant return on get(). Right now this is used with
// DoubleCheck<T> exclusively, which is implemented such that P and L are always
// the same, so it will be fine for that case.
return lazy;
}
return new DoubleCheck<T>(checkNotNull(provider));
}
Lazy 、Provider的使用:
//这里省略了Module 和Component的代码
public class FiveActivity extends AppCompatActivity {
@Inject
Lazy<ParamModule.Teacher> lazy;
@Inject
Provider<ParamModule.Teacher> provider;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
DaggerParamComponent.builder()
.paramModule(new ParamModule("Param",15))
.build().injectFiveActivity(this);
Log.e("FiveActivity", "FiveActivity--lazy--"+lazy.get().getName());
Log.e("FiveActivity", "FiveActivity--provider--"+provider.get().getName());
}
}
说明:
- 在使用Lazy ,Provider实现懒加载时,在使用Lazy ,provider获取对象时需要先调用其get()方法;
- Lazy比Provider在进行inject时,多做了DoubleCheck检查;
7、参数传递
在Module提供对象时,我们的对象经常需要一些参数来完成对象的构造,那么inject对象时,这些参数值又该如何传递给了对象呢?
将对象需要的参数保存在Module类中,在创建Module对象时,再讲参数值传递给Module
@Module
public class ParamModule {
public ParamModule(String name, int age) {
this.name = name;
this.age = age;
}
String name;
int age;
@Provides
Teacher providerTeacher(){
return new Teacher(name,age);
}
//静态内部类
public static class Teacher{
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
String name;
public int getAge() {
return age;
}
int age;
}
}
//给module传递参数值
public class FiveActivity extends AppCompatActivity {
@Inject
ParamModule.Teacher teacher;
@Inject
Lazy<ParamModule.Teacher> lazy;
@Inject
Provider<ParamModule.Teacher> provider;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
DaggerParamComponent.builder()
.paramModule(new ParamModule("Param",15))//此处就是给Module传递参数
.build().injectFiveActivity(this);
Log.e("FiveActivity", "FiveActivity----"+teacher.getName()+teacher.getAge());
Log.e("FiveActivity", "FiveActivity--lazy--"+lazy.get().getName());
Log.e("FiveActivity", "FiveActivity--provider--"+provider.get().getName());
}
}
四、Dagger2的原理分析
核心原理:
**APT技术(注解处理器) + 简单工厂模式 + Builder建造者模式**
将我们添加了@Module注解的类编译生成对应的对象的简单工厂类,该类负责提供该对象的实例,
将我们添加了@Component注解的接口编译生成对应的该接口的实现类,该实现类中包括了所有Module可提供的对象和依赖的Component对象,通过建造者模式将对象创建出来;
对使用Inject注解的类编译生成一个实现了MembersInjector泛型接口的实现类;
- 当调用Component的实现类创建对象时,先调用其builder()方法生成Component builder实例,然后调用build()方法时创建好Module提供的对象实例,这样我们再调用Component的inject方法时就可以将通过(3)的实现类来用创建好的Module对象给MembersInjector泛型实例对象的成员进行赋值了。
下面来看看编译生成的代码就非常清楚了:
1、Module生成的代码
public final class StudentModule_ProviderStudentFactory implements Factory<Student> {
private final StudentModule module;
public StudentModule_ProviderStudentFactory(StudentModule module) {
this.module = module;
}
@Override
public Student get() {
return providerStudent(module);
}
public static StudentModule_ProviderStudentFactory create(StudentModule module) {
return new StudentModule_ProviderStudentFactory(module);
}
public static Student providerStudent(StudentModule instance) {
return Preconditions.checkNotNullFromProvides(instance.providerStudent());
}
}
2、Component 代码:
public final class DaggerStudentComponent implements StudentComponent {
private final DaggerStudentComponent studentComponent = this;
private Provider<Student> providerStudentProvider;
private DaggerStudentComponent(StudentModule studentModuleParam) {
initialize(studentModuleParam);
}
public static Builder builder() {
return new Builder();
}
public static StudentComponent create() {
return new Builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final StudentModule studentModuleParam) {
this.providerStudentProvider = DoubleCheck.provider(StudentModule_ProviderStudentFactory.create(studentModuleParam));
}
@Override
public Student getStudent() {
return providerStudentProvider.get();
}
public static final class Builder {
private StudentModule studentModule;
private Builder() {
}
public Builder studentModule(StudentModule studentModule) {
this.studentModule = Preconditions.checkNotNull(studentModule);
return this;
}
public StudentComponent build() {
if (studentModule == null) {
this.studentModule = new StudentModule();
}
return new DaggerStudentComponent(studentModule);
}
}
}
3、inject生成的代码:
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final Provider<Student> studentProvider;
//构造方法
public MainActivity_MembersInjector(
Provider<Student> studentProvider) {
this.studentProvider = studentProvider;
}
//create方法:调用构造方法
public static MembersInjector<MainActivity> create(Provider<Student> studentProvider) {
return new MainActivity_MembersInjector(studentProvider);
}
@Override
public void injectMembers(MainActivity instance) {
injectStudent(instance, studentProvider.get());
}
@InjectedFieldSignature("com.leon.opensource_dagger2.MainActivity.student")
public static void injectStudent(MainActivity instance, Student student) {
instance.student = student;
}
}
五、知识点
我们运行程序的时候,经常会出现一些错误,这些错误信息中包含了很多关键信息,如下面的错误信息就可以了解到应用启动的调用关系:
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)