Android組件化神器 —— ServicePool

組件化開發過程當中,隨着組件愈來愈多,組件的以前的交互就會變得很是的複雜,此時組件間通訊變得尤爲的重要,ServicePool就是爲組件化而生,用最簡單的方式進行組件間通訊。使用依賴注入,按需靈活注入組件。同時支持組件熱插拔,達到組件即插即用的效果。可配置組件生命週期,作到組件按需建立和及時回收,充分利用懶加載的思想,有效解決組件初始化耗時致使的app啓動速度問題。點擊進入 項目地址java

ServicePool包含有 Activity路由, 組件路由等等最經常使用的組件化能力。除此以外,組件化開發過程當中有沒有遇到過想使用某個已有的類,好比一個工具類的時候,發現這個工具類在當前類的上層,沒法直接依賴和引用,而修改這個工具類的層級又會牽一髮而動全身的問題? 有沒有想要一個差別響應的能力,在不一樣的組件中或者環境下,有着不一樣的響應方式?有沒有想要一個自適應場景的能力,自動適應當前環境(好比Java仍是Android環境,好比Debug環境仍是Release環境等等),從而使用最合適的功能。又有沒有想過如何讓組件作到像USB接口同樣插上就能直接使用,拔掉也不影響主體功能的即插即用的效果。等等...。 下面就來具體介紹一下這個組件化神器——ServicePool!git

ServicePool基礎能力

基礎能力示意圖

如上圖所示:github

  1. 組件A,B是兩個互不依賴的組件,A,B不能直接通訊
  2. 組件A,B分別經過AService, BService對外提供服務
  3. 組件A,B的接口協議存放在組件服務池pool, 分別是接口IA, IB
  4. 當組件B須要組件A的服務時,組件B使用IA接口向ServicePool申請, 由ServicePool建立並返回aService給組件B, 此時組件b可使用aService的服務了
  5. 同理, 組件A使用IB向ServicePool申請bService
  6. aService,bService由ServicePool建立和管理
  7. 全部Service對象的優先級生命週期能夠經過@Service註解配置
/**
 * 服務池pool中
 *
 * IA.java
 */
public interface IA {
    void aName();
}


/**
 * 服務池pool
 *
 * IB.java
 */
public interface IB {
    void bName();
}
複製代碼
/**
 * 組件A
 *
 * AService.java
 */
@Service
public class AService implements IA {

    @Override
    public String aName() {
        return "A Service";
    }
}
複製代碼
/**
 * 組件B
 * 
 * BService.java
 */
@Service
public class BService implements IB {

    @Override
    public String bName() {
        return "B Service";
    }
}
複製代碼
組件A中執行:
   IB b = ServicePool.getService(IB.class);
   System.out.println("I'm A Service, I can get " + b.bName());

輸出: 
  I'm A Service, I can get B Service 組件B中執行: IA a = ServicePool.getService(IA.class); System.out.println("I'm B Service, I can get " + a.aName()); 輸出: I'm B Service, I can get A Service 複製代碼

依賴注入(DI)

因爲全部示例涉及到依賴注入,這裏提早對ServicePool的依賴注入作個說明。和其餘注入框架不一樣,ServicePool的注入方式很簡單,只有一種注入方式就是直接經過Class注入。數組

@Service
public class AImpl implements IA {
    @Override
    public String aName() {
        return "A Impl"
    }
}
複製代碼

ServicePool就是一個基於Class注入容器。最後經過ServicePool.getService(IA.class)方法注入對象, 也能夠經過@Service標記成員變量的方式注入,這兩個方法等價。緩存

public class MainActivity extends AppcompatActivity {
    /**
     * 等價於
     * IA = ServicePool.getService(IA.class);
     */
    @Service
    private IA a; 
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        System.out.println(a.getName()); //輸出 A Service
    }
}
複製代碼

ServicePool注入對象時,會根據Service配置的生命週期類型(scope)和優先級來決定當前是建立仍是直接返回緩存對象。bash

指定Service優先級,按優先級順序返回

組件按優先級順序返回示意圖

