前言
今天給大家講一下在CC框架下如何讓我們的jsBridge更加優雅。
jsBridge是作為js和java之間通訊的橋樑,本身它的職責只是完成通訊。
本文不是介紹js與java通訊過程的實現,你可以使用第三方庫(如:JsBridge),也可以自己來實現,或者用addJavascriptInterface,都可以。本文的側重點是如何讓我們的jsBridge不那麼臃腫,實現得更優雅,更利於維護。
但在實際封裝過程中,會發現需要我們需要解決很多耦合的問題:
- js呼叫的功能在其他module中,如何呼叫到這些功能,如何向jsbridge註冊這些功能?
- jsbridge依賴了太多module,怎麼解耦?
- 當js呼叫的功能是開啟其它頁面獲取該頁面處理後的結果並回撥給js,怎麼破? onResume? startActivityForResult? 一個常見的場景是:開啟登入介面,登入成功後將使用者資訊回撥給js。你是不是想過這樣做?
- jsBridge中封裝一個Activity/Fragment
- 用startActivityForResult的方式來開啟登入頁面
- 在onActivityResult方法中從登入介面設定的result中獲取使用者登入的資訊(或者onResume或EventBus方式來獲取返回值)
- 然後將使用者資訊回撥給js
將具體的業務邏輯寫在jsBridge模組中,本身就是一個災難,而且隨著業務型別的增加,最後這個Activity/Fragment會變得非常臃腫,而且難以複用
快速瞭解CC
- 是一套基於元件匯流排的元件化實施方案
- 一靜一動,開發時執行2個app,業務環境始終是完整的:
- 靜:主App (通過跨App的方式呼叫單元件App內的元件)
- 動:正在開發中的單元件App (通過跨App的方式呼叫主App內的元件)
- 支援漸進式元件化改造
- 解耦只是過程,而不是前提
CC框架下如何讓jsBridge更優雅?
CC框架為所有元件提供了統一的呼叫入口和回撥結果格式。
所以,在CC框架下,js呼叫native變得很簡單:
- jsBridge僅暴露一個介面給js,那就是元件呼叫介面
- js呼叫jsBridge的介面,將元件呼叫所需的引數傳給jsBridge
- jsBridge將引數透傳去呼叫功能元件(所有功能實現均在各個元件內部完成)
- jsBridge中接收到呼叫結果後,將結果轉換成json回撥給js
流程圖:
JsBridge的核心程式碼如下:
public class JsBridge {
private WeakReference<WebView> webViewWeakReference;
public JsBridge(WebView webView) {
this.webViewWeakReference = new WeakReference<WebView>(webView);
}
@JavascriptInterface
public void callNativeCC(String componentName, String actionName, String dataJson, final String callbackId) {
final WebView webView = webViewWeakReference.get();
if (webView == null) {
return;
}
Map<String, Object> params = null;
if (!TextUtils.isEmpty(dataJson)) {
try {
JSONObject json = new JSONObject(dataJson);
params = JsonUtil.toMap(json); //引數列表
} catch (JSONException e) {
e.printStackTrace();
}
}
//統一使用這種方式進行CC呼叫,不用關心具體元件是如何實現的
CC cc = CC.obtainBuilder(componentName)
.steActionName(actionName)
.setContext(webView.getContext()) //可用於startActivity等需要Context的功能
.setParams(params)
.build();
if (TextUtils.isEmpty(jsCallbackId)) {
cc.callAsync(); //無需回撥結果給js
} else {
cc.callAsyncCallbackOnMainThread(new IComponentCallback() {
@Override
public void onResult(CC cc, CCResult result) {
//將結果回撥給js
webView.loadUrl("javascript: callback(" + callbackId + "," + result + ")");
}
});
}
}
}
複製程式碼
是不是超級簡單?
這樣做的好處有:
- jsbridge迴歸初心:只是作為一個橋樑。
- jsBridge支援的功能更全面,app內部幾乎所有元件的功能都可以給js呼叫,而無需新增額外的程式碼
- 業務完全在元件內部實現,jsbridge跟元件之間無耦合
- 無論功能是同步實現的還是非同步回撥實現的,中間需要經歷什麼樣的流程,對於js和jsBridge來說呼叫方式完全一樣。
- 支援元件的按需依賴:jsBridge不再是全家桶,給多個app使用時,各app可以按需選擇需要支援js呼叫的元件,新增gradle依賴到主module的依賴列表中即可。
- 同一個元件在不同的app內可以有不同的實現,但需要保持介面協議一致,例如:不同app可以有自己特定的登入元件
- 後續新增新功能給js呼叫時,只要功能提供方實現一下,js中去呼叫即可,jsbridge元件無需修改
Tips
1. 有些功能必須要在onActivityResult中接收結果,如何在元件內部實現而不影響jsBridge?
確實有些功能必須要在onActivityResult中接收結果,例如:呼叫系統的選擇聯絡人、從系統相簿選擇圖片等。
其實,不止是onActivityResult,還有獲取許可權的回撥onRequestPermissionsResult
這些功能在元件內部實現時,可以在元件中通過建立一個透明的Activity或Fragment來實現結果的接收,然後將結果傳送給呼叫方: CC.sendCCResult(callId, result);
推薦使用Fragment方式實現
具體實現方式可參考如下開源庫:
-
透明Activity方式實現的許可權申請庫:AndPermission, 參考類:PermissionActivity.java
-
Fragment方式實現的許可權申請庫: RxPermissions,參考類:RxPermissions.java
2. js呼叫的有些功能需要使用者登入後才能用,如何加入登入條件判斷?
按照元件化開發的思想,是否需要登入才能用應由各元件自行判斷。
需要在元件內部完成登入狀態校驗、開啟登入介面、登入完成後再執行元件實際功能。
具體實現可參考另一篇文章: CC框架實踐(1):實現登入成功再進入目標介面功能
3. 沒有使用CC框架的情況下,如何讓jsBridge實現類似效果?
在沒有使用CC框架的情況下,也可以實現類似效果的。思路如下:
- 在工程的Common基礎庫中定義一套介面,例如: IJsCall/IJsCallback
public interface IJsCall {
String name(); //功能的名稱,供js呼叫
void handleJsCall(JSONObject params, IJsCallback callback);
}
public interface IJsCallback {
void callback(String result);
}
複製程式碼
- 在所有需要註冊給js呼叫的元件中實現IJsCall介面,實現具體的業務邏輯
- 在jsBridge中建立一個IJsCall的管理類JsCallMananger,示例程式碼:
public class JsCallManager {
private final Map<String, IJsCall> map = new HashMap<>();
public static final String DEFAULT_RESULT = "{\"success\":false}";
void init() {
//用於IJsCall自動註冊到list
//使用AutoRegister外掛將生成如下程式碼:
// registerJsCall(new JsCallA());
// registerJsCall(new JsCallB());
}
void registerJsCall(IJsCall call) {
if (call != null) {
map.put(call.name(), call);
}
}
public void onJsCall(String name, JSONObject json, IJsCallback callback) {
IJsCall jsCall = map.get(name);
if (jsCall != null) {
jsCall.handleJsCall(json, callback);
} else {
callback.callback(DEFAULT_RESULT);
}
}
}
複製程式碼
- 使用AutoRegister來完成IJsCall介面的自動註冊, Github原始碼, 原理介紹
- 在jsBridge中只暴露一個介面給js呼叫
- 在jsBridge中呼叫JsCallManager.onJsCall方法來實現統一的功能呼叫
總結
本文介紹了在CC框架下用元件呼叫的方式讓jsBridge實現跟具體業務完全解耦。並給出了非CC框架環境下實現類似效果的思路。