CC框架实践(3):让jsBridge更优雅

前言

今天给大家讲一下在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框架

CC: Component Caller(github地址),是一个可跨app进行组件调用的android组件化开发框架,可支持包括Activity跳转、数据获取、网络请求等等几乎所有指令的跨组件调用。

组件实现方可自行决定是同步实现还是异步实现。

调用方可自行决定是同步调用还是异步调用(<font color=red>Notice: 不要在主线程同步调用耗时操作</font>)。

并且组件调用可关联Activity和Fragment的生命周期(在onDestroy被调用时自动取消调用,避免出现内存泄露)

CC框架的使用方式,可查看github中的 README文档

CC框架的实现原理 详细介绍

CC框架下如何让jsBridge更优雅?

CC框架为所有组件提供了统一的调用入口和回调结果格式。

所以,在CC框架下,js调用native变得很简单:

  • jsBridge仅暴露一个接口给js,那就是组件调用接口
  • js调用jsBridge的接口,将组件调用所需的参数传给jsBridge
  • jsBridge将参数透传去调用功能组件(所有功能实现均在各个组件内部完成)
  • jsBridge中接收到调用结果后,将结果转换成json回调给js

流程图:

jsBridge调用流程

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 + ")"); 
                }
            });
        }
    }
}

是不是超级简单?

这样做的好处有:

  1. jsbridge回归初心:只是作为一个桥梁。
  2. jsBridge支持的功能更全面,app内部几乎所有组件的功能都可以给js调用,而无需添加额外的代码
  3. 业务完全在组件内部实现,jsbridge跟组件之间无耦合
  4. 无论功能是同步实现的还是异步回调实现的,中间需要经历什么样的流程,对于js和jsBridge来说调用方式完全一样。
  5. 支持组件的按需依赖:jsBridge不再是全家桶,给多个app使用时,各app可以按需选择需要支持js调用的组件,添加gradle依赖到主module的依赖列表中即可。
  6. 同一个组件在不同的app内可以有不同的实现,但需要保持接口协议一致,例如:不同app可以有自己特定的登录组件
  7. 后续添加新功能给js调用时,只要功能提供方实现一下,js中去调用即可,jsbridge组件无需修改

Tips

1. 有些功能必须要在onActivityResult中接收结果,如何在组件内部实现而不影响jsBridge?

确实有些功能必须要在onActivityResult中接收结果,例如:调用系统的选择联系人、从系统相册选择图片等。

其实,不止是onActivityResult,还有获取权限的回调onRequestPermissionsResult

这些功能在组件内部实现时,可以在组件中通过创建一个透明的Activity或Fragment来实现结果的接收,然后将结果发送给调用方: CC.sendCCResult(callId, result);

<font color=blue>推荐使用Fragment方式实现</font>

具体实现方式可参考如下开源库:

2. js调用的有些功能需要用户登录后才能用,如何加入登录条件判断?

按照组件化开发的思想,是否需要登录才能用应由各组件自行判断。

需要在组件内部完成登录状态校验、打开登录界面、登录完成后再执行组件实际功能。

具体实现可参考另一篇文章: CC框架实践(1):实现登录成功再进入目标界面功能

3. 没有使用CC框架的情况下,如何让jsBridge实现类似效果?

在没有使用CC框架的情况下,也可以实现类似效果的。思路如下:

  1. 在工程的Common基础库中定义一套接口,例如: IJsCall/IJsCallback
public interface IJsCall {
    String name(); //功能的名称,供js调用
    void handleJsCall(JSONObject params, IJsCallback callback);
}
public interface IJsCallback {
    void callback(String result);
}
  1. 在所有需要注册给js调用的组件中实现IJsCall接口,实现具体的业务逻辑
  2. 在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);
        }
    }
}
  1. 使用AutoRegister来完成IJsCall接口的自动注册, Github源码
  2. 在jsBridge中只暴露一个接口给js调用
  3. 在jsBridge中调用JsCallManager.onJsCall方法来实现统一的功能调用

总结

本文介绍了在CC框架下用组件调用的方式让jsBridge实现跟具体业务完全解耦。并给出了非CC框架环境下实现类似效果的思路。

系列文章

CC框架实践(1):实现登录成功再进入目标界面功能

CC框架实践(2):Fragment和View的组件化

CC框架实践(3):让jsBridge更优雅

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,319评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,801评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,567评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,156评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,019评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,090评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,500评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,192评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,474评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,566评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,338评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,212评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,572评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,890评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,169评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,478评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,661评论 2 335

推荐阅读更多精彩内容