若是IA有多個實現,如上圖所示,ServicePool會比較每一個實現優先級,來決定最終返回IA的哪一個實現微信

  1. @Service註解標記一個實現類時候能夠經過參數priority指定這個實現類的優先級
  2. 優先級值越大優先級越高, ServicePool默認會返回優先級最高的實現類對象
  3. 若是多個實現類優先級相同,那麼返回會有不肯定性
  4. 也能夠直接指定具體使用哪一個實現類,如ServicePool.getService(AService1.class)將會返回一個AService1對象
/**
 * 服務池pool中
 * 
 * IPriorityService.java
 */
public interface IPriorityService {
    int getPriority();
}
複製代碼
/**
 * 組件A中
 * 
 * PriorityService1.java
 * PriorityService2.java
 */
@Service(priority = 1)
public class PriorityService1 implements IPriorityService {
    @Override
    public int getPriority() {
        return 1;
    }
}

@Service(priority = 2)
public class PriorityService2 implements IPriorityService {
    @Override
    public int getPriority() {
        return 2;
    }
}
複製代碼
組件B中執行:
    IPriorityService priorityService = ServicePool.getService(IPriorityService.class);
    System.out.println("priority is " + priorityService.getPriority());
    
    priorityService = ServicePool.getService(PriorityService1.class);
    System.out.println("priority is " + priorityService.getPriority());
    
    priorityService = ServicePool.getService(PriorityService2.class);
    System.out.println("priority is " + priorityService.getPriority());
    
輸出:
   priority is 2
   priority is 1
   priority is 2
複製代碼

典型應用場景

  1. Java Library組件和Android Library組件使用不一樣的服務, 如classloader等等。
  2. debug環境,release環境或者不一樣的productFlavor使用不一樣的服務, 如logger, Mock等等
  3. 不一樣的業務場景使用不一樣的服務

給服務對象指定生命週期

每一個由ServicePool建立的service對象都有各自生命週期,service對象的生命週期由ServicePool管理, 並由@Service註解配置生命週期類型。markdown

  1. Service有once, temp, global三種生命週期類型.
  2. 指定Service的生命週期爲once,@Service(scope=IService.Scope.once),每次ServicePool.getService()都會建立一個新的對象,對象使用後隨gc自動被回收, scope默認爲once
  3. 指定Service的生命週期爲temp,@Service(scope=IService.Scope.temp),Service由WeakReference緩存,只適用無狀態服務。
  4. 指定Service的生命週期爲global,@Service(scope=IService.Scope.global),每次ServicePool.getService()拿到的都是同一個對象,App運行期間不會被回收
組件A中
/**
 * 
 * OnceService.java
 */
@Service(scope = IService.Scope.once)
public class OnceService implements LifecycleService {
}


/**
 * 
 * TempService.java
 */
@Service(scope = IService.Scope.temp)
public class TempService implements LifecycleService {
}


/**
 * 
 * GlobalService.java
 */
@Service(scope = IService.Scope.global)
public class GlobalService implements LifecycleService {
}
複製代碼
組件B中執行:
    System.out.println(ServicePool.getService(OnceService.class) == ServicePool.getService(OnceService.class));
    //System.out.println(ServicePool.getService(TempService.class) == ServicePool.getService(TempService.class));//不可靠
    System.out.println(ServicePool.getService(GlobalService.class) == ServicePool.getService(GlobalService.class));

輸出:
    false
    true
複製代碼

支持經過path查找Service

因爲ServicePool是基於class作的注入操做, ServicePool內部會將path映射成Class,這個映射操做是在編譯期完成的。app

/**
 * 服務池pool中
 * 
 * IPathService.java
 */
public interface IPathService {
    String pathServiceName();
}

複製代碼
/**
 * 組件A中
 * 
 * PathService
 */
@Service(path = "pathService")
public class PathService implements IPathService {
    @Override
    public String pathServiceName() {
        return "Path Service";
    }
}
複製代碼

IPathService是任意定義的接口,它能夠有一個或多個實現類,只要在實現類上加@Service註解並指定path屬性。咱們就能夠經過ServicePool.getService(path)來找到或者建立他的實現類對象。框架

