標籤:
前言:
前面分析了APP的現狀以及業務組件化的一些探討(Android業務組件化之現狀分析與探討),以及通訊的橋樑Schema的使用(Android業務組件化之URL Schema使用),今天重點來聊下子模組SubModule的拆分以及它們之間的路由Router實現。本篇涉及的相關知識比較多,閱讀本篇之間需要大致瞭解一下Java的註解(Java學習之註解Annotation實現原理)、Java的動態代理機制(Java設計模式之代理模式(Proxy))等。業務組件化是一個循序漸進的過程,一開始很難就能拿出終極解決方案,還是一步步來走的比較踏實實在。
我們首先搞清楚什麼是業務組件?
搞清楚這個對我們來說至關重要,否則很難拆分業務與依賴庫,也很難搞清楚哪些類應該放在業務子模組裡面哪些類應該放在依賴庫裡面。
1.)和業務無關
完全和業務沒有一點關係,比如項目中常用的各種Utils工具類,一些公用自訂控制項例如顯示圓角圖片的ImageView等
2.)弱業務
為什麼稱之為弱業務,原因就是這些不是完整的業務,但是又和APP業務相關,比如我們的網路請求,資料庫操作等。
3.)業務
這個就是我們針對要拆分的業務組件,一個完整的獨立的業務線才能稱之為業務,比如我們APP的登入註冊業務等。
業務組件的拆分粒度?
業務組件的拆分將是整個整改的重點,關於拆分的粒度也將成為討論的焦點,到底是粗一點好還是細一點好?粗一點更容易拆分,細一點更容易解耦靈活度高,這個根據實際情況來定,由於我們項目整改的過程中不能影響到新需求的開發,開始還是以粗一點的粒度進行拆分,先把大致幾個業務拆分出來,後期慢慢再做細。
子模組SubModule拆分:1.)子模組沒有拆分之間
頁面跳轉
Intent intent = new Intent(this, XXX.class);startActivity(intent);
資料傳遞
直接頁面startActivityForResult返回擷取, 間接頁面通過儲存或者變數 ,或者藉助開源架構EventBus等傳遞
沒有拆分的這種開發方式,其實使用起來簡單方便,但是這種顯示調用沒有任務文檔,每一個跳轉頁面都要和相關開發人員溝通,溝通成本比較高。
2.)拆分子模組之後
拆分子模組之後,任何模組之間的跳轉都要採用路由Router中轉,需要在相關Activity中配置如下資訊
<activity android:name=".GoodsDetailActivity" android:theme="@style/AppTheme"> <intent-filter> <data android:host="goods" android:path="/goodsDetail" android:port="8888" android:scheme="xl"/> <category android:name="android.intent.category.DEFAULT"/> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.BROWSABLE"/> </intent-filter> </activity>
頁面跳轉
/** * 通過uri跳轉指定頁面 * * @param url */ private void openRouterUri(String url) { PackageManager packageManager = mContext.getPackageManager(); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); boolean isValid = !activities.isEmpty(); if (isValid) { mContext.startActivity(intent); } }
資料的傳遞同樣可以直接頁面startActivityForResult返回擷取, 間接頁面通過儲存或者變數 ,或者藉助開源架構EventBus等傳遞,但是這些弱業務公用資料統一放在依賴庫裡。模組之間不存在代碼引用。通過這種方式android,iOS與H5用戶端可以通過一些簡單的url來實現跳轉了,通維護一份文檔來約束各個頁面的參數。
3.)路由Router簡單實現
1.定義兩個註解參數,一個標示URI註解,一個標示參數
RouterUri.java
@Documented@Target(METHOD)@Retention(RUNTIME)public @interface RouterUri { String routerUri() default "";}
RouterParam.java
@Documented@Target(PARAMETER)@Retention(RUNTIME)public @interface RouterParam { String value() default ""; }
2.定義一個URI協議介面
IRouterUri.java
public interface IRouterUri { @RouterUri(routerUri = "xl://goods:8888/goodsDetail")//請求Url地址 String jumpToGoodsDetail(@RouterParam("goodsId") String goodsId, @RouterParam("des") String des);//參數商品Id 商品描述}
3.定義一個單例,內部通過動態代理機制實現跳轉
public class XLRouter { private final static String TAG = XLRouter.class.getSimpleName(); private static XLRouter mInstance; private IRouterUri mRouterUri; private Context mContext; /** * 擷取單例引用 * * @return 單例 */ public static XLRouter getInstance(Context context) { XLRouter inst = mInstance; if (inst == null) { synchronized (XLRouter.class) { inst = mInstance; if (inst == null) { inst = new XLRouter(context.getApplicationContext()); mInstance = inst; } } } return inst; } /** * 建構函式 * * @param mContext application */ private XLRouter(Context mContext) { this.mContext = mContext; mRouterUri = create(IRouterUri.class); } /** * 返回Api */ public IRouterUri routerUri() { return mRouterUri; } public IRouterUri create(Class<?> aClass) { return (IRouterUri) Proxy.newProxyInstance(aClass.getClassLoader(), new Class<?>[]{aClass}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object... args) throws Throwable { StringBuilder stringBuilder = new StringBuilder(); RouterUri reqUrl = method.getAnnotation(RouterUri.class); Log.e(TAG, "IReqApi---reqUrl->" + reqUrl.routerUri()); stringBuilder.append(reqUrl.routerUri()); //Type[] parameterTypes = method.getGenericParameterTypes();//擷取註解參數類型 Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();//拿到參數註解 //Annotation[] annotation = method.getDeclaredAnnotations(); int pos = 0; for (int i = 0; i < parameterAnnotationsArray.length; i++) { Annotation[] annotations = parameterAnnotationsArray[i]; if (annotations != null && annotations.length != 0) { if (pos == 0) { stringBuilder.append("?"); } else { stringBuilder.append("&"); } pos++; RouterParam reqParam = (RouterParam) annotations[0]; stringBuilder.append(reqParam.value()); stringBuilder.append("="); stringBuilder.append(args[i]); Log.e(TAG, "reqParam---reqParam->" + reqParam.value() + "=" + args[i]); } } //下面就可以執行相應的跳轉操作 openRouterUri(stringBuilder.toString()); return null; } }); } /** * 通過uri跳轉指定頁面 * * @param url */ private void openRouterUri(String url) { PackageManager packageManager = mContext.getPackageManager(); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0); boolean isValid = !activities.isEmpty(); if (isValid) { mContext.startActivity(intent); } }}
4.調用方式
XLRouter.getInstance(context).routerUri().jumpToGoodsDetail("1000110002","goods des");總結:
要實現真正的業務組件化任重而道遠,我們這裡實現第一步拆分子模組,讓各個模組的代碼各自維護,先解耦他們之間的依賴關係,在app殼工程通過compile project(‘:umeng_social_sdk_library‘)這種方式選擇性接入哪個子模組,開發過程中開發哪個模組就引入哪個模組,測試環節也是如此,測試哪個模組打包引入哪個模組,最終測試需要引入全部相關模組,測試上線。
Android業務組件化之子模組SubModule的拆分以及它們之間的路由Router實現