javascript落成鼠标跟随特效,谈谈Java接口与贯彻的分别以及隐藏达成

By admin in 4858美高梅 on 2019年4月20日

兑现效益:

说明

JSBridge实现示例

说明

JSBridge完结示例

4858美高梅,一. what ?
对此2个框架来讲, 用户只须要掌握那个框架的器重零部件和接口就行了,
不要对外宣布太多的细节. 因为用户观察的东西太多反而产生了吸引.
对于用户来讲, 只要调用二个艺术就帮自个儿做到自己想要的那三个复杂作用,
那样最佳可是了. 接口和兑现分开也许说只对外发布用户要利用的接口,
而其落成则对用户隐藏起来. 那是3个框架应该做的事体,
也是Java的1个爱惜特点 —— 封装.
轻松的来说接口和贯彻的送别正是把接口已兑现分开, 尽量减弱两者之间的信赖性,
以方便移植和修改. 那么隐藏达成又怎么说吧? 前面早已说了,
叁个框架要达成的是不择花招不要公布完结, 只发表接口.
由此就需求对促成实行包装并隐藏. 那样说有个别不切合实际, 你或者有点不知所云.
下边小编将说说为什要开始展览接口和实现的分离对实现方式进行隐藏
以及怎么得以完结它们.

4858美高梅 1

目录

  • 前言
    • 参考来源
    • 楔子
  • JS实现部分
    • 说明
    • 实现
  • Android落成部分
    • 说明
    • JSBridge类
      实现
    • Callback类
      实现
    • Webview容器关键代码
      达成
    • API
      类实现
  • iOS完结部分
    • 说明
    • WebViewJavascriptBridgeBase
      实现
    • WebViewJavascriptBridge
      实现
    • Webview容器关键代码
      落成

目录

  • 前言
    • 参照来源
    • 楔子
  • JS达成部分
    • 说明
    • 实现
  • Android落成部分
    • 说明
    • JSBridge类
      实现
    • Callback类
      实现
    • Webview容器关键代码
      完毕
    • API
      类实现
  • iOS落成部分
    • 说明
    • WebViewJavascriptBridgeBase
      实现
    • WebViewJavascriptBridge
      实现
    • javascript落成鼠标跟随特效,谈谈Java接口与贯彻的分别以及隐藏达成。Webview容器关键代码
      落成

二. why ?
在《在Android上使用SPI机制》一文中早就说过有关接口和促成的辞行和动态转变落成的主题素材,
不过接口的完毕并未有对外隐藏, 用户能够平昔调用接口的贯彻, 而不利用接口.
那样编写的代码并不是面向接口编制程序, 而是硬编码. 假诺要转变实现,
这将不胜麻烦. 为了不让用户观看达成,
只须求将落到实处类成为包私有的类用反射起头化.
分离接口和得以达成以及隐藏完毕细节好处多多, 上边罗列多少个:

 

前言

前言

  1. 面向接口编制程序, 方便改动实现.
  2. 隐蔽完成细节, 收缩对外接口和类.
  3. 减掉接口和促成直接的竞相依赖.
  4. 封装, 高内聚.
  5. ……

兑今世码:

参照来源

前人栽树,后台乘凉,本文参考了以下来源

  • Hybrid 应用软件架构划设想计思路
  • marcuswestin/WebViewJavascriptBridge

参考来源

前人栽树,后台乘凉,本文参考了以下来源

  • Hybrid 应用程式架构划设想计思路
  • marcuswestin/WebViewJavascriptBridge

三. how ?
上面看看哪些得以落成:

<!DOCTYPE html>
<html>
<head>
 <title>鼠标跟随</title>
 <meta charset="utf-8">
 <style type="text/css">
  body{
   height: 5000px;
  }
  div{
   position: absolute;
   border: 1px solid #ccc;
   cursor: pointer;
   width: 100px;
   height: 100px;
   background-color: #03c03c;
   opacity: 0.8;
  }
 </style>
</head>
<body>
<div></div>
<script type="text/javascript" src="myScroll.js"></script>
<script type="text/javascript">
 // 鼠标跟随
 // pageY和pageX的ie67ie兼容写法
 // 在页面的位置 = 看得见的 + 看不见的
 // pageY/pageX = event.clientY/clientX + scroll().top/scroll().left
 var obj = document.getElementsByTagName("div")[0];
 var timer = null;
 var targetX = 0,
  targetY = 0,
  leaderX = 0,
  leaderY = 0;

 // 给整个文档绑定点击事件获取鼠标位置
 document.onclick = function(event){
  // 兼容获取事件对象
  event = event || window.event;
  // 鼠标在页面的位置 = 被卷去的部分 + 可视区域部分
  var pageY = event.pageY || scroll().top + event.clientY;
  var pageX = event.pageX || scroll().left + event.clientX;
  targetY = pageY - obj.offsetHeight/2;
  targetX = pageX - obj.offsetWidth/2;

  // 清除定时器
  clearInterval(timer);
  timer = setInterval(function(){
   // X,先左右,后上下
   // 为盒子的位置获取值
   leaderX = obj.offsetLeft;
   // 获取步长
   var stepX = (targetX - leaderX)/10;
   // 二次处理步长
   stepX = stepX > 0 ? Math.ceil(stepX) : Math.floor(stepX);
   leaderX = leaderX + stepX;
   // 赋值
   obj.style.left = leaderX + "px";
   // Y
   leaderY = obj.offsetTop;
   var stepY = (targetY - leaderY)/10;
   stepY = stepY > 0 ? Math.ceil(stepY) : Math.floor(stepY);
   leaderY = leaderY + stepY;
   obj.style.top = leaderY + "px";
  }, 30);
 }
</script>
</body>
</html>

楔子

正文介绍JSBridge的1体化兑现,包罗JS部分,Android原生,iOS原生部分

楔子

本文介绍JSBridge的全部兑现,包蕴JS部分,Android原生,iOS原生部分

  • (一) 定义接口

  myScroll.js

JS完结部分

JS落成部分

function scroll() {  // 开始封装自己的scrollTop
    if(window.pageYOffset !== undefined) {  // ie9+ 高版本浏览器
        // 因为 window.pageYOffset 默认的是0,所以需要判断
        return {
            left: window.pageXOffset,
            top: window.pageYOffset
        }
    }
    else if(document.compatMode === "CSS1Compat") {// 标准浏览器,来判断有没有声明DTD
        return {
            left: document.documentElement.scrollLeft,
            top: document.documentElement.scrollTop
        }
    }
    return {// 未声明 DTD
        left: document.body.scrollLeft,
        top: document.body.scrollTop
    }
}

说明

这是一份剔除了作业之后的JSbridge达成代码(JS部分)。JS落成代码就一套

说明

那是壹份剔除了作业之后的JSbridge达成代码(JS部分)。JS落成代码就1套

public interface ImageLoader {

    /**
     * 初始化ImageLoader
     * @param appContext ApplicatonContext
     */
    void init(@NonNull Context appContext);

    /**
     * 展示图片
     * @param targetView
     * @param uri
     * @param listener
     */
    void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener);

    /**
     * 取消图片展示
     * @param targetView
     */
    void cancelDisplay(ImageView targetView);

    /**
     * 销毁ImageLoader, 回收资源
     */
    void destroy();

}

  

实现

落成代码如下

