Rexxar Android 系列学习(3) Native 和 web 交互

关于 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) 缓存机制

你可能感兴趣的:(Rexxar Android 系列学习(3) Native 和 web 交互)