程式設計師不可以錯過的Android元件化神器-ServicePool!

yilian發表於2020-01-21

介紹

元件化開發過程中,隨著元件越來越多,元件的之前的互動就會變得非常的複雜,此時元件間通訊變得尤其的重要, ServicePool就是為元件化而生,用最簡單的方式進行元件間通訊。

使用依賴注入,按需靈活注入元件。同時支援元件熱插拔,達到元件即插即用的效果。可配置元件生命週期,做到元件按需建立和及時回收,充分利用懶載入的思想,有效解決元件初始化耗時導致的 app啟動速度問題。

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

ServicePool基礎能力

程式設計師不可以錯過的Android元件化神器-ServicePool!

如上圖所示:

  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使用 IBServicePool申請 bService
  6. aService, bServiceServicePool建立和管理
  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的注入方式很簡單,只有一種注入方式就是直接透過類注入。

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

ServicePool就是一個注入容器。最後透過 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)和優先順序來決定當前是建立還是直接返回快取物件。

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

程式設計師不可以錯過的Android元件化神器-ServicePool!

如果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使用不同的服務, 如 loggerMock等等
  3. 不同的業務場景使用不同的服務

給服務物件指定生命週期

每個由 ServicePool建立的 service物件都有各自生命週期, service物件的生命週期由 ServicePool管理, 並由 @Service註解配置生命週期型別。

  1. Serviceoncetempglobal三種生命週期型別.
  2. 指定 Service的生命週期為 once, @Service(scope=IService.Scope.once),每次 ServicePool.getService()都會建立一個新的物件,物件使用後隨 gc自動被回收,  scope預設為 once
  3. 指定 Service的生命週期為 temp, @Service(scope=IService.Scope.temp), ServiceWeakReference快取,只適用無狀態服務。
  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));輸出:falsetrue

支援透過path查詢Service

  /**
   * 服務池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開發過程中,肯定少不了對元件進行初始化,無論是內部元件還是引用外部庫,很多都需要執行初始化操作。常規的方式是所有初始化操作都是放到 ApplicationonCreate()/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註解還有 asyncdependencies兩個屬性。 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!!!");
      }
  }

ServicePool的初始化在
這篇文章的最後中有實際應用,也可做為示例參考。

元件懶載入機制 & 禁用懶載入

所有初始化操作都隨 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路由

未完待續....

star下我的GitHub: ,給文章點個贊一起學習!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69952849/viewspace-2674207/,如需轉載,請註明出處,否則將追究法律責任。

相關文章