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的方式了.

Android中Native和H5交互的更多相关文章

  1. Native与H5交互的一些解决方法

    一. 原生代码中直接加载页面 1.    具体案例 加载本地/网络HTML5作为功能介绍页 2.    代码示例 //本地 -(void)loadLocalPage:(UIWebView*)webVi ...

  2. Android 中 js 和 原生交互

    Android中的WebView 中加载的URL 默认是在手机浏览器中加载的,我们可以覆盖这种默认的动作,让网页在WebView中打开.通过设置WebView的WebViewClent 达到这个效果. ...

  3. Android WebView 基本设置与H5 交互

    mWebView.setDrawingCacheEnabled(true); WebChromeClient webChromeClient = new WebChromeClient(); mWeb ...

  4. 深入分析:Android中app之间的交互(二,使用ComponentName)

    在前一篇相关主题的博文中我们了解了如何使用Action来启动当前应用之外的Activity处理我们的业务逻辑,在本篇笔记中我在简单介绍一下使用ComponentName来与当前应用之外的应用进行交互. ...

  5. 深入分析:Android中app之间的交互(一,使用Action)

    在我们开发Android App应用的时候,有些需求需要我们启动其他的App来处理一些逻辑,例如我们需要根据一个地址来调用系统或者相关的地图Map App,这样我们不用在自己的App中编写相应的功能, ...

  6. Android中WebView与H5的交互,Native与JS方法互调

    项目中经常用到WebView与H5的交互,一个是H5调本地方法,一个是本地调H5方法,在此记录一下. 首先,启用JS支持 //启用js支持 webSettings.setJavaScriptEnabl ...

  7. Android 中Java和JavaScript交互入门

    如何实现JavaScript 和java 交互 实现Java和js交互十分便捷.通常只需要以下几步. WebView开启JavaScript脚本执行 WebView设置供JavaScript调用的交互 ...

  8. Android中Java和JavaScript交互

    Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是一个很举足轻重的脚本.本文将介绍如何实现Java代码和Javascript代码的相互调用. 如 ...

  9. cordova混合开发:Android中native调用javascript

    今天学习怎么在java中调用javascript方法,做个记录: 第一种方式,这个最简单: loadUrl("javascript:func1()"); 要注意要在devicere ...

随机推荐

  1. 关于php的session.serialize_handler的问题

    前言 php的session信息是储存在文件中的 session.save_path="" 指定储存的路径 session.save_handler="" 指定 ...

  2. Java实现打包下载BLOB字段中的文件

    概述 web项目的文件打包下载实现:servlet接收请求,spring工具类访问数据库及简化大字段内容获取,org.apache.tools.zip打包. 必要提醒:当前总结是继Java实现下载BL ...

  3. 洛谷——P1916 小书童——蚂蚁大战

    P1916 小书童——蚂蚁大战 题目背景 小A在你的帮助下,开始“刷题”,他在小书童里发现了一款叫“蚂蚁大战”(又称蛋糕保卫战)的游戏.(你懂得) 题目描述 游戏中会出现n只蚂蚁,分别有a1,a2…… ...

  4. 【最大权森林/Kruskal】POJ3723-Conscription

    [题目大意] 招募m+n个人每人需要花费$10000,给出一些关系,征募某个人的费用是原价-已招募人中和他亲密值的最大值,求最小费用. [思路] 人与人之间的亲密值越大,花费越少,即求出最大权森林,可 ...

  5. python基础之闭包函数与装饰器

    闭包函数: 什么是闭包函数: 闭指的是定义在一个函数内部 包指的是该函数包含对外部作用域(非全局作用域)名字的引用 def counter(): n=0 def incr(): nonlocal n ...

  6. Educational Codeforces Round 9 A. Grandma Laura and Apples 水题

    A. Grandma Laura and Apples 题目连接: http://www.codeforces.com/contest/632/problem/A Description Grandm ...

  7. 移动应用安全开发指南(Android)--Android组件和IPC

    概述 移动应用开发中,往往有跨进程通信的需求,方便地实现程序间的数据共享.Android提供了多种IPC通信的方式,给开发人员带来了便利,但如果选择或使用不当,就有可能发生各种各样的风险. 安全准则 ...

  8. 我告诉你MSDN

    https://msdn.itellyou.cn/ 微软的软件,系统,官方下载

  9. linux 端口占用查看 netstat -tunpl | grep 6379

    端口占用查看 netstat -tunpl | grep 6379 netstat -luntpu|grep fdfs

  10. TJSONTableObject跨平台解析JSON

    TJSONTableObject跨平台解析JSON USES SynCrossPlatformJSON TSQLRecordPeoplePersistent = class(TPersistent) ...