1、Dagger2的介紹和簡單使用:
A fast dependency injector for Android and Java.
一個快速的依賴注入庫為java和android,
什么是依賴注入?有興趣的同學可以看一下我之前轉載的一篇依賴注入的文章。
https://github.com/google/dagger
這是Dagger2的github地址,里面介紹了在項目中如何使用和依賴。
1.1 在介紹入門之前,先簡單的學習和認識一下基本的概念和名詞:
@inject
該注解定義在需要依賴的地方使用,即:標有這個注解的地方是告訴Dagger這個類或者字段需要依賴注入,當調用相關的注入方法時候,Dagger就會創(chuàng)建一個該類的實例,將其依賴注入。
@module
Module類里面的方法是專門提供依賴,所以,一個類被@Module所注解,也就是告訴Dagger,構造實例從哪里去找需要的依賴(或者是創(chuàng)建),modules的一個重要的特點是它們設計為分區(qū)和組合在一起,即,假如我們的APP中需要多個module,這個時候,我們可以使用modules這個注解進行將其組合在一塊。
@provide
在module中,我們定義的方法用這個注解,以此來告訴Dagger,我們想要創(chuàng)建這些對象,并提供。
@component
component類似于一個橋梁,也可以說一個注入器,即@Inject和@Module之間的橋梁,它的作用是連接這兩部分。
這里只是先做一個概念的解釋和闡述
2、如何快速使用 ?分為4步:
2.1 在Demo中,我們模擬一個用戶管理類(增加用戶功能)
public class UserManage {
public void addUser(){
Log.d("UserManage","Add a new User");
}
}
2.2 創(chuàng)建Module去提供相應的構建
@Module
public class UserModule {
@Provides
UserManage provideUserManager(){
return new UserManage();
}
}
2.3 創(chuàng)建Component實現(xiàn)橋梁
@Component(modules =UserModule.class )
public interface UserComponent {
void inject(MainActivity activity);
}
2.4 在Activity中進行依賴注入UserManage,這個類
public class MainActivity extends AppCompatActivity {
@Inject
UserManage userManage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerUserComponent.create().inject(this);
userManage.addUser();
}
}
我相信看上面的代碼可能有一定的難度,下面我會詳細的解釋一下一步步是如何實現(xiàn),為什么這么做?在詳細介紹之前,我們先看看我們的Demo是不是可以運行起來,如果可以打印出addUser()中的日志,說明我們在MainActivity中的將UserManage依賴成功。

說明我們依賴成功,并且調用了UserMange的addUser()方法,切記我們在運行前,一定要進行rebuild project一下,使用apt插件工具幫我們自動生成代碼。
3. Dagger2進階使用:
到這里,我們算是已經(jīng)是一個Dagger2的入門了,學會了簡單的使用。但是如何將Dagger2運用到自己的實際項目中,還需要在實際項目中使用。在這里我們所有使用的類是沒有進行參數(shù)的傳遞,這顯然是不符合在實際項目中的業(yè)務因此,這里我們需要給UserManage傳遞一個User參數(shù),代表實際要注入的對象。
@Module
public class UserModule {
@Provides
UserManage provideUserManager(User user){
return new UserManage(user);
}
}
那么我們?nèi)绾芜M行依賴注入這個User對象,我們重新rebuild的話,控制臺會報下面的錯誤,可以看出,是因為沒有提供User對象,而造成的,那么我們該如何提供這個User對象的實例?

有3種方案:(提供給它User對象不就完事了)
3.1、在UserModule中提供一個User對象 ,即:
@Module
public class UserModule {
@Provides
UserManage provideUserManager(User user){
return new UserManage(user);
}
@Provides
User provideUser(){
return new User("OneX_Zgj","22");
}
}
我們在UserManager中打印了相關則信息:
public class UserManage {
public UserManage(User user){
Log.d("TAG", "UserManage: "+user.getName() +" : " +user.getAge());
}
public void addUser(){
Log.d("Tag","Add a new User");
}
}
MainActivity和Component中的代碼沒有動:
運行結果:可以看到執(zhí)行成功了,并且依賴注入了User對象

