zoukankan      html  css  js  c++  java
  • Android中Native和H5交互

    1.概述

        时至今日,H5的跨平台性越发凸显优势,一套代码适配android、ios,既能减少开发成本,又便于更新与维护.但是native的性能体验也确实更佳,尤其体现在复杂界面和频繁变化的界面上.事实上,移动平台native+h5的开发模式不是什么新鲜事了,各种框架层出不穷,主要目的就是为了使native与h5交互更加便捷高效,而在Android中必然需要WebView作为载体来展示H5内容和进行交互.

    2.交互方式

    • 传统的JSInterface:使用Android原生的javascriptInterface来进行js和java的通信.

      Native Invoke Js                                                                                                                                                                                                                

      WebSettings webSettings = webView.getSettings();
      //打通js通道 WebSettings webSettings = webView.getSettings();
      webSettings.setJavaScriptEnabled(true);
      webView.addJavascriptInterface(new JsInterface(), "android");
      //然后通过WebView的addJavascriptInterface方法去注入一个自定义的interface。
      public class JsInterface {
      @JavascriptInterface
      public void showToast(String toast) {
      Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show(); log("show toast success");
      }
      public void log(final String msg){
      webView.post(new Runnable() {
      @Override
      public void run() {
      webView.loadUrl("javascript: log(" + "'" + msg + "'" + ")");
      }
      });
      }
      }
      webView.addJavascriptInterface(new JsInterface(), "android");
      webView.loadUrl("file:///android_asset/interact.html");                                      

      @JavascriptInterface 是为了修复4.2版本之前的addjavascriptInterface接口引起的漏洞,这个漏洞曾导致恶意网页通过Js方法遍历刚刚通过addjavascriptInterface注入进来的类的所有方法从中获取到getClass方法,然后通过反射获取到Runtime对象,进而调用Runtime对象的exec方法执行一些操作.于是,交互如果按照这种方式的话,就得区分4.2之前与之后.         Js Invoke Native

      function showToast(toast){
      javascript:android.showToast(toast); //这里的android是上面java代码命名的
      }
      function log(msg){ console.log(msg); }

                                                                                                                                                                                                              

    • JSBridge的方式:其实就是通过重写WebView中WebChromeClient类的onJsPrompt()方法来进行js和java的通信                                                                

      对WebView熟悉的话,肯定知道Js中对应的window.alert()window.confirm()window.prompt()这三个方法的调用在WebChromeClient中都有对应的回调方法,分别为:
      onJsAlert()onJsConfirm()onJsPrompt(),对于它们传入的message,都可以在相应的回调方法中接收到,所以,对于Js调Native方法, 可以借助这个信道,和前端协定好一段特定规则的message,这个规则中应至少包含这些信息:

      所调用Native方法所在类的类名
      所调用Native的方法名
      Js调用Native方法所传入的参数

      基于这些信息,定义一个自己的协议。比如规定sheme,path等等。比如:

    scheme://host:port/path?query对应的协定prompt传入message的格式为:jsbridge://class:port/method?params

      这样,前端和app端协商好后,前端需要通过Js调用Native方法来获取一些信息或功能,就只需要按照协议的格式把需要调用的类名、方法名、参数放入对应得位置即可,而 会在onJsPrompt方法中接收到,所以根据与前端协定好的协议来进行解析, 可以用一个Uri来包装这段协议,然后通过Uri:getHost、getPath、getQuery方法获取对应的类名,方法名,参数数据,最后通过反射来调用指定类中指定的方法.(port的作用是为了标识Js中的回调function,当Js调用Native方法时,会得到本次调用的port号)

    自动生成port和绑定function回调的Js代码如下:

     
    generatePort: function () {
        return Math.floor(Math.random() * (1 << 50)) + '' + increase++;
    },
    //调用Native方法
    callMethod: function (clazz, method, param, callback) {
        var port = PrivateMethod.generatePort();
        if (typeof callback !== 'function') {
            callback = null;
        }
        //绑定对应port的function回调函数
        PrivateMethod.registerCallback(port, callback);
        PrivateMethod.callNativeMethod(clazz, port, method, param);
    },
    onComplete: function (port, result) {
        //把Native返回的Json字符串转为JSONObject
        var resultJson = PrivateMethod.str2Json(result);
        //获取对应port的function回调函数
        var callback = PrivateMethod.getCallback(port).callback;
        PrivateMethod.unRegisterCallback(port);
        if (callback) {
            //执行回调
            callback && callback(resultJson);
        }
    }

    数据的返回格式为Json字符串,基本格式为:

     
    resultData = {
        status: {
            code: 0,//0:成功,1:失败
            msg: '请求超时'//失败时候的提示,成功可为空
        },
        data: {}//数据,无数据可以为空
    };

    所以符合Js调用的Native方法格式为:

     
    public static void ***(WebView webView, JSONObject data, JsCallback callback) {
    	//get some info ...
    	JsCallback.invokeJsCallback(callback, true, result, null);
    }
    

    判断Js调用的方法是否符合该格式的代码为,符合则存入一个Map中供Js调用:

     
    private void putMethod(Class<?> clazz) {
        if (clazz == null)
            return;
        ArrayMap<String, Method> arrayMap = new ArrayMap<>();
        Method method;
        Method[] methods = clazz.getDeclaredMethods();
        int length = methods.length;
        for (int i = 0; i < length; i++) {
            method = methods[i];
            int methodModifiers = method.getModifiers();
            if ((methodModifiers & Modifier.PUBLIC) != 0 && (methodModifiers & Modifier.STATIC) != 0 && method.getReturnType() == void.class) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes != null && parameterTypes.length == 3) {
                    if (WebView.class == parameterTypes[0] && JSONObject.class == parameterTypes[1] && JsCallback.class == parameterTypes[2]) {
                        arrayMap.put(method.getName(), method);
                    }
                }
            }
        }
        mArrayMap.put(clazz.getSimpleName(), arrayMap);
    }
    

    对于有返回值的方法,并不需要设置它的返回值,因为方法的结果最后是通过JsCallback.invokeJsCallback来进行对Js层的回调.

    看图:

    • UrlRouter:UrlRouter是一个通过url来让前端唤起native页面的框架。不过,如果协议定义的合理,它可以让前端,Android和iOS三端有一个高度的统一,十分方便

    通过WebViewClient类的shouldOverrideUrlLoading方法去拦截前端写的url,发现如果是符合定义的UrlRouter协议的话,就跳转到相应的页面。

    eg.前端一个按钮<input type="button" value="login" onclick="javascript:location.href='http://www.baidu.com/'">

    java代码中这样:

     
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        parserURL(url); //解析url,如果符合跳转native界面的url规则,则跳转native界面
        return super.shouldOverrideUrlLoading(view, url);
    }

    这种方式没怎么用过,也就不细说了.

    3.总结

    H5用Webview加载的方式,往往还涉及到WebView的各种安全性、兼容性的问题,再比如易OOM,Webview许多方法并不按正常时机加载;这些坑都是需要踩过填过之后才能积累经验的.上述3种方式,都有自己的适用场景,比如个别页面需要加载h5首推第一种,app中有大量的h5交互那么就直接用第二种吧,对于一些平台统一的特殊要求的话就用UrlRouter的方式了.

  • 相关阅读:
    DIY 作品 及 维修 不定时更新
    置顶,博客中所有源码 github
    openwrt PandoraBox PBR-M1 极路由4 HC5962 更新固件
    使用 squid 共享 虚拟专用网至局域网
    第一次参加日语能力测试 N5
    libx264 libfdk_aac 编码 解码 详解
    开发RTSP 直播软件 H264 AAC 编码 live555 ffmpeg
    MFC Camera 摄像头预览 拍照
    http2 技术整理 nginx 搭建 http2 wireshark 抓包分析 server push 服务端推送
    plist 图集 php 批量提取 PS 一个个切
  • 原文地址:https://www.cnblogs.com/fuyaozhishang/p/6582616.html
Copyright © 2011-2022 走看看