前端与android客户端之间的相互调用

avatar
前端工程师

前端与Android客户端实现相互调用的几种方案

uri解析

统一资源标识符( Uniform Resource Identifier,URI),可以使用js向android客 户端发送一个uri链接。android 通过解析uri链接从而得知js代码的目的,从而进行下一步动作。简单来说就是js代码通过uri来定位android端上已有的资源,至于这个资源它本身的行为有android端自已决定。

实现的过程- Js 调用 Android

android端

Android 端的webview通过setWebViewClient设置自定义WebViewClient,其中有一个shouldOverrideUrlLoading 函数,可以接收到页面目标路径,通过webResourceRequest.getUrl().toString() 接可以知道js调用uri,并通过解析->执行--回调。

@Override
public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest webResourceRequest) {
    if (shouldOverrideUrl(webView,webResourceRequest.getUrl().toString())){

        return true;
    }else{
        return super.shouldOverrideUrlLoading(webView,webResourceRequest);
    }
}
private boolean shouldOverrideUrl(WebView view, String url){
    if (url.startsWith("demo:") ) {
          Log.i("UrlUtil",UrlUtil.getUrlParams(url).get("obj"));
         ((KalidorBaseWebView)view).analysis(UrlUtil.getUrlParams(url).get("obj"));
         return true;
    }
    return false;
}
js
function speak(obj){
  window.location.href ="demo://?obj="+encodeURIComponent(JSON.stringify(obj))
}

JavascriptInterface

JavascriptInterface是Android 提供的一种本地对象的映射,使得webview内的js代码可以通过此映射的对象调用对象中的方法,从而实现webview内的js调用android的能力。其具体映射的过程可以参考blog.csdn.net/sk719887916… ,临时找的,哈哈哈。

实现的过程- Js 调用 Android

android

Android 可以增加一个类,对可以被webview调用的方法设置 @JavascriptInterface(这个是用来增加安全性的),具体原因简单来说就是4.2之前向webview注入对象,对象的某些方法其实是不应该被调用的,但是被映射到了webview,js就可以调用并不想映射到webview的方法。现在流行的是通过映射一个单一的方法来实现类似通信的功能,定义一套调用的协议,然后定义一个通过js注册一些调用的api(内置在android客户端内),并基于这些调用api公开一个jssdk。js就可以安全的调用android的能力了。

@JavascriptInterface
public String postMessage(final String message) {
    getmWebView().post(new Runnable() {
        @Override
        public void run() {
            Log.e(this.getClass().getName(), "postMessage: " + message);
            webview.analysis(message);
        }
    });
    return "看看b";
}
js
function speak(obj){
    var res =  window.KalimdorWebContainerBridge.postMessage(
    JSON.stringify(
       {
          className:"com.daojia.calldemo.AlertPopu",
          method:"show",
          args: {
             title:"JsBridge来的哦",
             message:"给你们演示一把"
          }
       }))
}

onJsPrompt -- 强烈不建议这么做,这么做改变这个方法本身的使用场景了

Android通过 WebChromeClient的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调分别拦截JS对话框(即上述三个方法),得到他们的消息内容,然后解析即可。 常用的拦截是JS的输入框即prompt()方法,因为只有prompt()可以返回任意类型的值,操作最全面方便、更加灵活;而alert()对话框没有返回值;confirm()对话框只能返回两种状态(确定/取消)两个值。 --- 引用自Android WebView与JS的交互方式总结 主要是懒得写了

实现过程

adnroid

Android 端的webview通过setWebChromeClient设置自定义WebChromeClient其中有一个通过onJsPrompt就可以接受到js调用的prompt事件,因为是处理用户输入的所以是可以返回参数的。

