关于 Native 和 web 交互这部分内容相对来说比较独立,平时在做 WebView 开发一般都会涉及到 WebView 和 Js 交互通信问题。Native 调用 Js 方法,Js 调用 Native 方法。这就可能涉及到 Js 注入或者 JsBridge 问题。不过 Rexxar Android 中没有采用这种方式,而是通过 WebView 回调 shouldOverrideUrlLoading 方法进行拦截。当然这种拦截规则是 Native 和 Web 之间定好的 URL 协议,WebView 截获 URL 请求,根据 URL 规则来判断是否符合一定规则,符合则调用相应函数完成操作。
这里从官方摘取:
例如,Rexxar 中 UI 相关的功能的协议如下:
请求 douban://rexxar.douban.com/widget/nav_title,可以定义 Navigation Bar Title。
请求 douban://rexxar.douban.com/widget/nav_menu,可以定义 Navigation Bar Button。
请求 douban://rexxar.douban.com/widget/toast,可以出现一个消息通知 toast。
对比
优点:
很规范、URL 协议可复用、比较简单
缺点:
速度没有 onJsPrompt 快,通过 onJsPrompt 函数也能实现 Js 调用 Android 函数,通过 Json 来转化,这部分之前也做过 Js 安全注入调研,以后总结下。
JS 调用 Native 实现
public interface RexxarWidget {
/**
* a special path for the widget
*
* @return
*/
String getPath();
/**
* @param view
* @param url
* @return
*/
boolean handle(WebView view, String url);
}
RexxarWidget 是一个接口,其实就是一种规范,我们知道给当前页面提供了那些功能,每一种 Js 调用 Native 的方法都会对应一个 RexxarWidget 实现类,来处理当前功能逻辑。
实例:
首先在初始化的时候将需要的功能 add 进去,这样才能够被调用
mRexxarWebView.addRexxarWidget(new TitleWidget());
mRexxarWebView.addRexxarWidget(new AlertDialogWidget());
mRexxarWebView.addRexxarWidget(new ToastWidget());
mRexxarWebView.addRexxarWidget(new PullToRefreshWidget());
mRexxarWebView.addRexxarWidget(new MenuWidget());
通过拦截 URL 处理 handle:
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith(Constants.getContainerWidgetBase())) {
boolean handled;
for (RexxarWidget widget : mWidgets) {
if (null != widget) {
handled = widget.handle(view, url);
if (handled) {
return true;
}
}
}
}
return super.shouldOverrideUrlLoading(view, url);
}
我们来看一个实现:
public class TitleWidget implements RexxarWidget {
static final String KEY_TITLE = "title";
@Override
public String getPath() {
return "/widget/nav_title";
}
@Override
public boolean handle(WebView view, String url) {
if (TextUtils.isEmpty(url)) {
return false;
}
Uri uri = Uri.parse(url);
if (TextUtils.equals(uri.getPath(), getPath())) {
String title = uri.getQueryParameter(KEY_TITLE);
if (null != view && view.getContext() instanceof Activity) {
((Activity)view.getContext()).setTitle(Uri.decode(title));
}
return true;
}
return false;
}
}
可以看到 getPath() 返回当前的路由类型,外部可以根据这种类型来制定调用 URL 定义。
Native 调用 JS 实现
这个方式就是原生方式,Rexxar 做了通用处理:
/**
* Native调用js方法, 传递参数
*
* @param functionName 方法名
* @param jsonString 参数,需要是json格式
*/
public void callFunction(String functionName, String jsonString) {
if (TextUtils.isEmpty(functionName)) {
return;
}
if (TextUtils.isEmpty(jsonString)) {
mCore.loadUrl(String.format(Constants.FUNC_FORMAT, functionName));
} else {
jsonString = jsonString.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");
jsonString = jsonString.replaceAll("(\\\\)([utrn])", "\\\\$1$2");
jsonString = jsonString.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");
mCore.loadUrl(String.format(Constants.FUNC_FORMAT_WITH_PARAMETERS, functionName, jsonString));
}
}
总结
这部分还是比较容易懂得,可能 web 页面量少的时候感觉不出来这种方式好用,等到量多了的时候,在开发效率上能有不少的提高,因为协议自由方便,编写起来很容易实现。
Rexxar Android 系列学习其他文章
Rexxar Android 系列学习(1) 项目结构
Rexxar Android 系列学习(2) 路由协议
Rexxar Android 系列学习(3) Native 和 web 交互
Rexxar Android 系列学习(4) 错误处理
Rexxar Android 系列学习(5) 过滤拦截
Rexxar Android 系列学习(6) 缓存机制