[聚合文章] 混合开发框架整理:使用Crosswalk + WebViewJavascriptBridge进行混合开发

JavaScript 1900-01-01 21 阅读

作者:hwj3747
转载请注明

Crosswalk介绍

目前APP的开发模式大多基于H5+原生壳的开发模式,这时候使用到的WebView的性能就至关重要。我们知道,Android平台上,系统的碎片化比较严重,同Android版本的WebView的H5解析能力也有较大差异,导致相应的HTML5应用一致性难以保证。所以在做混合开发的时候,对Android系统的适配是一个比较麻烦的问题。

这个时候,如果能在我们的APP嵌入一个第三方,不使用系统自带浏览器的话,这些问题就都迎刃而解了。Crosswalk就是这样一个第三方浏览器,其具有较好的H5性、功能支持,较好的平台一致性,以及近似原生应用的系统整合体验。

Crosswalk项目具有以下优势:

最大限度降低Android碎片化的影响,得到一致的,可预测的行为。
使用最新的Web技术及API。在Android 4.0+版本上提供丰富的功能。
使用Chrome DevTools轻松调试。(很方便的功能,可以直接在谷歌浏览器上进行调试,只不过需要翻墙)
提升应用中HTML,CSS和JavaScript的性能。

WebViewJavascriptBridge介绍

WebViewJavascriptBridge 是盛名已久的 JSBridge 库,其主要作用是作为原生native与web JS沟通的“桥梁”,讲人话就是,能实现JS调用原生代码,以及原生调用JS的这么一个功能。目前该库已经累计获得1W+的start,并且已经有很多成熟的项目在使用这个库了,由此可见,该库是被广大开发者所认可的。

美中不足的是,目前官方只支持IOS版本的库。万幸它是开源的,许多人大神都基于它实现了支持Android版本的WebViewJavascriptBridge。比如说我使用到的是“大头鬼”开发的一个库:WebViewJavascriptBridge

准备工作

首先,先从crosswalk官网下载aar包
我用到的是这个包:crosswalk-23.53.589.4.aar
然后从github上down下WebViewJavascriptBridge库,导入Android studio
然后在该项目的基础上导入crosswalk的aar包:
集成方法:

  • 设置grade外部库为libs,拷贝aar文件到libs**
repositories {    flatDir {        dirs 'libs'    }}
  • 关联crosswalk库
compile(name: 'crosswalk-23.53.589.4', ext: 'aar')

然后同步一下代码就准备就绪了

使用Crosswalk和WebViewJavascriptBridge开发应用

打开项目,发现原本的项目用的还是系统自带的webview以及WebViewClient

public class BridgeWebView extends WebView implements WebViewJavascriptBridge 
public class BridgeWebViewClient extends WebViewClient

我们要做的就是把这两个替换成相应的crosswalk的XWalkView和XWalkResourceClient
所以新建一个类,模仿BridgeWebView 的写法,把BridgeWebView 相关的注册和处理handle,消息分发,等操作搬到自定义的XWalkView上。

