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

前言

今天给你们讲一下在CC框架下如何让咱们的jsBridge更加优雅。javascript

jsBridge是做为js和java之间通讯的桥梁,自己它的职责只是完成通讯。java

本文不是介绍js与java通讯过程的实现,你可使用第三方库(如:JsBridge),也能够本身来实现,或者用addJavascriptInterface,均可以。本文的侧重点是如何让咱们的jsBridge不那么臃肿,实现得更优雅,更利于维护。android

但在实际封装过程当中,会发现须要咱们须要解决不少耦合的问题:git

  • js调用的功能在其余module中,如何调用到这些功能,如何向jsbridge注册这些功能?
  • jsbridge依赖了太多module,怎么解耦?
  • 当js调用的功能是打开其它页面获取该页面处理后的结果并回调给js,怎么破? onResume? startActivityForResult? 一个常见的场景是:打开登陆界面,登陆成功后将用户信息回调给js。你是否是想过这样作?
    • jsBridge中封装一个Activity/Fragment
    • 用startActivityForResult的方式来打开登陆页面
    • 在onActivityResult方法中从登陆界面设置的result中获取用户登陆的信息(或者onResume或EventBus方式来获取返回值)
    • 而后将用户信息回调给js

将具体的业务逻辑写在jsBridge模块中,自己就是一个灾难,并且随着业务类型的增长,最后这个Activity/Fragment会变得很是臃肿,并且难以复用github

快速了解CC

  • 是一套基于组件总线的组件化实施方案
  • 一静一动,开发时运行2个app,业务环境始终是完整的:
    • 静:主App (经过跨App的方式调用单组件App内的组件)
    • 动:正在开发中的单组件App (经过跨App的方式调用主App内的组件)
  • 支持渐进式组件化改造
    • 解耦只是过程,而不是前提

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

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

因此,在CC框架下,js调用native变得很简单:json

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

流程图:app

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);

推荐使用Fragment方式实现

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

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:可关联生命周期的android组件化开发框架

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

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

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

相关文章
相关标签/搜索