模組化是 mPaaS 架構的設計原則之一,業務模組的低耦合與高內聚有利於業務的擴充和維護。
業務模組以 Bundle 的形式存在互不影響,但 Bundle 之間會存在一些關聯性,比如跳轉到另一個 Bundle 介面,調用另一個 Bundle 中的介面,或者 Bundle 中的一些操作需要在初始化的過程中完成等。
因此,mPaaS 設計了 metainfo 萬用群組件註冊機制,各個 Bundle 將需要註冊的組件在 metainfo.xml 中聲明。
架構目前支援以下組件:
ActivityApplication(Application)
ExternalService(Service)
BroadcastReceiver
Pipeline
metainfo.xml 格式如下:
<?xml version="1.0" encoding="UTF-8"?>
<metainfo>
<broadcastReceiver>
<className>com.mpaas.demo.broadcastreceiver.TestBroadcastReceiver</className>
<action>com.mpaas.demo.broadcastreceiver.ACTION_TEST</action>
</broadcastReceiver>
<application>
<className>com.mpaas.demo.activityapplication.MicroAppEntry</className>
<appId>33330007</appId>
</application>
</metainfo>Application 組件
ActivityApplication 是 mPaaS 架構設計的組件,起到 Activity 容器的角色。ActivityApplication 組件的主要作用在於管理和組織各個 Activity,專門用於解決跳轉到另一個Bundle 介面的問題。因而,調用方只需關心業務方在架構中註冊的 ActivityApplication 資訊以及約定的參數。
關於此任務
ActivityApplication 的建立、銷毀等一系列邏輯完全由 mPaaS 架構來管理。業務方只需要處理其收到的參數並管理自己業務下的 Activity,這樣業務方和調用方之間被有效隔離開來。業務方和調用方只需要協調調用的參數,使得依賴更加輕量。
基於 mPaaS 架構開發的 Android 用戶端應用,Activity 須繼承自 BaseActivity 或 BaseFragmentActivity,以便能夠被 ActivityApplication 類所管理。
操作步驟
在工程的主 module 中建立
metainfo.xml檔案,放在如下圖位置:
在
metainfo.xml中寫入如下的配置,其中:className配置的類名用於提供跳轉的類名稱和定義各階段的行為。具體類的定義,參見步驟 3 的代碼。架構通過className定義的名稱載入相應的類,所以該類一定不能被混淆,需要在混淆檔案中保留。appId:業務的唯一標識。業務方只需要知道該業務的 appId 就能完成跳轉。appId 與 ActivityApplication 的映射由架構層處理。<?xml version="1.0" encoding="UTF-8"?> <metainfo> <application> <className>com.mpaas.demo.hotpatch.HotpatchMicroApp</className> <appId>33330002</appId> </application> </metainfo>
如果
metainfo通過className指定的類只是完成簡單的跳轉,使用以下代碼實現:/** * 情境一: * 若只可能會跳轉到某一個Activity介面,那麼需要重載getEntryClassName和onRestart,前者返回Activity的classname,後者需要調用getMicroApplicationContext().startActivity(this, getEntryClassName()); * 情境二: * 若需要根據需求跳轉到不同的Activity介面那麼需要重載onStart和onRestart,根據bundle中的參數跳轉到指定的介面 * Created by mengfei on 2018/7/23. */ public class MicroAppEntry extends ActivityApplication { @Override public String getEntryClassName() { //情境一:只可能跳轉到某一個 Activity 在此返回 classname 即可 //情境二:根據參數跳轉到某一介面,需要返回 null return MainActivity.class.getName(); } /** * Application被建立時被調用,實作類別可以在這裡做些初始化的工作 * * @param bundle */ @Override protected void onCreate(Bundle bundle) { doStartApp(bundle); } /** * 啟動Application時被調用 * 如果Application還沒有被建立,會先去執行create方法,然後再執行onStart()回調 */ @Override protected void onStart() { } /** * 當Application被銷毀時,調用此回調 * * @param bundle */ @Override protected void onDestroy(Bundle bundle) { } /** * 啟動Application時,如果Application已經被start過了,則不調用onStart()而是調用onRestart()回調 * * @param bundle */ @Override protected void onRestart(Bundle bundle) { //針對情境一:需要在此調用getMicroApplicationContext().startActivity(this, getEntryClassName()); doStartApp(bundle); } /** * 當一個新的Application被start時,當前的Application將被暫停,此方法被回調 */ @Override protected void onStop() { } private void doStartApp(Bundle bundle) { String dest = bundle.getString("dest"); if ("main".equals(dest)) { Context ctx = LauncherApplicationAgent.getInstance().getApplicationContext(); ctx.startActivity(new Intent(ctx, MainActivity.class)); } else if ("second".equals(dest)) { Context ctx = LauncherApplicationAgent.getInstance().getApplicationContext(); ctx.startActivity(new Intent(ctx, SecondActivity.class)); } } }作為調用者,您需要通過架構封裝的
MicroApplicationContext中提供的介面進行跳轉。curId參數也可以傳 null:// 擷取 MicroApplicationContext 對象: MicroApplicationContext context = MPFramework.getMicroApplicationContext(); String curId = ""; ActivityApplication curApp = context.getTopApplication(); if (null != curApp) { curId = curApp.getAppId(); } String appId = "目標ApplicationActivity的id"; Bundle bundle = new Bundle(); // 附加參數,也可以不傳 context.startApp(curId, appId, bundle);
Service 組件
mPaaS 設計了 Service 組件解決跨 Bundle 調用介面。Service 組件用於將一些邏輯以服務的形式提供出來,供其他模組使用。
關於此任務
Service 組件的特點如下:
沒有使用者介面的限制。
在設計上,遵循介面與實現分離。
原則上只有介面類對調用者可見,所以介面類應定義在介面 module 中(在產生 Bundle project 時,預設會產生一個介面 module,名字是 api),實現定義在主 module 中。
外部調用都通過 MicroApplicationContext 的 findServiceByInterface 介面,通過interfaceName 擷取相應的服務。對於 Bundle 使用來說,只暴露服務抽象介面類,即 interfaceName 中定義的類,抽象介面類會定義在介面包中。
操作步驟
通過以下步驟註冊 Service 組件:
定義
metainfo.xml位置,如下圖所示:
在
metainfo.xml中寫入如下的配置。架構將interfaceName作為key,className作為value,記錄兩者的映射關係。其中,className為具體介面的實作類別,interfaceName為抽象介面類:<metainfo> <service> <className>com.mpaas.cq.bundleb.MyServiceImpl</className> <interfaceName>com.mpaas.cq.bundleb.api.MyService</interfaceName> <isLazy>true</isLazy> </service> </metainfo>抽象介面類定義如下:
public abstract class MyService extends ExternalService { public abstract String funA(); }介面類實現定義如下:
public class MyServiceImpl extends MyService { @Override public String funA() { return "這是 BundleB 提供的介面 by service"; } @Override protected void onCreate(Bundle bundle) { } @Override protected void onDestroy(Bundle bundle) { } }外部調用方式如下:
MyService myservice = LauncherApplicationAgent.getInstance().getMicroApplicationContext().findServiceByInterface(MyService.class.getName()); myservice.funA();
BroadcastReceiver 組件
BroadcastReceiver 是 android.content.BroadcastReceiver 的封裝,但區別在於 mPaaS 架構採用了 android.support.v4.content.LocalBroadcastManager 來註冊和反註冊 BroadcastReciever,因此,這些廣播僅用於當前應用程式內部,除此之外,mPaas架構內部內建了一系列的廣播事件,供使用者監聽。
關於此任務
mPaaS內建廣播事件
mPaaS 定義了多種廣播事件,主要用於監聽當前應用的狀態,註冊監聽與原生開發沒有任何區別,但有一點需要特別注意,這些狀態只有在主進程才能監聽到。範例程式碼如下:
內建的廣播事件如下:
public interface MsgCodeConstants {
String FRAMEWORK_ACTIVITY_CREATE = "com.alipay.mobile.framework.ACTIVITY_CREATE";
String FRAMEWORK_ACTIVITY_RESUME = "com.alipay.mobile.framework.ACTIVITY_RESUME";
String FRAMEWORK_ACTIVITY_PAUSE = "com.alipay.mobile.framework.ACTIVITY_PAUSE";
// 使用者離開的廣播,壓後台廣播
String FRAMEWORK_ACTIVITY_USERLEAVEHINT = "com.alipay.mobile.framework.USERLEAVEHINT";
// 所有 Activity 全都 Stop 的廣播,可能代表壓後台,但目前沒有用相同的判斷邏輯
String FRAMEWORK_ACTIVITY_ALL_STOPPED = "com.alipay.mobile.framework.ACTIVITY_ALL_STOPPED";
String FRAMEWORK_WINDOW_FOCUS_CHANGED = "com.alipay.mobile.framework.WINDOW_FOCUS_CHANGED";
String FRAMEWORK_ACTIVITY_DESTROY = "com.alipay.mobile.framework.ACTIVITY_DESTROY";
String FRAMEWORK_ACTIVITY_START = "com.alipay.mobile.framework.ACTIVITY_START";
String FRAMEWORK_ACTIVITY_DATA = "com.alipay.mobile.framework.ACTIVITY_DATA";
String FRAMEWORK_APP_DATA = "com.alipay.mobile.framework.APP_DATA";
String FRAMEWORK_IS_TINY_APP = "com.alipay.mobile.framework.IS_TINY_APP";
String FRAMEWORK_IS_RN_APP = "com.alipay.mobile.framework.IS_RN_APP";
// 使用者回到前台的廣播
String FRAMEWORK_BROUGHT_TO_FOREGROUND = "com.alipay.mobile.framework.BROUGHT_TO_FOREGROUND";
}自訂廣播事件
定義
metainfo.xml位置,如下圖所示:
在
metainfo.xml中寫入如下配置:<?xml version="1.0" encoding="UTF-8"?> <metainfo> <broadcastReceiver> <className>com.mpaas.demo.broadcastreceiver.TestBroadcastReceiver</className> <action>com.mpaas.demo.broadcastreceiver.ACTION_TEST</action> </broadcastReceiver> </metainfo>自訂 Receiver 實現
public class TestBroadcastReceiver extends BroadcastReceiver { private static final String ACTION_TEST = "com.mpaas.demo.broadcastreceiver.ACTION_TEST"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_TEST.equals(action)) { //TODO } } }發送廣播
LocalBroadcastManager.getInstance(LauncherApplicationAgent.getInstance().getApplicationContext()).sendBroadcast(new Intent("com.mpaas.demo.broadcastreceiver.ACTION_TEST"));
Pipeline 組件
mPaaS 架構有一個比較明顯的啟動過程,Pipeline 機制允許業務線將自己的運行邏輯封裝成 Runnable 放到 Pipeline。架構在適當的階段啟動適當的 Pipeline。
以下為定義的 Pipeline 時機:
com.alipay.mobile.framework.INITED: 架構初始化完成。進程在後台啟動,架構也會初始化。com.alipay.mobile.client.STARTED: 用戶端開始啟動。必須等到介面出現,例如,歡迎介面。com.alipay.mobile.TASK_SCHEDULE_SERVICE_IDLE_TASK:優先順序最低,當沒有其他高優先順序的操作時才會得到執行
因為 Pipeline 的調用是由架構觸發,使用者只需要在 metainfo 裡指定相應的時機。
關於此任務
您可以下載包含該萬用群組件的程式碼範例。有關下載地址、使用方法及注意事項,查看 擷取程式碼範例。
操作步驟
定義
metainfo.xml位置,如下圖所示:
在
metainfo.xml中寫入如下的配置:<?xml version="1.0" encoding="UTF-8"?> <metainfo> <valve> <className>com.mpaas.demo.pipeline.TestPipeLine</className> <!--pipelineName就是用於指定執行所在的階段--> <pipelineName>com.alipay.mobile.client.STARTED</pipelineName> <threadName>com.mpaas.demo.pipeline.TestPipeLine</threadName> <!--weight指定了操作的最佳化級,值越小,代表越會優先得到執行--> <weight>10</weight> </valve> </metainfo>實現 Pipeline:
public class TestPipeLine implements Runnable { @Override public void run() { //.... } }