前言:
这是本人的第一篇博客,有不正确或者不规范之处,敬请见谅!回归正文,由于最近在做Android端与H5界面之间的交互,之前没做过类似的功能,在网上找了很多资料,说法不一,而且实现起来也各有不同。
我在做功能之前,自己动手做了一个小Demo,当时是用的H5界面中的JavaScript调用Android端的方法进行实现,这个方法是可行的,也就是此文的方法二。
但是在实际开发中,由于要和IOS统一, 而IOS那边当时想用url的方式,也就是此文要说的方法一,开始他说IOS端做不到用我说的方法二(即调用方法的方式),此时又不能说让web端实现两套代码或者实现判断,这毕竟是不好的。所以我们一开始就选择了通过url的方式。
后来他通过查阅资料,发现也可以通过调用方法的方式去实现。所以最后的总结就是,方法一和方法二都是可以使用的,但我们开发的时候选择了方法一。下面我们就从方法一开始谈起。
方法一:
项目地址:https://github.com/fsrmeng/WebView-Master
简书地址:http://www.jianshu.com/p/798f4eeb7d04
其实通过url的方式,就是相当于定义协议规则,也就是条件,我们拿到这个条件,进行判断,实现自己的逻辑代码。这里有一个关键的web前端代码: window.location.href=“url”,此处的url就是我们定义的协议规则。一旦H5走到上段代码,Android WebView就会调用shouldOverrideUrlLoading(WebView view, String url)。
我们定义了url 为“app://showgame.toast?”+JSON.stringify(json),而通过获取到app这个字段来判断是否是http协议,这里的json其实就是传递过来的参数,代码如下:
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
try {
//这一步很关键,web端传过来的有可能是其它编码格式
url = URLDecoder.decode(url, "utf-8");
//通过判断拦截到的url是否含有pre,来辨别是http请求还是调用android方法的请求
String[] parts = url.split("[?]");
String code = parts[0];
String pre = "app://";
if (!url.contains(pre)) {
//该url是http请求,用webview加载url
view.loadUrl(url);
return true;
}
//该url是调用android方法的请求,通过解析url中的参数来执行相应方法
String params = JavaScriptManager.getJSParams(url, code);
//在这里实现各种android方法的逻辑代码
JavaScriptManager.invokeAndroidMethod(mContext, code, params, mWebView);
return true;
} catch (Exception e) {
e.printStackTrace();
view.loadUrl(url);
return true;
}
}
});
这里我创建了一个管理类JavaScriptManager,并在其中创建了两个方法,一个是getJSParams(url, code),另一个是invokeAndroid(mContext, code, params, mWebView)。
接下来再看JavaScriptManager类两个方法的实现:
/**
* 获取JS传来的参数
* @param url
* @param pre
* @return
*/
public static String getJSParams(String url, String pre) {
String params = "";
if (url.contains(pre)) {
int index = url.indexOf(pre);
int end = index + pre.length();
params = url.substring(end + 1);
}
return params;
}
/**
*JS调用Android中的方法,根据code去判断调用具体的方法
* @param mContext
* @param code
* @param params
* @param mWebView
*/
public static void invokeAndroidMethod(Context mContext, String code, String params, final WebView mWebView) {
if (code.equals("app://showgame.toast")) {
try {
JSONObject json = new JSONObject(params);
String toast = json.optString("data");
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
} catch (JSONException e) {
e.printStackTrace();
}
return;
}else if (code.equals("app://showgame.getHotelData")) {
try {
final JSONObject json = new JSONObject(params);
final String callback = json.optString("callback");
json.put("hotel_name", "维多利亚大酒店");
json.put("order_status", "已支付");
json.put("orderId", "201612291809626");
json.put("seller", "携程");
json.put("expire_time", "2017年1月6日 23:00");
json.put("price", "688.0");
json.put("back_price", "128.0");
json.put("pay_tpye", "支付宝支付");
json.put("room_size", "3间房");
json.put("room_count", "3");
json.put("in_date", "2017年1月6日 12:00");
json.put("out_date", "2017年1月8日 12:00");
json.put("contact", "赵子龙先生");
json.put("phone", "18888888888");
json.put("server_phone", "0755-85699309");
json.put("address", "深圳市宝安区兴东地铁站旁边");
invokeJavaScript(mContext, callback, json.toString(), mWebView);
} catch (JSONException e) {
e.printStackTrace();
}
return;
}
}
这里封装的方法很实用,以后诸如类似的JS调用Android端方法的话,就可以定义类似的协议,从而只需要在invokeAndroidMethod(Context mContext, String code, String params, final WebView mWebView)方法中,根据code,通过协议判断,从而实现不同的逻辑代码,也就实现了JS调用Android端不同的方法。
注意:这里还有一点需要特别说明,那就是我们需要让自己的WebView支持JS:
mWebView.getSettings().setJavaScriptEnabled(true);
其实开发中遇到的远没有这么简单,开始是写了Demo能运行,但是一旦和web前端联调的时候,总是会出现各种各样的问题,这里将总结以下几点:
1.运行起来后,发现并不会走shouldOverrideUrlLoading()方法,这时候不要怀疑自己的程序出现问题了,因为既然Demo能运行起来,那就肯定不是我们这边的问题,而是web端的问题(即使浏览器、IOS或者部分安卓手机能运行),其实遇到这个问题,我们也是研究了很长时间,但是最终找到了问题的所在,就是可能web端需要把所有的代码都写在一个HTML文件中,而不是通过引用JS文件的方式,最好也不要用什么框架,但是问题所存在的原因,我不知道,因为我并不懂web端,其实web端同事也不知道!
2.解决了第一个问题之后,应该就能调用我们上面说得这个方法了,但是此时可能会发现传过来的参数中文乱码了。当时web端通过各种编码格式,都行不通,后来我在我这边拿到这个url,进行utf-8编码,这样就不会乱码了。也就是我在shouldOverrideUrlLoading()中写得:
//这一步很关键,web端传过来的有可能是其它编码格式
url = URLDecoder.decode(url, "utf-8");
写在最后
本文提供了Android WebView与JS互相调用的方法一,方法二将在我接下来的博客中展示,敬请期待!