組件B中執行:
    IPathService pathService = ServicePool.getService("pathService");
    System.out.println(pathService.pathServiceName());

輸出:
    Path Service
複製代碼

典型應用場景:

  1. activity路由
  2. 混合開發中,能夠經過path將橋接方法分發到對應執行器

組件初始化

app開發過程當中,確定少不了對組件進行初始化,不管是內部組件仍是引用外部庫,不少都須要執行初始化操做。常規的方式是全部初始化操做都是放到Application的onCreate()/onAttachBaseContext()方法中執行。組件有不少而Application只有1個, 如何讓每一個組件均可以擁有它本身的初始化類呢?

ServciePool中有一個@Init註解,任何被@Init註解標記的Service類被認爲是一個須要執行操做初始化操做的Service類, 同時這個Service類須要實現IInitService接口。

@Init(lazy = false) //lazy = false表示禁用懶加載,則該Service會隨Application初始化而初始化
@Service
public class InitService implements IInitService {
    
    @Override
    public void onInit() {
        //do something.
    }
}
複製代碼

若是初始化組件想要隨Application初始化而初始化,須要將@Init註解的lazy賦值爲false,表示禁用懶加載。 除了lazy屬性,@Init註解還有async,dependencies兩個屬性。

async屬性顧名思義是異步執行,async默認爲false,設置爲true表示該組件初始化會在異步線程執行。

dependencies能夠傳遞一個初始化組件類數組,表示當前組件初始化依賴這個數組中的全部組件。ServicePool會先初始化數組中的組件再去執行當前組件的初始化。

@Init
@Service
public class InitService1 implements IInitService {
    
    @Override
    public void onInit() {
        System.out.println("Service 1 Inited!!!");
    }
}

@Init
@Service
public class InitService2 implements IInitService {
    
    @Override
    public void onInit() {
        System.out.println("Service 2 Inited!!!");
    }
}

@Init(lazy = false, dependencies=[InitService1.class, InitService2.class])
@Service
public class InitService3 implements IInitService {
    
    @Override
    public void onInit() {
        System.out.println("Service 3 Inited!!!");
    }
}
複製代碼

因爲InitService1, InitService2之間沒有依賴關係,所以他兩的執行順序不肯定,InitService3同時依賴InitService1和InitService2,所以InitService3必定是最後執行的。

Application初始化後執行結果:
    Service 2 Inited!!!
    Service 1 Inited!!!
    Service 3 Inited!!!

複製代碼

ServicePool的初始化在如何優雅的管理多環境下的Android代碼這篇文章的最後中有實際應用,也可作爲示例參考。

組件懶加載機制 & 禁用懶加載

全部初始化操做都隨Application啓動執行,一方面會致使Application很是臃腫,另外一方面雖然單個組件初始化時長很短,但n多個組件初始化時長結合在了一塊兒就會致使啓動時間超長。

懶加載是ServicePool的核心思想。全部組件只有在第一次被使用時纔會被建立和執行初始化。而不是集中在Application初始化過程。分散初始化從而減輕App啓動壓力。舉個🌰

微信分享是很經常使用的功能,咱們以微信分享爲例,WXShareManager用來助力微信分享相關操做。

@Init
@Service
public class WXShareManager implement IInitService {
    
    public static final String appId = "wx499fa9b1ba4a93db";
    public static final String userName = "gh_6619a14a139d";


    @Override
    public void onInit() {
        IWXAPI wxApi = WXAPIFactory.createWXAPI(mContext, null);
        wxApi.registerApp(appId);
    }
    
    public void share(...) {
        //do wx share.
    }
}
複製代碼

shareManager注入對象的時候初始化操做會被執行。

public class ShareActivity extends AppcompatActivity {
    @Service
    private WXShareManager shareManager;//此時會觸發WXShareManager的onInit初始化。
    
    ...
    
    void onClick(View v) {
        shareManager.share(...);
    }
}
複製代碼

組件熱插拔

未完待續....

Activity路由

未完待續....

快速接入ServicePool

相關文章
相關標籤/搜索