@Override
public boolean onJsPrompt(WebView webView, String s, String message, String s2, JsPromptResult jsPromptResult) {
    String str = ((KalidorBaseWebView)webView).analysis(message);
    jsPromptResult.confirm(str);
    return true;
}
js
function speak(obj){
   var res = prompt(JSON.stringify(obj))
   document.getElementById('a').innerText = res
}
function alertDialog() {
   return {
        className:"com.daojia.calldemo.AlertPopu",
        method:"show",
        args: {
            title:"JsBridge来的哦",
            message:"给你们演示一把"
        }
   }
}

Socket

通过Socket(我用的是websocket,主要是socket.io没找到Android端的服务器包),android也可以和webview内的js实现相互调用。

实现方式

android
public class MyWebSocketServer extends WebSocketServer {
    private KalidorBaseWebView webview;

    MyWebSocketServer(InetSocketAddress host,KalidorBaseWebView Review) {
        super(host);
        this.webview = Review;
    }

    public void setWebview(KalidorBaseWebView Review) {
        this.webview = Review;
    }
    @Override
    public void onOpen(WebSocket conn, ClientHandshake handshake) {
        Log.d("websocket", "onOpen()一个客户端连接成功:" + conn.getRemoteSocketAddress());
    }
    @Override
    public void onMessage(WebSocket conn, String message) {
        // 需要保证android 和 加载网页的设备(我这边是电脑) 在同一个网段内,连同一个wifi即可
        Log.d("websocket", "onMessage()网页端来的消息->" + message);
        webview.isSocket = true;
        webview.post(new Runnable() {
            @Override
            public void run() {
                Log.e(this.getClass().getName(), "postMessage: " + message);
                String res =  webview.analysis(message);
                MyWebSocketServer.this.broadcast(res);
            }
        });
    }

    static MyWebSocketServer myWebsocketServer;
    // 实现方法,在服务中或者OnCreate()方法调用此方法
    public static void startMyWebsocketServer(KalidorBaseWebView Review) {
            // 端口复用
            if (myWebsocketServer != null) {
                myWebsocketServer.setWebview(Review);
               return;
            }
            // 9090为端口
            InetSocketAddress myHost = new InetSocketAddress("127.0.0.1", 9090);
            myWebsocketServer = new MyWebSocketServer(myHost, Review);
            myWebsocketServer.start();

    }
}
js
var ws = new WebSocket('ws://127.0.0.1:9090');
ws.onopen = function () {
  console.log('ws onopen');
  ws.onmessage = function (e) {
    console.log('from server: ' + e.data);
    this.dispatch(e)
  };;
};
function speak(obj){
  ws.send(JSON.stringify(obj));
}
function alertDialog() {
   return {
        className:"com.daojia.calldemo.AlertPopu",
        method:"show",
        args: {
            title:"JsBridge来的哦",
            message:"给你们演示一把"
        }
   }
}

同步&异步

在上面我们已经搞明白了一些js调用android的方式,那么如果我们希望调用之后android端能够回复js改怎么搞呢?哪些可以同步返回,哪些需要注册一个函数等待异步调用呢?

JavascriptInterface &onJsPrompt

js如果通过这些方式调用Android,Android 可以return 返回值,也就是可以实现同步调用-返回。

异步

在android中,可以通过调用loadUrl("javascript:xxxx('')")(强烈不推荐,一个是页面可能会刷新,主要是这玩意和加载url是一个方法多恐怖)。evaluateJavascript("xxxx('')"),这个方法是可以获取js的返回值的,比较推荐。

webview.evaluateJavascript("window.dispatch('" + dispatchMessage + "')", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
        //此处为js返回的结果
        Log.e("onReceiveValue", "onReceiveValue: " + value);

    }
});

另外通过socket建立的通信,也可以使用socket的api实现异步返回。

MyWebSocketServer.this.broadcast(res);

题外话

node服务之间的远程调用 简单版本

github.com/runner-up-j… github.com/runner-up-j…

好好学习,一起加油,努力活到最后。

参考文档

1.你应该知道的RPC原理

2.如何手撸一个较为完整的RPC框架

3. Android WebView与JS的交互方式总结

4. Android WebSocketServer 服务端几行代码轻松实现 5.两分钟了解RPC和LPC