3.2:單獨創(chuàng)建一個UserCreateModule
單獨創(chuàng)建一個UserCreateModule進行provide 一個特定的User對象,但是我們在UserModule中如何使用呢?
先看UserCreateModule中的實現(xiàn)
@Module
public class UserCreateModule {
@Provides
User provideUser(){
return new User("OneX_zgj","22");
}
}
@Module(includes = UserCreateModule.class)
public class UserModule {
@Provides
UserManage provideUserManager(User user){
return new UserManage(user);
}
// @Provides
// User provideUser(){
// return new User("OneX_Zgj","22");
// }
}
現(xiàn)在觀察在UserModule中是如何進行實現(xiàn)的?細心的同學肯定會發(fā)現(xiàn)在類上面的注解放生了變化,對,就是因為這個注解,我們就可以引用和依賴UserCreateModule中提供的對象。

3.3:注解依賴到UserComponent中
我們將UserCreateModule加入到UserComponent中,也是可以運行的:
@Component(modules ={UserModule.class,UserCreateModule.class} )
public interface UserComponent {
void inject(MainActivity activity);
}
4. 創(chuàng)建和區(qū)分不同的對象實例
4.1在介紹和使用之前,先介紹一下這兩個注解
@Qualifier:區(qū)分不同的對象實例
@Named :其實是@Qualifier的一種實現(xiàn),我們也可以自己定義。
4.1.1 @Name 注解的使用:標記不同的對象實例
在實際項目中,我們有業(yè)務是進行創(chuàng)建出不同的實例對象,但是從我們的Demo
比如我們開發(fā)一般在測試庫,而實際發(fā)布,需要改用正式版庫,那么,普通的話,我們需要修改不同地址等等。
在這里我們簡單模擬一下功能,我們使用NetUrl假設封裝了地址,我們需要2中不同的url,去適配測試庫和正式庫,所以在代碼中,我們需要根據(jù)某種狀態(tài)或者標識,進行將它們區(qū)分出來:
NetUrl的簡單實現(xiàn):
public class NetUrl {
private String url;
public NetUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
在看一下我們在Module中的實現(xiàn)和使用@Name注解區(qū)分不同的實例(正式庫和測試庫)
@Module
public class OkhttpApi {
@Provides
@Named("test")
NetUrl provideNetTestUrl(){
return new NetUrl("http://test");
}
@Provides
@Named("release")
NetUrl provideNetReleaseUrl(){
return new NetUrl("http://release");
}
}
在看一下OkhttpApiComponent中的實現(xiàn):
@Component(modules = OkhttpApi.class)
public interface OkhttpApiComponent {
void inject(MainActivity activity);
}
接下來我們查看下MainActivity中的代碼:需要注意的是,如果在Module中進行加入了@Name注解,我們在MainActivity中的@Inject旁邊也要加入@Name注解,指明當前對象需要注入什么樣的實例。
public class MainActivity extends AppCompatActivity {
// @Inject
// UserManage userManage;
@Inject
@Named("test")
NetUrl TestApi;
@Inject
@Named("release")
NetUrl ReleaseApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerOkhttpApiComponent.create().inject(this);
Log.d("Tag", TestApi.getUrl());
Log.d("Tag", ReleaseApi.getUrl());
}
}