(function() {
    (function() {
        var hasOwnProperty = Object.prototype.hasOwnProperty;
        var JSBridge = window.JSBridge || (window.JSBridge = {});
        //jsbridge协议定义的名称
        var CUSTOM_PROTOCOL_SCHEME = 'CustomJSBridge';
        //最外层的api名称
        var API_Name = 'namespace_bridge';
        //进行url scheme传值的iframe
        var messagingIframe = document.createElement('iframe');
        messagingIframe.style.display = 'none';
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
        document.documentElement.appendChild(messagingIframe);

        //定义的回调函数集合,在原生调用完对应的方法后,会执行对应的回调函数id
        var responseCallbacks = {};
        //唯一id,用来确保每一个回调函数的唯一性
        var uniqueId = 1;
        //本地注册的方法集合,原生只能调用本地注册的方法,否则会提示错误
        var messageHandlers = {};
        //当原生调用H5注册的方法时,通过回调来调用(也就是变为了异步执行,加强安全性)
        var dispatchMessagesWithTimeoutSafety = true;
        //本地运行中的方法队列
        var sendMessageQueue = [];

        //实际暴露给原生调用的对象
        var Inner = {
            /**
             * @description 注册本地JS方法通过JSBridge给原生调用
             * 我们规定,原生必须通过JSBridge来调用H5的方法
             * 注意,这里一般对本地函数有一些要求,要求第一个参数是data,第二个参数是callback
             * @param {String} handlerName 方法名
             * @param {Function} handler 对应的方法
             */
            registerHandler: function(handlerName, handler) {
                messageHandlers[handlerName] = handler;
            },
            /**
             * @description 调用原生开放的方法
             * @param {String} handlerName 方法名
             * @param {JSON} data 参数
             * @param {Function} callback 回调函数
             */
            callHandler: function(handlerName, data, callback) {
                //如果没有 data
                if(arguments.length == 3 && typeof data == 'function') {
                    callback = data;
                    data = null;
                }
                _doSend({
                    handlerName: handlerName,
                    data: data
                }, callback);
            },
            /**
             * iOS专用
             * @description 当本地调用了callHandler之后,实际是调用了通用的scheme,通知原生
             * 然后原生通过调用这个方法来获知当前正在调用的方法队列
             */
            _fetchQueue: function() {
                var messageQueueString = JSON.stringify(sendMessageQueue);
                sendMessageQueue = [];
                return messageQueueString;
            },
            /**
             * @description 原生调用H5页面注册的方法,或者调用回调方法
             * @param {String} messageJSON 对应的方法的详情,需要手动转为json
             */
            _handleMessageFromNative: function(messageJSON) {
                setTimeout(_doDispatchMessageFromNative);
                /**
                 * @description 处理原生过来的方法
                 */
                function _doDispatchMessageFromNative() {
                    var message;
                    try {
                        message = JSON.parse(messageJSON);
                    } catch(e) {
                        //TODO handle the exception
                        console.error("原生调用H5方法出错,传入参数错误");
                        return;
                    }

                    //回调函数
                    var responseCallback;
                    if(message.responseId) {
                        //这里规定,原生执行方法完毕后准备通知h5执行回调时,回调函数id是responseId
                        responseCallback = responseCallbacks[message.responseId];
                        if(!responseCallback) {
                            return;
                        }
                        //执行本地的回调函数
                        responseCallback(message.responseData);
                        delete responseCallbacks[message.responseId];
                    } else {
                        //否则,代表原生主动执行h5本地的函数
                        if(message.callbackId) {
                            //先判断是否需要本地H5执行回调函数
                            //如果需要本地函数执行回调通知原生,那么在本地注册回调函数,然后再调用原生
                            //回调数据有h5函数执行完毕后传入
                            var callbackResponseId = message.callbackId;
                            responseCallback = function(responseData) {
                                //默认是调用EJS api上面的函数
                                //然后接下来原生知道scheme被调用后主动获取这个信息
                                //所以原生这时候应该会进行判断,判断对于函数是否成功执行,并接收数据
                                //这时候通讯完毕(由于h5不会对回调添加回调,所以接下来没有通信了)
                                _doSend({
                                    handlerName: message.handlerName,
                                    responseId: callbackResponseId,
                                    responseData: responseData
                                });
                            };
                        }

                        //从本地注册的函数中获取
                        var handler = messageHandlers[message.handlerName];
                        if(!handler) {
                            //本地没有注册这个函数
                        } else {
                            //执行本地函数,按照要求传入数据和回调
                            handler(message.data, responseCallback);
                        }
                    }
                }
            }

        };
        /**
         * @description JS调用原生方法前,会先send到这里进行处理
         * @param {JSON} message 调用的方法详情,包括方法名,参数
         * @param {Function} responseCallback 调用完方法后的回调
         */
        function _doSend(message, responseCallback) {
            if(responseCallback) {
                //取到一个唯一的callbackid
                var callbackId = Util.getCallbackId();
                //回调函数添加到集合中
                responseCallbacks[callbackId] = responseCallback;
                //方法的详情添加回调函数的关键标识
                message['callbackId'] = callbackId;
            }
            var uri;
            //android中,可以通过onJsPrompt或者截取Url访问都行
            var ua = navigator.userAgent;
            if(ua.match(/(iPhone\sOS)\s([\d_]+)/)||ua.match(/(iPad).*OS\s([\d_]+)/)) {
                //ios中,通过截取客户端url访问
                //因为ios可以不暴露scheme,而是由原生手动获取
                //正在调用的方法详情添加进入消息队列中,原生会主动获取
                sendMessageQueue.push(message);
                uri = Util.getUri();
            }else{
                //android中兼容处理,将所有的参数一起拼接到url中
                uri = Util.getUri(message);
            }
            //获取 触发方法的url scheme
            //采用iframe跳转scheme的方法
            messagingIframe.src = uri;
        }

        var Util = {
            getCallbackId: function() {
                //如果无法解析端口,可以换为Math.floor(Math.random() * (1 << 30));
                return 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            },
            //获取url scheme
            //第二个参数是兼容android中的做法
            //android中由于原生不能获取JS函数的返回值,所以得通过协议传输
            getUri: function(message) {
                var uri = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
                if(message) {
                    //回调id作为端口存在
                    var callbackId, method, params;
                    if(message.callbackId) {
                        //第一种:h5主动调用原生
                        callbackId = message.callbackId;
                        method = message.handlerName;
                        params = message.data;
                    } else if(message.responseId) {
                        //第二种:原生调用h5后,h5回调
                        //这种情况下需要原生自行分析传过去的port是否是它定义的回调
                        callbackId = message.responseId;
                        method = message.handlerName;
                        params = message.responseData;
                    }
                    //参数转为字符串
                    params = this.getParam(params);
                    //uri 补充
                    uri += ':' + callbackId + '/' + method + '?' + params;
                }

                return uri;
            },
            getParam: function(obj) {
                if(obj && typeof obj === 'object') {
                    return JSON.stringify(obj);
                } else {
                    return obj || '';
                }
            }
        };
        for(var key in Inner) {
            if(!hasOwnProperty.call(JSBridge, key)) {
                JSBridge[key] = Inner[key];
            }
        }

    })();

    //注册一个测试函数
    JSBridge.registerHandler('testH5Func', function(data, callback) {
        alert('测试函数接收到数据:' + JSON.stringify(data));
        callback && callback('测试回传数据...');
    });
    /*
     ***************************API********************************************
     * 开放给外界调用的api
     * */
    window.jsapi = {};
    /**
     ***app 模块 
     * 一些特殊操作
     */
    jsapi.app = {
        /**
         * @description 测试函数
         */
        testNativeFunc: function() {
            //调用一个测试函数
            JSBridge.callHandler('testNativeFunc', {}, function(res) {
                callback && callback(res);
            });
        }
    };
})();               

实现

兑当代码如下

