一、什么是IOC注入?
IOC ( Inversion of Control ) :
原來是由程序代碼中主動獲取的資源,現(xiàn)在變成由第三方獲取并使用原來的代碼被動接受的方式,從而達到解耦的效果,也就是控制反轉(zhuǎn)。
通俗易懂:
**之前我們將兩個對象A與B產(chǎn)生關(guān)聯(lián),是在一個對象A中去new另外一個對象B,而IOC技術(shù)則是當(dāng)對象A需要對象B時,IOC容器就會給A提供一個B對象,而B對象的創(chuàng)建和銷毀全部交給IOC去處理,對象A就完全不用關(guān)心這些。**
IOC注入分三種:
1) 運行時注入:eventBus 等
2)源碼是注入: android studio插件
3)編譯期注入:butterknife ,dagger2
二、Dagger2 的介紹
Dagger2 是Java和Android的依賴注入的編譯期框架。Dagger完成注入有三個角色:
1)生產(chǎn)者(Module): 主要負責(zé)對象的生產(chǎn)
2)橋梁(Component):將生產(chǎn)的對象與消費者進行對應(yīng)關(guān)聯(lián)
3)消費者(Inject):對象的使用
三、Dagger2 的基本使用
1、添加Dagger2的依賴
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-complier:2.4'
2、提供對象的@Module
- @Module注解負責(zé)給我們提供需要的 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 是一個接口,該接口方法中的參數(shù)是不能使用多態(tài)的,只能明確是哪一個類;例如MainActivity ,不能使用Activity來替換;
@Component(modules = {DatabaseModule.class, HttpModule.class})
public interface CustomComponent {
//此處的參數(shù)是不能多態(tài)的,即不能用Activity來偷懶,必須是明確的類,例如MainActivity
void injectMainActivity(MainActivity mainActivity);
}
以上當(dāng)個Component的情況比較簡單,但實際情況中,會出現(xiàn)一個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已經(jīng)在上面的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())//這里添加了依賴后編譯才會出現(xiàn)
.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時,由從組件(子組件)負責(zé)與Activity的inject ,而主組件(父組件)負責(zé)提供獲取子組件的對象;
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的時候,需要調(diào)用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 他們之間的關(guān)系已經(jīng)有所了解:Module就是我們的內(nèi)容提供者,或者說就對象的提供者,他負責(zé)創(chuàng)建對象,而Component就像是在Module和Inject對象之間的一座橋梁,讓他們二者產(chǎn)生關(guān)聯(lián)。至于他們是怎么關(guān)聯(lián)上的,我們再最后再細說,接著先說他們的使用。
4、單例Singleton 與 Scope
當(dāng)我們需要獲取某一個類的單例時,可以使用Singleton注解和自定義的Scope,
4.1)Singleton注解:
在Module中提供對象的方法上添加@Singleton注解,同時需要在該Module的Component上也需要添加@Singleton注解;
只要有一個Module是@Singleton注解過的,那么Component就必須要加上@Singleton注解;
Singleton注解的單例時局部單例,也就是說在第二個頁面使用inject出來的HttpModule對象和在MainActivity中的對象時不相同的;
A、在同一個Activity中時:
inject第一個單例對象,會在調(diào)用Componet的get方法時進行雙重檢查(第一次獲取對象,就會返回一個對象的實例,并將對象的provider設(shè)置為null ); inject第二個單例對象時,由于上一次已經(jīng)將provider為null ,會直接將上一次的對象返回; 這樣在同一個Activity中的inject的單例就是同一個對象。B、當(dāng)在不同的Activity中時:
由于兩個Activity中的Component對象不是同一個對象,所以單例的module對象也不相同。解決局部單例的方案:要實現(xiàn)全局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注解的定義復(fù)制一份,修改一下注解的名字。
- 在需要標(biāo)識為單例的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、標(biāo)記 @Named 注解
當(dāng)我們在Module中需要返回同一個類型的兩個或多個對象時,如果按照上面的步驟,那么就會出現(xiàn)編譯出錯的問題,這是因為在Module中有兩個或多個獲取對象的方法,在inject的時候不知道該如何對應(yīng),這就需要使用@Named注解,其實說白了就是給返回相同類型對象的方法加上一個唯一的標(biāo)記name,這樣在inject的時候使用這個標(biāo)記j就可以將對象對應(yīng)起來了。
//////////////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是用于區(qū)分在Module中提供了兩個相同類型的對象時,給對象添加的一個標(biāo)記,這樣在Inject的時候同樣使用Named標(biāo)記來進行對應(yīng);
- 不使用Named,會導(dǎo)致編譯的時候,不知道該使用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實現(xiàn)懶加載時,在使用Lazy ,provider獲取對象時需要先調(diào)用其get()方法;
- Lazy比Provider在進行inject時,多做了DoubleCheck檢查;
7、參數(shù)傳遞
在Module提供對象時,我們的對象經(jīng)常需要一些參數(shù)來完成對象的構(gòu)造,那么inject對象時,這些參數(shù)值又該如何傳遞給了對象呢?
將對象需要的參數(shù)保存在Module類中,在創(chuàng)建Module對象時,再講參數(shù)值傳遞給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);
}
//靜態(tài)內(nèi)部類
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傳遞參數(shù)值
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傳遞參數(shù)
.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技術(shù)(注解處理器) + 簡單工廠模式 + Builder建造者模式**
將我們添加了@Module注解的類編譯生成對應(yīng)的對象的簡單工廠類,該類負責(zé)提供該對象的實例,
將我們添加了@Component注解的接口編譯生成對應(yīng)的該接口的實現(xiàn)類,該實現(xiàn)類中包括了所有Module可提供的對象和依賴的Component對象,通過建造者模式將對象創(chuàng)建出來;
對使用Inject注解的類編譯生成一個實現(xiàn)了MembersInjector泛型接口的實現(xiàn)類;
- 當(dāng)調(diào)用Component的實現(xiàn)類創(chuàng)建對象時,先調(diào)用其builder()方法生成Component builder實例,然后調(diào)用build()方法時創(chuàng)建好Module提供的對象實例,這樣我們再調(diào)用Component的inject方法時就可以將通過(3)的實現(xiàn)類來用創(chuàng)建好的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;
//構(gòu)造方法
public MainActivity_MembersInjector(
Provider<Student> studentProvider) {
this.studentProvider = studentProvider;
}
//create方法:調(diào)用構(gòu)造方法
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;
}
}
五、知識點
我們運行程序的時候,經(jīng)常會出現(xiàn)一些錯誤,這些錯誤信息中包含了很多關(guān)鍵信息,如下面的錯誤信息就可以了解到應(yīng)用啟動的調(diào)用關(guān)系:
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)