最近牽頭髮起公司app的重構工做,如何經過重構讓項目的耦合下降、開發效率提升,一直是我努力的方向,今天來學習一下一個註解框架Dagger2,而後看看如何使用它來下降項目的耦合。 git
一句話:一款快速的註解框架,應用於Android、Java,由 Google 開發和維護,是 Square 的 Dagger 項目的分支。github
gitHub:https://github.com/google/dagger編程
Dagger2採用依賴注入方式,依賴注入是一種面向對象的編程模式,它的出現是爲了下降耦合性,所謂耦合就是類之間依賴關係,所謂下降耦合就是下降類和類之間依賴關係。設計模式
Java的面向對象編程特性,一般會在一個Java對象中引用另外一個Java對象,舉例說明一下:緩存
public class ClassA { private ClassB classB; public ClassA(){ classB =new ClassB(); } public void doSomething(){ classB.doSomething(); } }
經過上面的例子能夠看出,ClassA須要藉助ClassB才能完成一些特定操做,可是咱們在ClassA直接實例化了ClassB,這樣耦合就產生了,第一違背了單一職責原則,ClassB的實例化應該由本身完成,不該該由ClassA來完成,第二違背了開閉原則,一旦ClassB的構造函數產生變化,就須要修改ClassA的構造函數。app
經過依賴注入下降這種耦合關係:框架
1.經過構造參數傳參的方式ide
public class ClassA { private ClassB classB; public ClassA(ClassB classB){ this.classB =classB; } public void doSomething(){ classB.doSomething(); } }
2.經過set方法的方式函數
public class ClassA { private ClassB classB; public ClassA(){ } public void setClassB(ClassB classB) { this.classB = classB; } public void doSomething(){ classB.doSomething(); } }
3.經過接口注入的方式學習
interface ClassBInterface { void setB(ClassB classB); } public class ClassA implements ClassBInterface { private ClassB classB; public ClassA() { } @Override public void setB(ClassB classB) { this.classB = classB; } public void doSomething() { classB.doSomething(); } }
4.經過註解注入
public class ClassA { @Inject ClassB classB; public ClassA() { } public void doSomething() { classB.doSomething(); } }
Dagger2採用的就是註解注入的方式,而後編譯自動生成目標代碼的方式實現宿主與被依賴者之間的關係。
在Android中的使用方式很簡單:只需在Module的build.gradle中添加一下配置
dependencies { compile 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' }
Dagger2 annotation講解
@Module 修飾的類專門用來提供依賴
@Provides 修飾的方法用在Module類裏
@Inject 修飾須要依賴的地方(能夠是構造方法、field或者通常的方法)
@Component 鏈接@Module和注入的橋樑
以項目中實際場景緩存管理爲例,來體驗一下解耦效果。設計遵循單一職責原則。
LCache類
/** * Created by lichaojun on 2017/3/30. * 處理緩存 */ public class LCache { private static final String DEFAULT_CACHE_NAME="LCache";//默認緩存名字 private static final int DEFAULT_MAX_CACHE_SIZE=1024;//默認緩存名字 private String cacheName=DEFAULT_CACHE_NAME;//緩存名字 private int maxCacheSize=DEFAULT_MAX_CACHE_SIZE; public LCache (){ } @Inject public LCache(String cacheName,int maxCacheSize){ this.cacheName=cacheName; this.maxCacheSize=maxCacheSize; } public void saveCache(String key ,String value){ Log.e(LCacheManager.TAG,"cacheName: = "+cacheName); Log.e(LCacheManager.TAG,"maxCacheSize: = "+maxCacheSize); Log.e(LCacheManager.TAG,"saveCache: key = "+key +" value = "+value); } public void readCache(String key){ Log.e(LCacheManager.TAG,"readCache: key: = "+key); } }
LExecutor類
public class LExecutor { private static final int DEFAULT_CPU_CORE = Runtime.getRuntime().availableProcessors();//默認線程池維護線程的最少數量 private int coreSize = DEFAULT_CPU_CORE;//線程池維護線程的最少數量 @Inject public LExecutor(int coreSize) { this.coreSize = coreSize; } public void runTask(Runnable runnable) { if (runnable == null) { return; } Log.e(LCacheManager.TAG,"coreSize: = "+coreSize); Log.e(LCacheManager.TAG, "runTask"); runnable.run(); } }
LCacheModule類
@Module public class LCacheModule { /** * 提供緩存對象 * @return 返回緩存對象 */ @Provides @Singleton LCache provideLCache() { return new LCache("lcj",500); } }
LExecutorModule類
@Module public class LExecutorModule { /** * 提供app 多任務最少維護線程個數 * @return 返回多任務最少維護線程個數 */ @Provides @Singleton LExecutor provideLExecutor() { return new LExecutor(10); } }
@Component(modules = {LCacheModule.class,LExecutorModule.class}) @Singleton public interface LCacheComponent { LCache lCache(); // app緩存 LExecutor lExecutor(); // app多任務線程池 void inject(LCacheManager lCacheManager); }
/**
* Created by lichaojun on 2017/3/30.
* 緩存處理管理
*/
public class LCacheManager {
public static final String TAG=LCacheManager.class.getSimpleName();
private LCacheComponent cacheComponent;
private static class SingletonHolder {
private static LCacheManager instance = new LCacheManager();
}
private LCacheManager(){
cacheComponent = DaggerLCacheComponent.builder().lCacheModule(new LCacheModule()).build();
cacheComponent.inject(this);
}
public static LCacheManager getInstance() {
return SingletonHolder.instance;
}
public void saveCache(final String key , final String value) {
cacheComponent.lExecutor().runTask(new Runnable() {
@Override
public void run() {
cacheComponent.lCache().saveCache(key,value);
}
});
}
public void readCache(final String key){
cacheComponent.lExecutor().runTask(new Runnable() {
@Override
public void run() {
cacheComponent.lCache().readCache(key);
}
});
}
}
LCacheManager.getInstance().saveCache("key","who is lcj ?");
看下打印結果:
經過Dagger2的方式剛開始可能會以爲忽然間一個簡單的事情,變得複雜了,其實沒有,經過Dagger2很好的處理好了依賴關係,具體說明,好比咱們緩存LCache須要添加一個最大緩存個數變化,若是按照以前的方式,咱們首先須要對LCache進行修改,好比修改構造函數增長maxCacheSize,而後必須對LCacheManager進行修改,如今經過Dagger2的方式的話,咱們只需修改LCacheModule就能夠了,LCache實例化和相關參數和LCacheManager之間並無太大的依賴關係。
基於上面的緩存處理需求,咱們須要實現讀寫分別使用不一樣的多任務LExecutor,而且LExecutor的最小線程數爲5,咱們會在LCacheComponent添加提供writeLExecutor函數,以下:
@Component(modules = {LCacheModule.class,LExecutorModule.class}) @Singleton public interface LCacheComponent { LCache lCache(); // app緩存 LExecutor lExecutor(); // app多任務線程池 LExecutor writeLExecutor(); // app 寫緩存多任務線程池 void inject(LCacheManager lCacheManager); }
在LExecutorModule中添加提供依賴初始化的provideWriteLExecutor函數。以下:
@Module public class LExecutorModule { /** * 提供app 多任務最少維護線程個數 * @return 返回多任務最少維護線程個數 */ @Provides @Singleton LExecutor provideLExecutor() { return new LExecutor(10); } /** * 提供app 多任務最少維護線程個數 * @return 返回多任務最少維護線程個數 */ @Provides @Singleton LExecutor provideWriteLExecutor() { return new LExecutor(5); } }
而後寫完以後Rebuild一下項目,覺得萬事大吉了,結果報了以下錯誤,
怎麼辦呢,難道Dagger2就這麼不堪一擊嗎,固然不是解決這個問題很容易,使用@Named註解解決這個問題,咱們只須要在LCacheComponent的writeLExecutor()和
LExecutorModule的provideWriteLExecutor()函數上添加相同的@Named("WriteLExecutor")便可。
對於Module的provide函數也是能夠傳遞參數的,不過須要在當前Module中須要提供相關的參數的函數。例如:LCacheModule能夠修改以下:
@Module public class LCacheModule { /** * 提供緩存對象 * @return 返回緩存對象 */ @Provides @Singleton LCache provideLCache( @Named("LCache")String name , @Named("LCache")int maxCacheSize) { return new LCache(name,maxCacheSize); } /** * 提供緩存對象 * @return 返回緩存對象 */ @Provides @Singleton @Named("LCache") String provideLCacheName() { return "lcjCache"; } /** * 提供緩存對象 * @return 返回緩存對象 */ @Provides @Singleton @Named("LCache") int provideLCacheMaxSize() { return 600; } }
這裏又使用了別名@Name也是由於爲了不bound multiple times錯誤致使編譯失敗,在編譯的過程當中Dagger2會自動去尋找相關參數進行綁定依賴關係,這點仍是挺神奇的。
今天簡單的寫個例子對Dagger2有個初步的理解與認識,因爲項目並無採用MVP設計模式,準備逐步採用Dagger2+MVP來下降項目中耦合。