Android註解使用之Dagger2實現項目依賴關係解耦

前言:

   最近牽頭髮起公司app的重構工做,如何經過重構讓項目的耦合下降、開發效率提升,一直是我努力的方向,今天來學習一下一個註解框架Dagger2,而後看看如何使用它來下降項目的耦合。    git

Dagger2

    一句話:一款快速的註解框架,應用於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採用的就是註解注入的方式,而後編譯自動生成目標代碼的方式實現宿主與被依賴者之間的關係。

Dagger2在Android的使用方式及簡單說明

在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和注入的橋樑

Dagger2舉例說明

 以項目中實際場景緩存管理爲例,來體驗一下解耦效果。設計遵循單一職責原則。

 1.首先定義緩存類和多任務類。而且在其構造函數上添加@Inject註解

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();
    }
}

2.使用@Module分別定義LCacheModule、LExecutorModule類來提供相關依賴

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);
    }
}

3.使用@Component 用來將@Inject和@Module關聯起來,新建LCacheComponent類

@Component(modules = {LCacheModule.class,LExecutorModule.class})
@Singleton
public interface LCacheComponent {

    LCache lCache();   // app緩存

    LExecutor lExecutor();  // app多任務線程池

    void inject(LCacheManager lCacheManager);
}

4.在宿主中注入想要依賴的對象

/**
* 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);
}
});
}
}

5.使用場景調用及簡單解說

LCacheManager.getInstance().saveCache("key","who is lcj ?");

看下打印結果:

經過Dagger2的方式剛開始可能會以爲忽然間一個簡單的事情,變得複雜了,其實沒有,經過Dagger2很好的處理好了依賴關係,具體說明,好比咱們緩存LCache須要添加一個最大緩存個數變化,若是按照以前的方式,咱們首先須要對LCache進行修改,好比修改構造函數增長maxCacheSize,而後必須對LCacheManager進行修改,如今經過Dagger2的方式的話,咱們只需修改LCacheModule就能夠了,LCache實例化和相關參數和LCacheManager之間並無太大的依賴關係。

6.關於@Module提供多個同類型@Provides

 基於上面的緩存處理需求,咱們須要實現讀寫分別使用不一樣的多任務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來下降項目中耦合。

相關文章
相關標籤/搜索