4.1.2我們通過自定義Scope來實現(xiàn)和區(qū)分不同的實例
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Release {
}
1、定義了Release的scope,好奇的同學,可能會問,你是怎么知道這樣定義Scope的,哈哈,答案是:我肯定抄的啦,就是模仿@Name注解進行寫的。
2、定義Test的Scope
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Test {
}
在Module中替換相應的注解
@Module
public class OkhttpApi {
// @Provides
// @Named("test")
// NetUrl provideNetTestUrl(){
// return new NetUrl("http://test");
// }
//
//
// @Provides
// @Named("release")
// NetUrl provideNetReleaseUrl(){
// return new NetUrl("http://release");
// }
@Provides
@Test
NetUrl provideNetTestUrl(){
return new NetUrl("http://test");
}
@Provides
@Release
NetUrl provideNetReleaseUrl(){
return new NetUrl("http://release");
}
}
在MainActivity中進行替換為我們自定義的Scope
public class MainActivity extends AppCompatActivity {
// @Inject
// UserManage userManage;
// @Inject
// @Named("test")
// NetUrl TestApi;
//
// @Inject
// @Named("release")
// NetUrl ReleaseApi;
@Inject
@Test
NetUrl TestApi;
@Inject
@Release
NetUrl ReleaseApi;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerOkhttpApiComponent.create().inject(this);
Log.d("Tag", TestApi.getUrl());
Log.d("Tag", ReleaseApi.getUrl());
}
}

@Singleton的使用,在實際項目開發(fā)中,我們會對網(wǎng)絡請求對象進行單例(比如okhttp實例),來節(jié)約內(nèi)存,這個時候我們就需要進行使用Singleton,來表明該對象是要用單例創(chuàng)建的。
4.2 如何在實際項目中進行單例的操作:(比如確保在App中有only one 個okhttp的實例?)
下面的Demo將演示如何在Application中創(chuàng)建一個單例:
4.2.1 創(chuàng)建Module
@Module
public class AppMoudle {
private MyApp context;
public AppMoudle(MyApp context) {
this.context = context;
}
@Singleton
@Provides
public OkhttpUtils provideOkhttpUtils() {
OkhttpUtils okService = new OkhttpUtils(context);
Log.d("TAG", "provideApiService: " + okService);
return okService;
}
}
4.2.2 創(chuàng)建AppComponent
@Singleton
@Component (modules = AppMoudle.class)
public interface AppComponent {
/**
* 全局單例。所以不用Inject Activity
*
* @return 向下返回ApiService實例
*/
OkhttpUtils getApiService();
}
4.2.3在App中實例化出來AppComponent,確保是唯一的實例
public class MyApp extends Application {
private AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.builder().appMoudle(new AppMoudle(this)).build();
}
public AppComponent getAppComponent() {
return mAppComponent;
}
}
4.2.4 在子類的Component中使用:
UserComponet 中的實現(xiàn):
@PreActivity
@Component(modules = UserModule.class ,dependencies = AppComponent.class)
public interface UserComponet {
void inject(MainActivity activity);
}
LoginComponent 中的實現(xiàn):
@PreActivity
@Component(modules = UserModule.class,dependencies = AppComponent.class)
public interface LoginComponent {
void inject(LoginActivity activity);
}
這里我們?yōu)槭裁匆脗€@PreActivity,因為在AppComponent中使用了Singleton注解,沒有scope的component不能依賴有scope的component,而且要不同于依賴的Component,所以這里我們需要自定義一個Scope。
PreActivity的實現(xiàn)
@Scope
@Documented
@Retention(RUNTIME)
public @interface PreActivity {
}
4.2.5在Activity中的使用:
public class MainActivity extends AppCompatActivity {
@Inject
OkhttpUtils okhttpUtils;
@Inject
OkhttpUtils okhttpUtils2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnStart = (Button) findViewById(R.id.btn_start);
DaggerUserComponet.builder().appComponent(((MyApp) getApplication()).getAppComponent()).build().inject(this);
okhttpUtils.logId();
okhttpUtils2.logId();
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, LoginActivity.class));
}
});
}
}
LoginActivity中的實現(xiàn)和MainActivity中的實現(xiàn)完全一樣。
最后一步,運行一下程序,我們觀察okhttpClient實例的內(nèi)存地址:

這樣我們實現(xiàn)了App中的單例操作。
5.在使用Dagger2中我們經(jīng)常會范的一下錯誤:
1.component的inject方法接受父類型的參數(shù),而調用的時候傳入的子類型對象,對象則無法進行注入。
比如:我們在Component使用的是BaseActivity,而實際要注入的是MainActivity,雖然MainActivity extends BaseActivity,但是在Dagger2中,依舊無法進行注入。