(function() {
    (function() {
        var hasOwnProperty = Object.prototype.hasOwnProperty;
        var JSBridge = window.JSBridge || (window.JSBridge = {});
        //jsbridge协议定义的名称
        var CUSTOM_PROTOCOL_SCHEME = 'CustomJSBridge';
        //最外层的api名称
        var API_Name = 'namespace_bridge';
        //进行url scheme传值的iframe
        var messagingIframe = document.createElement('iframe');
        messagingIframe.style.display = 'none';
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
        document.documentElement.appendChild(messagingIframe);

        //定义的回调函数集合,在原生调用完对应的方法后,会执行对应的回调函数id
        var responseCallbacks = {};
        //唯一id,用来确保每一个回调函数的唯一性
        var uniqueId = 1;
        //本地注册的方法集合,原生只能调用本地注册的方法,否则会提示错误
        var messageHandlers = {};
        //当原生调用H5注册的方法时,通过回调来调用(也就是变为了异步执行,加强安全性)
        var dispatchMessagesWithTimeoutSafety = true;
        //本地运行中的方法队列
        var sendMessageQueue = [];

        //实际暴露给原生调用的对象
        var Inner = {
            /**
             * @description 注册本地JS方法通过JSBridge给原生调用
             * 我们规定,原生必须通过JSBridge来调用H5的方法
             * 注意,这里一般对本地函数有一些要求,要求第一个参数是data,第二个参数是callback
             * @param {String} handlerName 方法名
             * @param {Function} handler 对应的方法
             */
            registerHandler: function(handlerName, handler) {
                messageHandlers[handlerName] = handler;
            },
            /**
             * @description 调用原生开放的方法
             * @param {String} handlerName 方法名
             * @param {JSON} data 参数
             * @param {Function} callback 回调函数
             */
            callHandler: function(handlerName, data, callback) {
                //如果没有 data
                if(arguments.length == 3 && typeof data == 'function') {
                    callback = data;
                    data = null;
                }
                _doSend({
                    handlerName: handlerName,
                    data: data
                }, callback);
            },
            /**
             * iOS专用
             * @description 当本地调用了callHandler之后,实际是调用了通用的scheme,通知原生
             * 然后原生通过调用这个方法来获知当前正在调用的方法队列
             */
            _fetchQueue: function() {
                var messageQueueString = JSON.stringify(sendMessageQueue);
                sendMessageQueue = [];
                return messageQueueString;
            },
            /**
             * @description 原生调用H5页面注册的方法,或者调用回调方法
             * @param {String} messageJSON 对应的方法的详情,需要手动转为json
             */
            _handleMessageFromNative: function(messageJSON) {
                setTimeout(_doDispatchMessageFromNative);
                /**
                 * @description 处理原生过来的方法
                 */
                function _doDispatchMessageFromNative() {
                    var message;
                    try {
                        message = JSON.parse(messageJSON);
                    } catch(e) {
                        //TODO handle the exception
                        console.error("原生调用H5方法出错,传入参数错误");
                        return;
                    }

                    //回调函数
                    var responseCallback;
                    if(message.responseId) {
                        //这里规定,原生执行方法完毕后准备通知h5执行回调时,回调函数id是responseId
                        responseCallback = responseCallbacks[message.responseId];
                        if(!responseCallback) {
                            return;
                        }
                        //执行本地的回调函数
                        responseCallback(message.responseData);
                        delete responseCallbacks[message.responseId];
                    } else {
                        //否则,代表原生主动执行h5本地的函数
                        if(message.callbackId) {
                            //先判断是否需要本地H5执行回调函数
                            //如果需要本地函数执行回调通知原生,那么在本地注册回调函数,然后再调用原生
                            //回调数据有h5函数执行完毕后传入
                            var callbackResponseId = message.callbackId;
                            responseCallback = function(responseData) {
                                //默认是调用EJS api上面的函数
                                //然后接下来原生知道scheme被调用后主动获取这个信息
                                //所以原生这时候应该会进行判断,判断对于函数是否成功执行,并接收数据
                                //这时候通讯完毕(由于h5不会对回调添加回调,所以接下来没有通信了)
                                _doSend({
                                    handlerName: message.handlerName,
                                    responseId: callbackResponseId,
                                    responseData: responseData
                                });
                            };
                        }

                        //从本地注册的函数中获取
                        var handler = messageHandlers[message.handlerName];
                        if(!handler) {
                            //本地没有注册这个函数
                        } else {
                            //执行本地函数,按照要求传入数据和回调
                            handler(message.data, responseCallback);
                        }
                    }
                }
            }

        };
        /**
         * @description JS调用原生方法前,会先send到这里进行处理
         * @param {JSON} message 调用的方法详情,包括方法名,参数
         * @param {Function} responseCallback 调用完方法后的回调
         */
        function _doSend(message, responseCallback) {
            if(responseCallback) {
                //取到一个唯一的callbackid
                var callbackId = Util.getCallbackId();
                //回调函数添加到集合中
                responseCallbacks[callbackId] = responseCallback;
                //方法的详情添加回调函数的关键标识
                message['callbackId'] = callbackId;
            }
            var uri;
            //android中,可以通过onJsPrompt或者截取Url访问都行
            var ua = navigator.userAgent;
            if(ua.match(/(iPhone\sOS)\s([\d_]+)/)||ua.match(/(iPad).*OS\s([\d_]+)/)) {
                //ios中,通过截取客户端url访问
                //因为ios可以不暴露scheme,而是由原生手动获取
                //正在调用的方法详情添加进入消息队列中,原生会主动获取
                sendMessageQueue.push(message);
                uri = Util.getUri();
            }else{
                //android中兼容处理,将所有的参数一起拼接到url中
                uri = Util.getUri(message);
            }
            //获取 触发方法的url scheme
            //采用iframe跳转scheme的方法
            messagingIframe.src = uri;
        }

        var Util = {
            getCallbackId: function() {
                //如果无法解析端口,可以换为Math.floor(Math.random() * (1 << 30));
                return 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            },
            //获取url scheme
            //第二个参数是兼容android中的做法
            //android中由于原生不能获取JS函数的返回值,所以得通过协议传输
            getUri: function(message) {
                var uri = CUSTOM_PROTOCOL_SCHEME + '://' + API_Name;
                if(message) {
                    //回调id作为端口存在
                    var callbackId, method, params;
                    if(message.callbackId) {
                        //第一种:h5主动调用原生
                        callbackId = message.callbackId;
                        method = message.handlerName;
                        params = message.data;
                    } else if(message.responseId) {
                        //第二种:原生调用h5后,h5回调
                        //这种情况下需要原生自行分析传过去的port是否是它定义的回调
                        callbackId = message.responseId;
                        method = message.handlerName;
                        params = message.responseData;
                    }
                    //参数转为字符串
                    params = this.getParam(params);
                    //uri 补充
                    uri += ':' + callbackId + '/' + method + '?' + params;
                }

                return uri;
            },
            getParam: function(obj) {
                if(obj && typeof obj === 'object') {
                    return JSON.stringify(obj);
                } else {
                    return obj || '';
                }
            }
        };
        for(var key in Inner) {
            if(!hasOwnProperty.call(JSBridge, key)) {
                JSBridge[key] = Inner[key];
            }
        }

    })();

    //注册一个测试函数
    JSBridge.registerHandler('testH5Func', function(data, callback) {
        alert('测试函数接收到数据:' + JSON.stringify(data));
        callback && callback('测试回传数据...');
    });
    /*
     ***************************API********************************************
     * 开放给外界调用的api
     * */
    window.jsapi = {};
    /**
     ***app 模块 
     * 一些特殊操作
     */
    jsapi.app = {
        /**
         * @description 测试函数
         */
        testNativeFunc: function() {
            //调用一个测试函数
            JSBridge.callHandler('testNativeFunc', {}, function(res) {
                callback && callback(res);
            });
        }
    };
})();               
  • (2) 达成接口 (完毕类要定义成包私有的, 即未有修饰符)
    FrescoImageLoader.java文件:

 