@SuppressLint("SetJavaScriptEnabled")public class MyXWalkView extends XWalkView implements WebViewJavascriptBridge {    private final String TAG = "BridgeWebView";    public static final String toLoadJs = "WebViewJavascriptBridge.js";    Map<String, CallBackFunction> responseCallbacks = new HashMap<String, CallBackFunction>();    Map<String, BridgeHandler> messageHandlers = new HashMap<String, BridgeHandler>();    BridgeHandler defaultHandler = new DefaultHandler();    private List<Message> startupMessage = new ArrayList<Message>();    public List<Message> getStartupMessage() {        return startupMessage;    }    public void setStartupMessage(List<Message> startupMessage) {        this.startupMessage = startupMessage;    }    private long uniqueId = 0;    public MyXWalkView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MyXWalkView(Context context) {        super(context);        init();    }    /**     *     * @param handler     *            default handler,handle messages send by js without assigned handler name,     *            if js message has handler name, it will be handled by named handlers registered by native     */    public void setDefaultHandler(BridgeHandler handler) {        this.defaultHandler = handler;    }    private void init() {        this.setVerticalScrollBarEnabled(false);        this.setHorizontalScrollBarEnabled(false);        this.getSettings().setJavaScriptEnabled(true);        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {            WebView.setWebContentsDebuggingEnabled(true);        }        this.setResourceClient(new MyXWalkResourceClient(this));    }    /**     * 获取到CallBackFunction data执行调用并且从数据集移除     * @param url     */    void handlerReturnData(String url) {        String functionName = BridgeUtil.getFunctionFromReturnUrl(url);        CallBackFunction f = responseCallbacks.get(functionName);        String data = BridgeUtil.getDataFromReturnUrl(url);        if (f != null) {            f.onCallBack(data);            responseCallbacks.remove(functionName);            return;        }    }    @Override    public void send(String data) {        send(data, null);    }    @Override    public void send(String data, CallBackFunction responseCallback) {        doSend(null, data, responseCallback);    }    /**     * 保存message到消息队列     * @param handlerName handlerName     * @param data data     * @param responseCallback CallBackFunction     */    private void doSend(String handlerName, String data, CallBackFunction responseCallback) {        Message m = new Message();        if (!TextUtils.isEmpty(data)) {            m.setData(data);        }        if (responseCallback != null) {            String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));            responseCallbacks.put(callbackStr, responseCallback);            m.setCallbackId(callbackStr);        }        if (!TextUtils.isEmpty(handlerName)) {            m.setHandlerName(handlerName);        }        queueMessage(m);    }    /**     * list<message> != null 添加到消息集合否则分发消息     * @param m Message     */    private void queueMessage(Message m) {        if (startupMessage != null) {            startupMessage.add(m);        } else {            dispatchMessage(m);        }    }    /**     * 分发message 必须在主线程才分发成功     * @param m Message     */    void dispatchMessage(Message m) {        String messageJson = m.toJson();        //escape special characters for json string  为json字符串转义特殊字符        messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");        messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");        String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);        // 必须要找主线程才会将数据传递出去 --- 划重点        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {            this.loadUrl(javascriptCommand);        }    }    /**     * 刷新消息队列     */    void flushMessageQueue() {        if (Thread.currentThread() == Looper.getMainLooper().getThread()) {            loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {                @Override                public void onCallBack(String data) {                    // deserializeMessage 反序列化消息                    List<Message> list = null;                    try {                        list = Message.toArrayList(data);                    } catch (Exception e) {                        e.printStackTrace();                        return;                    }                    if (list == null || list.size() == 0) {                        return;                    }                    for (int i = 0; i < list.size(); i++) {                        Message m = list.get(i);                        String responseId = m.getResponseId();                        // 是否是response  CallBackFunction                        if (!TextUtils.isEmpty(responseId)) {                            CallBackFunction function = responseCallbacks.get(responseId);                            String responseData = m.getResponseData();                            function.onCallBack(responseData);                            responseCallbacks.remove(responseId);                        } else {                            CallBackFunction responseFunction = null;                            // if had callbackId 如果有回调Id                            final String callbackId = m.getCallbackId();                            if (!TextUtils.isEmpty(callbackId)) {                                responseFunction = new CallBackFunction() {                                    @Override                                    public void onCallBack(String data) {                                        Message responseMsg = new Message();                                        responseMsg.setResponseId(callbackId);                                        responseMsg.setResponseData(data);                                        queueMessage(responseMsg);                                    }                                };                            } else {                                responseFunction = new CallBackFunction() {                                    @Override                                    public void onCallBack(String data) {                                        // do nothing                                    }                                };                            }                            // BridgeHandler执行                            BridgeHandler handler;                            if (!TextUtils.isEmpty(m.getHandlerName())) {                                handler = messageHandlers.get(m.getHandlerName());                            } else {                                handler = defaultHandler;                            }                            if (handler != null){                                handler.handler(m.getData(), responseFunction);                            }
                

注:本文内容来自互联网,旨在为开发者提供分享、交流的平台。如有涉及文章版权等事宜,请你联系站长进行处理。