Android落成部分

Android完毕部分

说明

这是Android原生中配套的JSBridge实今世码。Android的落到实处相相比较JS复杂,包罗四个部分

说明

那是Android原生中配套的JSBridge落成代码。Android的兑现绝相比JS复杂,包蕴四个部分

class FrescoImageLoader implements ImageLoader {

    private Context mAppContext;

    @Override
    public void init(@NonNull Context appContext) {

        if(Fresco.hasBeenInitialized()) return;

        // hold appContext
        mAppContext = appContext;

        // init fresco
        OkHttpClient client = new OkHttpClient.Builder()
                .addNetworkInterceptor(chain -> {
                    DevUtil.d("ImageLoader", "request-url: " + chain.request().url().toString());
                    return chain.proceed(chain.request());
                })
                .build();
        ImagePipelineConfig config = OkHttpImagePipelineConfigFactory
                .newBuilder(appContext, client)
                .build();
        Fresco.initialize(appContext, config);
    }

    @Override
    public void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener) {
        // Fresco
        if (targetView instanceof DraweeView) {
            DraweeView realView = (DraweeView) targetView;
            realView.setController(getDraweeController(realView, uri, listener));
            return;
        }

        // Generic ImageView
        targetView.setImageURI(uri);
    }

    private DraweeController getDraweeController(DraweeView targetView, Uri uri, LoadListener listener) {
        DraweeController controller = Fresco.newDraweeControllerBuilder()
                .setOldController(targetView.getController())
                .setUri(uri)
                .setControllerListener(listener == null ? null : new BaseControllerListener<ImageInfo>() {
                    @Override
                    public void onFailure(String id, Throwable throwable) {
                        super.onFailure(id, throwable);
                        if (listener != null) {
                            listener.onFailed(targetView);
                        }
                    }

                    @Override
                    public void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {
                        super.onFinalImageSet(id, imageInfo, animatable);
                        if (imageInfo instanceof CloseableBitmap) {
                            CloseableBitmap image = (CloseableBitmap) imageInfo;
                            Bitmap resultBitmap = image.getUnderlyingBitmap();
                            if (listener != null) {
                                listener.onSuccess(targetView, resultBitmap);
                            }
                        }
                    }
                })
                .build();
        return controller;
    }

    @Override
    public void cancelDisplay(ImageView targetView) {

    }

    @Override
    public void destroy() {
        Fresco.shutDown();
    }
}

JSBridge类实现

贯彻代码如下

public class JSBridge {
    private static Map<String, HashMap<String, Method>> exposedMethods = new HashMap<>();

    //原生注册API方法
    public static void register(String exposedName, Class<? extends IBridge> clazz) {
        if (!exposedMethods.containsKey(exposedName)) {
            try {
                exposedMethods.put(exposedName, getAllMethod(clazz));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //得到所有的注册方法
    private static HashMap<String, Method> getAllMethod(Class injectedCls) throws Exception {
        HashMap<String, Method> mMethodsMap = new HashMap<>();
        Method[] methods = injectedCls.getDeclaredMethods();
        for (Method method : methods) {
            String name;
            if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null) {
                continue;
            }
            Class[] parameters = method.getParameterTypes();
            if (null != parameters && parameters.length == 4) {
                if (parameters[0] == BaseWebLoader.class && parameters[1] == WebView.class && parameters[2] == JSONObject.class && parameters[3] == Callback.class) {
                    mMethodsMap.put(name, method);
                }
            }
        }
        return mMethodsMap;
    }

    //调用Hava中的方法
    //其中BaseWebLoader是JSBridge的webview容器(二次封装)
    //执行完方法后,如果又回到,自动就会调用
    public static String callJava(BaseWebLoader webLoader,WebView webView, String uriString) {
        String methodName = "";
        String className = "";
        String param = "{}";
        String port = "";
        if (!TextUtils.isEmpty(uriString) && uriString.startsWith("EpointJSBridge")) {
            Uri uri = Uri.parse(uriString);
            className = uri.getHost();
            param = uri.getQuery();
            port = uri.getPort() + "";
            String path = uri.getPath();
            if (!TextUtils.isEmpty(path)) {
                methodName = path.replace("/", "");
            }
        }


        if (exposedMethods.containsKey(className)) {
            HashMap<String, Method> methodHashMap = exposedMethods.get(className);

            if (methodHashMap != null && methodHashMap.size() != 0 && methodHashMap.containsKey(methodName)) {
                Method method = methodHashMap.get(methodName);
                if (method != null) {
                    try {
                        method.invoke(null,webLoader, webView, new JSONObject(param), new Callback(webView, port));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }
}   

以此类的功用是原生定义一些爆出的api

JSBridge类实现

贯彻代码如下

public class JSBridge {
    private static Map<String, HashMap<String, Method>> exposedMethods = new HashMap<>();

    //原生注册API方法
    public static void register(String exposedName, Class<? extends IBridge> clazz) {
        if (!exposedMethods.containsKey(exposedName)) {
            try {
                exposedMethods.put(exposedName, getAllMethod(clazz));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //得到所有的注册方法
    private static HashMap<String, Method> getAllMethod(Class injectedCls) throws Exception {
        HashMap<String, Method> mMethodsMap = new HashMap<>();
        Method[] methods = injectedCls.getDeclaredMethods();
        for (Method method : methods) {
            String name;
            if (method.getModifiers() != (Modifier.PUBLIC | Modifier.STATIC) || (name = method.getName()) == null) {
                continue;
            }
            Class[] parameters = method.getParameterTypes();
            if (null != parameters && parameters.length == 4) {
                if (parameters[0] == BaseWebLoader.class && parameters[1] == WebView.class && parameters[2] == JSONObject.class && parameters[3] == Callback.class) {
                    mMethodsMap.put(name, method);
                }
            }
        }
        return mMethodsMap;
    }

    //调用Hava中的方法
    //其中BaseWebLoader是JSBridge的webview容器(二次封装)
    //执行完方法后,如果又回到,自动就会调用
    public static String callJava(BaseWebLoader webLoader,WebView webView, String uriString) {
        String methodName = "";
        String className = "";
        String param = "{}";
        String port = "";
        if (!TextUtils.isEmpty(uriString) && uriString.startsWith("EpointJSBridge")) {
            Uri uri = Uri.parse(uriString);
            className = uri.getHost();
            param = uri.getQuery();
            port = uri.getPort() + "";
            String path = uri.getPath();
            if (!TextUtils.isEmpty(path)) {
                methodName = path.replace("/", "");
            }
        }


        if (exposedMethods.containsKey(className)) {
            HashMap<String, Method> methodHashMap = exposedMethods.get(className);

            if (methodHashMap != null && methodHashMap.size() != 0 && methodHashMap.containsKey(methodName)) {
                Method method = methodHashMap.get(methodName);
                if (method != null) {
                    try {
                        method.invoke(null,webLoader, webView, new JSONObject(param), new Callback(webView, port));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }
}   

本条类的效应是原生定义一些暴露的api

UILImageLoader.java文件

Callback类实现

贯彻代码如下

public class Callback {
    private static Handler mHandler = new Handler(Looper.getMainLooper());
    private static final String CALLBACK_JS_FORMAT = "javascript:JSBridge._handleMessageFromNative(%s);";
    private String mPort;
    private WeakReference<WebView> mWebViewRef;

    public Callback(WebView view, String port) {
        mWebViewRef = new WeakReference<>(view);
        mPort = port;
    }


    public void apply(JSONObject jsonObject) throws JSONException {
        JSONObject object = new JSONObject();
        object.put("responseId", mPort);
        object.putOpt("responseData", jsonObject);
        final String execJs = String.format(CALLBACK_JS_FORMAT, String.valueOf(object));
        //如果activity已经关闭则不回调
        if (mWebViewRef != null && mWebViewRef.get() != null && !((BaseWebLoader) mWebViewRef.get().getContext()).getActivity().isFinishing()) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mWebViewRef.get().loadUrl(execJs);
                }
            });
        }
    }
}

那些类的效能是,定义原生中的回调函数

Callback类实现

贯彻代码如下

public class Callback {
    private static Handler mHandler = new Handler(Looper.getMainLooper());
    private static final String CALLBACK_JS_FORMAT = "javascript:JSBridge._handleMessageFromNative(%s);";
    private String mPort;
    private WeakReference<WebView> mWebViewRef;

    public Callback(WebView view, String port) {
        mWebViewRef = new WeakReference<>(view);
        mPort = port;
    }


    public void apply(JSONObject jsonObject) throws JSONException {
        JSONObject object = new JSONObject();
        object.put("responseId", mPort);
        object.putOpt("responseData", jsonObject);
        final String execJs = String.format(CALLBACK_JS_FORMAT, String.valueOf(object));
        //如果activity已经关闭则不回调
        if (mWebViewRef != null && mWebViewRef.get() != null && !((BaseWebLoader) mWebViewRef.get().getContext()).getActivity().isFinishing()) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    mWebViewRef.get().loadUrl(execJs);
                }
            });
        }
    }
}

其一类的功效是,定义原生中的回调函数

class UILImageLoader implements ImageLoader {

    private com.nostra13.universalimageloader.core.ImageLoader mImpl;

    @Override
    public void init(@NonNull Context appContext) {
        mImpl = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
    }

    @Override
    public void displayImage(@NonNull ImageView targetView, @NonNull Uri uri, @Nullable LoadListener listener) {
        com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(uri.toString(), targetView);
    }

    @Override
    public void cancelDisplay(ImageView targetView) {
        mImpl.cancelDisplayTask(targetView);
    }

    @Override
    public void destroy() {
        mImpl.destroy();
    }

}

Webview容器关键代码落成

金玉满堂代码如下

Webview容器关键代码落成

得以达成代码如下

别的省略…………….

注册api方法

//定义api集合
JSBridge.register("namespace_bridge",BridgeImpl.class);             

注册api方法

//定义api集合
JSBridge.register("namespace_bridge",BridgeImpl.class);             
  • (3) 为落成类定义先河化工厂类

抓获url scheme并实行格局的代码

public boolean shouldOverrideUrlLoading(WebView view, String url){
    //读取到url后通过callJava分析调用
    JSBridge.callJava(BaseWebLoader.this,view,url);

    //如果返回false,则WebView处理链接url,如果返回true,代表WebView根据程序来执行url
    return true;
}

内部的严重性代码是登记函数,捕获url,施行措施等

抓获url scheme并推行措施的代码

public boolean shouldOverrideUrlLoading(WebView view, String url){
    //读取到url后通过callJava分析调用
    JSBridge.callJava(BaseWebLoader.this,view,url);

    //如果返回false,则WebView处理链接url,如果返回true,代表WebView根据程序来执行url
    return true;
}

里面包车型地铁首要代码是注册函数,捕获url,实践形式等

API 类实现

福寿绵绵代码如下

public class BridgeImpl implements IBridge {
    /**
     * 测试原生方法
     */
    public static void testNativeFunc(final BaseWebLoader webLoader, WebView wv, JSONObject param, final Callback callback) {
       //可以这样获取参数 param.optString(键值);
        //在一个新的线程内执行
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //执行一些自己的内容
                    JSONObject object = new JSONObject();
                    //添加测试信息
                    object.put("test", "test");
                    //执行回调
                    callback.apply(getJSONObject(1, "", object));
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

其一类是局地api的现实贯彻,webview里面就是注册了那一个对应的api

API 类实现

达成代码如下

public class BridgeImpl implements IBridge {
    /**
     * 测试原生方法
     */
    public static void testNativeFunc(final BaseWebLoader webLoader, WebView wv, JSONObject param, final Callback callback) {
       //可以这样获取参数 param.optString(键值);
        //在一个新的线程内执行
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //执行一些自己的内容
                    JSONObject object = new JSONObject();
                    //添加测试信息
                    object.put("test", "test");
                    //执行回调
                    callback.apply(getJSONObject(1, "", object));
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

本条类是局地api的切切实实得以完结,webview里面就是注册了这么些对应的api

class ImageLoaderFactory {

    private static ImageLoader mImageLoader;

    private ImageLoaderFactory() {
        //no instance
    }

    /**
     * @return
     */
    public static ImageLoader createImageLoader(String implClass) {
        if (mImageLoader == null) {
            mImageLoader = createImageLoaderWithClassName(implClass);
        }
        return mImageLoader;
    }

    /**
     * 此处使用类反射, 所有implClass都不能混淆, 类名必须keep:  {@code -keep class a.b.c.ImplClass}
     * @param implClass 实现类有: FrescoImageLoader, GlideImageLoader, PicassoImageLoader, UILImageLoader
     * @return
     */
    private static ImageLoader createImageLoaderWithClassName(String implClass) {
        try {
            Class klass = Class.forName(implClass);
            Constructor constructor = klass.getDeclaredConstructor();
            if (constructor == null) {
                throw new RuntimeException(implClass + " 的实现类必须有一个无参构造方法 !");
            }
            boolean isAccessible = constructor.isAccessible();
            constructor.setAccessible(true);
            Object obj = constructor.newInstance();
            constructor.setAccessible(isAccessible);
            if ( !(obj instanceof ImageLoader) ) {
                throw new RuntimeException(implClass + "必须实现" + ImageLoader.class.getName() + "接口");
            }

            ImageLoader imageLoader = (ImageLoader) obj;
            return imageLoader;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

iOS达成部分

iOS实现部分

  • (4) 要使用ImageLoader, 直接用工厂类创设就可以,
    大家只需求驾驭达成类的称谓, 其余细节都不须要知道.
    达成类都以莫斯中国科学技术大学学内聚, 外部完全不精通其内部的逻辑.
    外部只要求调用接口的主意达成业务逻辑就可以.
    下面是ImageLoader及其相关兑现的欧洲经济共同体结构:

说明

那是iOS原生中配套的JSBridge完成代码。iOS中代码是基于UIWebview的,来源于三个github上的开源项目,地址
marcuswestin/WebViewJavascriptBridge

说明

那是iOS原生中配套的JSBridge实现代码。iOS中代码是基于UIWebview的,来源于多少个github上的开源项目,地址
marcuswestin/WebViewJavascriptBridge

接口与贯彻分离, 隐藏落成.png

WebViewJavascriptBridgeBase 实现

福寿绵绵代码如下

@implementation WebViewJavascriptBridgeBase {
    __weak id _webViewDelegate;
    long _uniqueId;
}

static bool logging = false;
static int logMaxLength = 500;

+ (void)enableLogging { logging = true; }
+ (void)setLogMaxLength:(int)length { logMaxLength = length;}

-(id)init {
    self = [super init];
    self.messageHandlers = [NSMutableDictionary dictionary];
    self.startupMessageQueue = [NSMutableArray array];
    self.responseCallbacks = [NSMutableDictionary dictionary];
    _uniqueId = 0;
    return(self);
}

- (void)dealloc {
    self.startupMessageQueue = nil;
    self.responseCallbacks = nil;
    self.messageHandlers = nil;
}

- (void)reset {
    self.startupMessageQueue = [NSMutableArray array];
    self.responseCallbacks = [NSMutableDictionary dictionary];
    _uniqueId = 0;
}

- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
    NSMutableDictionary* message = [NSMutableDictionary dictionary];

    if (data) {
        message[@"data"] = data;
    }

    if (responseCallback) {
        NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
        self.responseCallbacks[callbackId] = [responseCallback copy];
        message[@"callbackId"] = callbackId;
    }

    if (handlerName) {
        message[@"handlerName"] = handlerName;
    }
    [self _queueMessage:message];
}

- (void)flushMessageQueue:(NSString *)messageQueueString{
    if (messageQueueString == nil || messageQueueString.length == 0) {
        NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
        return;
    }

    id messages = [self _deserializeMessageJSON:messageQueueString];
    for (WVJBMessage* message in messages) {
        if (![message isKindOfClass:[WVJBMessage class]]) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
            continue;
        }
        [self _log:@"RCVD" json:message];

        NSString* responseId = message[@"responseId"];
        if (responseId) {
            WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            responseCallback(message[@"responseData"]);
            [self.responseCallbacks removeObjectForKey:responseId];
        } else {
            WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            if (callbackId) {
                responseCallback = ^(id responseData) {
                    if (responseData == nil) {
                        responseData = [NSNull null];
                    }

                    WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:msg];
                };
            } else {
                responseCallback = ^(id ignoreResponseData) {
                    // Do nothing
                };
            }

            WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];

            if (!handler) {
                NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                continue;
            }

            handler(message[@"data"], responseCallback);
        }
    }
}

/*这段代码在本文中没有用到
 * 因为再原项目中,JSBridge的js库是放在iOS本地沙盒中的,所以才需要手动注入
 * 但是本文中的示例,JSBridge是直接在Html中引用的,所以无需注入
- (void)injectJavascriptFile {
    NSString *js = WebViewJavascriptBridge_js();
    [self _evaluateJavascript:js];
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}
*/
-(BOOL)isCorrectProcotocolScheme:(NSURL*)url {
    if([[url scheme] isEqualToString:kCustomProtocolScheme]){
        return YES;
    } else {
        return NO;
    }
}

-(BOOL)isQueueMessageURL:(NSURL*)url {
    if([[url host] isEqualToString:kQueueHasMessage]){
        return YES;
    } else {
        return NO;
    }
}

-(BOOL)isBridgeLoadedURL:(NSURL*)url {
    return ([[url scheme] isEqualToString:kCustomProtocolScheme] && [[url host] isEqualToString:kBridgeLoaded]);
}

-(void)logUnkownMessage:(NSURL*)url {
    NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", kCustomProtocolScheme, [url path]);
}

-(NSString *)webViewJavascriptCheckCommand {
    return @"typeof WebViewJavascriptBridge == \'object\';";
}

-(NSString *)webViewJavascriptFetchQueyCommand {
    return @"JSBridge._fetchQueue();";
}

- (void)disableJavscriptAlertBoxSafetyTimeout {
    [self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"];
}

// Private
// -------------------------------------------

- (void) _evaluateJavascript:(NSString *)javascriptCommand {
    [self.delegate _evaluateJavascript:javascriptCommand];
}

- (void)_queueMessage:(WVJBMessage*)message {
//    if (self.startupMessageQueue) {
//        [self.startupMessageQueue addObject:message];
//    } else {
//        [self _dispatchMessage:message];
//    }
    [self _dispatchMessage:message];
}

- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    [self _log:@"SEND" json:messageJSON];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];

    NSString* javascriptCommand = [NSString stringWithFormat:@"JSBridge._handleMessageFromNative('%@');", messageJSON];
    if ([[NSThread currentThread] isMainThread]) {
        [self _evaluateJavascript:javascriptCommand];

    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self _evaluateJavascript:javascriptCommand];
        });
    }
}

- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{
    return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding];
}

- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON {
    return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
}

- (void)_log:(NSString *)action json:(id)json {
    if (!logging) { return; }
    if (![json isKindOfClass:[NSString class]]) {
        json = [self _serializeMessage:json pretty:YES];
    }
    if ([json length] > logMaxLength) {
        NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]);
    } else {
        NSLog(@"WVJB %@: %@", action, json);
    }
}

@end

WebViewJavascriptBridgeBase是JSBridge逻辑代码的功底支撑,也正是说这当中封装一些基础的代码,供JSBridge内部调用

WebViewJavascriptBridgeBase 实现

兑今世码如下

@implementation WebViewJavascriptBridgeBase {
    __weak id _webViewDelegate;
    long _uniqueId;
}

static bool logging = false;
static int logMaxLength = 500;

+ (void)enableLogging { logging = true; }
+ (void)setLogMaxLength:(int)length { logMaxLength = length;}

-(id)init {
    self = [super init];
    self.messageHandlers = [NSMutableDictionary dictionary];
    self.startupMessageQueue = [NSMutableArray array];
    self.responseCallbacks = [NSMutableDictionary dictionary];
    _uniqueId = 0;
    return(self);
}

- (void)dealloc {
    self.startupMessageQueue = nil;
    self.responseCallbacks = nil;
    self.messageHandlers = nil;
}

- (void)reset {
    self.startupMessageQueue = [NSMutableArray array];
    self.responseCallbacks = [NSMutableDictionary dictionary];
    _uniqueId = 0;
}

- (void)sendData:(id)data responseCallback:(WVJBResponseCallback)responseCallback handlerName:(NSString*)handlerName {
    NSMutableDictionary* message = [NSMutableDictionary dictionary];

    if (data) {
        message[@"data"] = data;
    }

    if (responseCallback) {
        NSString* callbackId = [NSString stringWithFormat:@"objc_cb_%ld", ++_uniqueId];
        self.responseCallbacks[callbackId] = [responseCallback copy];
        message[@"callbackId"] = callbackId;
    }

    if (handlerName) {
        message[@"handlerName"] = handlerName;
    }
    [self _queueMessage:message];
}

- (void)flushMessageQueue:(NSString *)messageQueueString{
    if (messageQueueString == nil || messageQueueString.length == 0) {
        NSLog(@"WebViewJavascriptBridge: WARNING: ObjC got nil while fetching the message queue JSON from webview. This can happen if the WebViewJavascriptBridge JS is not currently present in the webview, e.g if the webview just loaded a new page.");
        return;
    }

    id messages = [self _deserializeMessageJSON:messageQueueString];
    for (WVJBMessage* message in messages) {
        if (![message isKindOfClass:[WVJBMessage class]]) {
            NSLog(@"WebViewJavascriptBridge: WARNING: Invalid %@ received: %@", [message class], message);
            continue;
        }
        [self _log:@"RCVD" json:message];

        NSString* responseId = message[@"responseId"];
        if (responseId) {
            WVJBResponseCallback responseCallback = _responseCallbacks[responseId];
            responseCallback(message[@"responseData"]);
            [self.responseCallbacks removeObjectForKey:responseId];
        } else {
            WVJBResponseCallback responseCallback = NULL;
            NSString* callbackId = message[@"callbackId"];
            if (callbackId) {
                responseCallback = ^(id responseData) {
                    if (responseData == nil) {
                        responseData = [NSNull null];
                    }

                    WVJBMessage* msg = @{ @"responseId":callbackId, @"responseData":responseData };
                    [self _queueMessage:msg];
                };
            } else {
                responseCallback = ^(id ignoreResponseData) {
                    // Do nothing
                };
            }

            WVJBHandler handler = self.messageHandlers[message[@"handlerName"]];

            if (!handler) {
                NSLog(@"WVJBNoHandlerException, No handler for message from JS: %@", message);
                continue;
            }

            handler(message[@"data"], responseCallback);
        }
    }
}

/*这段代码在本文中没有用到
 * 因为再原项目中,JSBridge的js库是放在iOS本地沙盒中的,所以才需要手动注入
 * 但是本文中的示例,JSBridge是直接在Html中引用的,所以无需注入
- (void)injectJavascriptFile {
    NSString *js = WebViewJavascriptBridge_js();
    [self _evaluateJavascript:js];
    if (self.startupMessageQueue) {
        NSArray* queue = self.startupMessageQueue;
        self.startupMessageQueue = nil;
        for (id queuedMessage in queue) {
            [self _dispatchMessage:queuedMessage];
        }
    }
}
*/
-(BOOL)isCorrectProcotocolScheme:(NSURL*)url {
    if([[url scheme] isEqualToString:kCustomProtocolScheme]){
        return YES;
    } else {
        return NO;
    }
}

-(BOOL)isQueueMessageURL:(NSURL*)url {
    if([[url host] isEqualToString:kQueueHasMessage]){
        return YES;
    } else {
        return NO;
    }
}

-(BOOL)isBridgeLoadedURL:(NSURL*)url {
    return ([[url scheme] isEqualToString:kCustomProtocolScheme] && [[url host] isEqualToString:kBridgeLoaded]);
}

-(void)logUnkownMessage:(NSURL*)url {
    NSLog(@"WebViewJavascriptBridge: WARNING: Received unknown WebViewJavascriptBridge command %@://%@", kCustomProtocolScheme, [url path]);
}

-(NSString *)webViewJavascriptCheckCommand {
    return @"typeof WebViewJavascriptBridge == \'object\';";
}

-(NSString *)webViewJavascriptFetchQueyCommand {
    return @"JSBridge._fetchQueue();";
}

- (void)disableJavscriptAlertBoxSafetyTimeout {
    [self sendData:nil responseCallback:nil handlerName:@"_disableJavascriptAlertBoxSafetyTimeout"];
}

// Private
// -------------------------------------------

- (void) _evaluateJavascript:(NSString *)javascriptCommand {
    [self.delegate _evaluateJavascript:javascriptCommand];
}

- (void)_queueMessage:(WVJBMessage*)message {
//    if (self.startupMessageQueue) {
//        [self.startupMessageQueue addObject:message];
//    } else {
//        [self _dispatchMessage:message];
//    }
    [self _dispatchMessage:message];
}

- (void)_dispatchMessage:(WVJBMessage*)message {
    NSString *messageJSON = [self _serializeMessage:message pretty:NO];
    [self _log:@"SEND" json:messageJSON];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\'" withString:@"\\\'"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\r" withString:@"\\r"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\f" withString:@"\\f"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2028" withString:@"\\u2028"];
    messageJSON = [messageJSON stringByReplacingOccurrencesOfString:@"\u2029" withString:@"\\u2029"];

    NSString* javascriptCommand = [NSString stringWithFormat:@"JSBridge._handleMessageFromNative('%@');", messageJSON];
    if ([[NSThread currentThread] isMainThread]) {
        [self _evaluateJavascript:javascriptCommand];

    } else {
        dispatch_sync(dispatch_get_main_queue(), ^{
            [self _evaluateJavascript:javascriptCommand];
        });
    }
}

- (NSString *)_serializeMessage:(id)message pretty:(BOOL)pretty{
    return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message options:(NSJSONWritingOptions)(pretty ? NSJSONWritingPrettyPrinted : 0) error:nil] encoding:NSUTF8StringEncoding];
}

- (NSArray*)_deserializeMessageJSON:(NSString *)messageJSON {
    return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
}

- (void)_log:(NSString *)action json:(id)json {
    if (!logging) { return; }
    if (![json isKindOfClass:[NSString class]]) {
        json = [self _serializeMessage:json pretty:YES];
    }
    if ([json length] > logMaxLength) {
        NSLog(@"WVJB %@: %@ [...]", action, [json substringToIndex:logMaxLength]);
    } else {
        NSLog(@"WVJB %@: %@", action, json);
    }
}

@end

WebViewJavascriptBridgeBase是JSBridge逻辑代码的功底支撑,相当于说那其间封装一些基础的代码,供JSBridge内部调用

  • (5) 上边是运用工厂类创建ImageLoader贯彻的代码

WebViewJavascriptBridge 实现

落成代码如下

#if __has_feature(objc_arc_weak)
    #define WVJB_WEAK __weak
#else
    #define WVJB_WEAK __unsafe_unretained
#endif

@implementation WebViewJavascriptBridge {
    WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView;
    WVJB_WEAK id _webViewDelegate;
    long _uniqueId;
    WebViewJavascriptBridgeBase *_base;
}

/* API
 *****/

+ (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; }
+ (void)setLogMaxLength:(int)length { [WebViewJavascriptBridgeBase setLogMaxLength:length]; }

+ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView {
    WebViewJavascriptBridge* bridge = [[self alloc] init];
    [bridge _platformSpecificSetup:webView];
    return bridge;
}

- (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate {
    _webViewDelegate = webViewDelegate;
}

- (void)send:(id)data {
    [self send:data responseCallback:nil];
}

- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:nil];
}

- (void)callHandler:(NSString *)handlerName {
    [self callHandler:handlerName data:nil responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data {
    [self callHandler:handlerName data:data responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}

- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
    _base.messageHandlers[handlerName] = [handler copy];
}

- (void)disableJavscriptAlertBoxSafetyTimeout {
    [_base disableJavscriptAlertBoxSafetyTimeout];
}


/* Platform agnostic internals
 *****************************/

- (void)dealloc {
    [self _platformSpecificDealloc];
    _base = nil;
    _webView = nil;
    _webViewDelegate = nil;
}

- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand
{
    return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
}

/* Platform specific internals: OSX
 **********************************/
#if defined WVJB_PLATFORM_OSX

- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
    _webView = webView;

    _webView.policyDelegate = self;

    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}

- (void) _platformSpecificDealloc {
    _webView.policyDelegate = nil;
}

- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener
{
    if (webView != _webView) { return; }
    NSURL *url = [request URL];

    if ([_base isCorrectProcotocolScheme:url]) {
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
            NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
            [_base flushMessageQueue:messageQueueString];
        } else {
            [_base logUnkownMessage:url];
        }
        [listener ignore];
    } else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
        [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
    } else {
        [listener use];
    }
}



/* Platform specific internals: iOS
 **********************************/
#elif defined WVJB_PLATFORM_IOS

- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
    _webView = webView;
    _webView.delegate = self;
    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}

- (void) _platformSpecificDealloc {
    _webView.delegate = nil;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if (webView != _webView) { return; }

    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
        [strongDelegate webViewDidFinishLoad:webView];
    }
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    if (webView != _webView) { return; }

    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
        [strongDelegate webView:webView didFailLoadWithError:error];
    }
}
//捕获url,并进行分析
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if (webView != _webView) { return YES; }
    NSURL *url = [request URL];
    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if ([_base isCorrectProcotocolScheme:url]) {
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
            NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
            [_base flushMessageQueue:messageQueueString];
        } else {
            [_base logUnkownMessage:url];
        }
        return NO;
    } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
        return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
    } else {
        return YES;
    }
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    if (webView != _webView) { return; }

    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
        [strongDelegate webViewDidStartLoad:webView];
    }
}

#endif

@end

WebViewJavascriptBridge是根本的JSBridge逻辑达成代码

WebViewJavascriptBridge 实现

落成代码如下

#if __has_feature(objc_arc_weak)
    #define WVJB_WEAK __weak
#else
    #define WVJB_WEAK __unsafe_unretained
#endif

@implementation WebViewJavascriptBridge {
    WVJB_WEAK WVJB_WEBVIEW_TYPE* _webView;
    WVJB_WEAK id _webViewDelegate;
    long _uniqueId;
    WebViewJavascriptBridgeBase *_base;
}

/* API
 *****/

+ (void)enableLogging { [WebViewJavascriptBridgeBase enableLogging]; }
+ (void)setLogMaxLength:(int)length { [WebViewJavascriptBridgeBase setLogMaxLength:length]; }

+ (instancetype)bridgeForWebView:(WVJB_WEBVIEW_TYPE*)webView {
    WebViewJavascriptBridge* bridge = [[self alloc] init];
    [bridge _platformSpecificSetup:webView];
    return bridge;
}

- (void)setWebViewDelegate:(WVJB_WEBVIEW_DELEGATE_TYPE*)webViewDelegate {
    _webViewDelegate = webViewDelegate;
}

- (void)send:(id)data {
    [self send:data responseCallback:nil];
}

- (void)send:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:nil];
}

- (void)callHandler:(NSString *)handlerName {
    [self callHandler:handlerName data:nil responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data {
    [self callHandler:handlerName data:data responseCallback:nil];
}

- (void)callHandler:(NSString *)handlerName data:(id)data responseCallback:(WVJBResponseCallback)responseCallback {
    [_base sendData:data responseCallback:responseCallback handlerName:handlerName];
}

- (void)registerHandler:(NSString *)handlerName handler:(WVJBHandler)handler {
    _base.messageHandlers[handlerName] = [handler copy];
}

- (void)disableJavscriptAlertBoxSafetyTimeout {
    [_base disableJavscriptAlertBoxSafetyTimeout];
}


/* Platform agnostic internals
 *****************************/

- (void)dealloc {
    [self _platformSpecificDealloc];
    _base = nil;
    _webView = nil;
    _webViewDelegate = nil;
}

- (NSString*) _evaluateJavascript:(NSString*)javascriptCommand
{
    return [_webView stringByEvaluatingJavaScriptFromString:javascriptCommand];
}

/* Platform specific internals: OSX
 **********************************/
#if defined WVJB_PLATFORM_OSX

- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
    _webView = webView;

    _webView.policyDelegate = self;

    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}

- (void) _platformSpecificDealloc {
    _webView.policyDelegate = nil;
}

- (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id)listener
{
    if (webView != _webView) { return; }
    NSURL *url = [request URL];

    if ([_base isCorrectProcotocolScheme:url]) {
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
            NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
            [_base flushMessageQueue:messageQueueString];
        } else {
            [_base logUnkownMessage:url];
        }
        [listener ignore];
    } else if (_webViewDelegate && [_webViewDelegate respondsToSelector:@selector(webView:decidePolicyForNavigationAction:request:frame:decisionListener:)]) {
        [_webViewDelegate webView:webView decidePolicyForNavigationAction:actionInformation request:request frame:frame decisionListener:listener];
    } else {
        [listener use];
    }
}



/* Platform specific internals: iOS
 **********************************/
#elif defined WVJB_PLATFORM_IOS

- (void) _platformSpecificSetup:(WVJB_WEBVIEW_TYPE*)webView {
    _webView = webView;
    _webView.delegate = self;
    _base = [[WebViewJavascriptBridgeBase alloc] init];
    _base.delegate = self;
}

- (void) _platformSpecificDealloc {
    _webView.delegate = nil;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    if (webView != _webView) { return; }

    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
        [strongDelegate webViewDidFinishLoad:webView];
    }
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
    if (webView != _webView) { return; }

    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
        [strongDelegate webView:webView didFailLoadWithError:error];
    }
}
//捕获url,并进行分析
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    if (webView != _webView) { return YES; }
    NSURL *url = [request URL];
    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if ([_base isCorrectProcotocolScheme:url]) {
        if ([_base isBridgeLoadedURL:url]) {
            [_base injectJavascriptFile];
        } else if ([_base isQueueMessageURL:url]) {
            NSString *messageQueueString = [self _evaluateJavascript:[_base webViewJavascriptFetchQueyCommand]];
            [_base flushMessageQueue:messageQueueString];
        } else {
            [_base logUnkownMessage:url];
        }
        return NO;
    } else if (strongDelegate && [strongDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
        return [strongDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
    } else {
        return YES;
    }
}

- (void)webViewDidStartLoad:(UIWebView *)webView {
    if (webView != _webView) { return; }

    __strong WVJB_WEBVIEW_DELEGATE_TYPE* strongDelegate = _webViewDelegate;
    if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
        [strongDelegate webViewDidStartLoad:webView];
    }
}

#endif

@end

WebViewJavascriptBridge是最首要的JSBridge逻辑达成代码

Webview容器关键代码 落成

兑今世码如下

Webview容器关键代码 完成

金镶玉裹福禄双全代码如下

import android.content.Context;

public final class ImageManager {

    private static String TAG = "ImageManager";
    private static ImageLoader sImageLoader;

    private ImageManager() {
        //no instance
    }

    public static void init(Context appContext) {
        if (sImageLoader != null) {
            throw new IllegalStateException(TAG + " already initalized");
        }

        sImageLoader = ImageLoaderFactory.createImageLoader("com.stone.app.manager.imageloader.internal.FrescoImageLoader");
        sImageLoader.init(appContext);
    }

    public static ImageLoader getImageLoader() {
        return sImageLoader;
    }
}

建立JSBridge桥梁

- (void)viewDidLoad {

    [super viewDidLoad];
    // 一些其它操作

    // WebViewJavascriptBridge
    // 给webView建立JS和OC的沟通桥梁
    [WebViewJavascriptBridge enableLogging];
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wv];
    [self.bridge setWebViewDelegate:self];
    [self registAPIForJS];
}   

建立JSBridge桥梁

- (void)viewDidLoad {

    [super viewDidLoad];
    // 一些其它操作

    // WebViewJavascriptBridge
    // 给webView建立JS和OC的沟通桥梁
    [WebViewJavascriptBridge enableLogging];
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.wv];
    [self.bridge setWebViewDelegate:self];
    [self registAPIForJS];
}   

总结:

注册API方法

- (void)registAPIForJS
{
    // 测试原生方法
    [self.bridge registerHandler:@"testNativeFunc" handler:^(id data, WVJBResponseCallback responseCallback) {
        //可以这样获取参数
        NSString *str = data[@"key值"];

       //可以做一些自己的事情
    }];
}

注册API方法

- (void)registAPIForJS
{
    // 测试原生方法
    [self.bridge registerHandler:@"testNativeFunc" handler:^(id data, WVJBResponseCallback responseCallback) {
        //可以这样获取参数
        NSString *str = data[@"key值"];

       //可以做一些自己的事情
    }];
}
  1. 将接口和贯彻类分别, 接口和落到实处各自位于单独的包中,
    并且达成类定义为包私有的 (即类未有修饰符).
  2. 概念工厂类, 使用反射起先化完成类.
  3. 瞩目混淆的时候不能够混淆完结类的类名, 因为其起先化使用了反射.

破获url scheme并实施格局的代码

//关键是这句代码
[self.bridge setWebViewDelegate:self];
//然后再WebViewJavascriptBridge里就可以重新shouldStartLoadWithRequest来捕获url了

破获url scheme并实施措施的代码

//关键是这句代码
[self.bridge setWebViewDelegate:self];
//然后再WebViewJavascriptBridge里就可以重新shouldStartLoadWithRequest来捕获url了

有一个设计方式也是分开接口和促成并使其各自独立变化, 那正是桥接情势.
具体能够参考Android中的Window和Context的设计.
关于动态扩展分离接口和实现,
能够参见《在Android上使用SPI机制》

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有