Js与原生交互详解(JsBridge)

1.前言

优美的js与原生的交互应该是像微信那样的,简洁方便,通俗易懂,网上看了很多博客,发现很多人都没说清楚明白,说的明白的又太抽象,又是画图又是理论的,弄到后面就作者一个人懂,一点也不适合小白,所以我特地写一篇,就这样。

 

2.效果说明

微信JSDK:比如一个选择图片的交互,人家是这么写的。

wx.chooseImage({
  success: function (res) {
    var tempFilePaths = res.tempFilePaths // tempFilePaths 的每一项是一个本地临时文件路径
  }
})

那么我们也要做跟人家微信差不多的,至少给到前端同学调用的时候要跟微信一样,这样才叫优美。

需求:需要这么一个方法 scan 调用之后可以打开原生摄像头,扫描完成之后,返回给结果。

最终要求实现如下代码:

tommy.scan({
  onSuccess: function(res) {
    alert("扫描结果" + res);
  }
});

如果你也想做成这样,请继续往下看,OR现在就可以离开,大家节约时间。用原生安卓为例子写,如果是IOS开发的同学你可以考虑要不要继续。

 

3.交互方式

具体的交互方式有两种,一种是注入,一种的loadurl的时候解析出来,不过网上一大堆,说的五花八门,那么我就用最简单的方式给小白们讲一下。

这是我早期的文章,大家可以喵喵,就跟网上那些乱七八糟的差不多,原理就那样,地址:链接

当然了这篇文章我觉得还可以,虽然看到后面蒙圈了,而且也没有实现优美的js调用方式,但是!阔以稍微借鉴一下,用来了解js跟原生是如何交互的。地址:链接

3.1)注入的方式

 

原生端的代码如下:

// 原生步骤很简单
// 写一个function注入到前端的js页面

// 1.定义一个scan的扫描方法
public class JavascriptInterface {
        @android.webkit.JavascriptInterface
        public void scan() {
			// 这是一个 scan 方法
        }

        @android.webkit.JavascriptInterface
        public void print() {
        	// 这是一个 print 方法
        }

        // ... 方法都在这里写
    }
    
// 2.把这个方法注入到webView中的js里面,两部完成Native的注入定义
// 这里要注意,最好等页面加载完成,再注入
webView.addJavascriptInterface(new JavascriptInterface(), "tommy");


// 3.回调
// 当处理好 scan 的操作,原生再调用事先说好的方法 scanCall 这样,js端就能收到回调
mWebView.loadUrl("javascript:scanCallBack("+ res +")");

Js端调用方法如下:

    <script>

      window.tommy.scan();

	  // 事先定义好一个方法,给Native回调
	  // 比如说,摄像头拍好了,Native就调用这个方法,然后js就知道了
	  scanCallBack function(res) {
          
      }
			 
    </script>

3.2)LoadUrl的方式

 

原生端的代码如下:

// 1.
// 这里代码我抄别人的了,懒得写了,因为Demo我用上面的方式
// 复写WebViewClient类的shouldOverrideUrlLoading方法
mWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
	    Uri uri = Uri.parse(url);
        // 裁剪出来,拿到 method 判断是否是 scan,如果是,执行scan操作
	    // 按照预先设定好的协议解析url
	   		
	    // 执行JS所需要调用的逻辑
      	return super.shouldOverrideUrlLoading(view, url);
    }
    
    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        // 通知前端,网页js加载完成
        
    }
});

// 2.回调
// 当处理好 scan 的操作,原生再调用事先说好的方法 scanCall 这样,js端就能收到回调
mWebView.loadUrl("javascript:scanCallBack("+ res +")");

Js端调用方法如下:

    <script>
        // 调用
        document.location = "js://webview?method=scan";
         

        // 事先定义好一个方法,给Native回调
        // 比如说,摄像头拍好了,Native就调用这个方法,然后js就知道了
        scanCallBack function(res) {

        }

    </script>

 

4.机制

同步异步

我已经尽量把图画简单了 ^ - ^,下面上代码,让你们进一步理解。

同步:比如说调用打印方法,打完直接返回通知js

    /**
     * H5 访问 Android 回调 打印
     *
     * @param json
     */
    @Override
    public void print(String json) {
        Log.e(TAG, "接收到js的数据:" + json);

        try {
            Toast.makeText(this, json, Toast.LENGTH_LONG).show();
            mAgentWeb.getJsAccessEntrace().quickCallJs("window.tommy.printCallBack", "ok");
        } catch (Exception e) {
            mAgentWeb.getJsAccessEntrace().quickCallJs("window.tommy.printCallBack", "fail");
            e.printStackTrace();
            Log.e(TAG, "打印报错:" + e.getMessage());
        }
    }

异步:比如调用扫描方法,调用打印方法之后,没有马上能回调,连java这边都要异步去处理,什么时候返回取决于用户什么时候扫描,由startActivityForResult方法可以看出,回调到js的方法要写在Activity返回的监听里面。

    @Override
    public void scan() {
        AndPermission.with(this)
                .runtime()
                .permission(Permission.CAMERA)
                .onGranted(permissions -> {
                    Log.e(TAG, "成功获取到摄像头权限");
                    startActivityForResult(new Intent(this, QRCodeActivity.class), QRCodeActivity.REQUEST_CODE_QRCODE_RESULT);
                })
                .onDenied(permissions -> {
                    Log.e(TAG, "用户拒绝获取到摄像头权限");
                })
                .start();
    }



// ...等Activity回调
 mAgentWeb.getJsAccessEntrace().quickCallJs("window.tommy.scanCallBack", result);

5.开始讲解

其实前面讲了一大堆都是基础,现在才是正题,上面的简单实现,也就是网上一大堆人说的,至于怎么封装成优雅的调用方式,网上很少有人讲,讲的也是一大堆图,各种注册监听Handler,重点都漏完,竟讲那些老生长谈……Ok,回归正题。

 

原js代码:

    <script>

      	window.tommy.scan();

        // 事先定义好一个方法,给Native回调
        // 比如说,摄像头拍好了,Native就调用这个方法,然后js就知道了
        scanCallBack function(res) {
          
      	}
			 
    </script>

这代码我们是已经可以实现,Native扫描,然后扫描完成也能接收到扫描结果res,只是这样前端看上去不太好看,也不怎么友好。那么现在我们的问题是,把scanCallBack放进去到scan里面回调就行了,是其实聪明你也是这么想的,只是网上没人说而已,特别是js基础不太好的安卓同学 ^ - ^

啥也不说,直接上代码:

HTML代码:

<!DOCTYPE HTML><html lang="en">
    <head>
      <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> 
      <META HTTP-EQUIV="Cache-Control" CONTENT="no-cache"> 
      <META HTTP-EQUIV="Expires" CONTENT="0">
    <meta charset="UTF-8">
    <title>Apache Tomcat Examples</title>
   
    </head>


    <button id="scan" onclick="scanQRC()" style="width: 600px;height: 100px;font-size: 60px;"> 扫描二维码 </button>

    <button id="print" onclick="xReLoad()" style="width: 600px;height: 100px;font-size: 60px;margin-top: 60px;"> 重新加载 </button>


    <script src="./tommy.js"></script>
    <script>

      function xReLoad() {
        location.reload();
      }

      // 扫码
      function scanQRC() {
        tommy.scan({
          onSuccess: function(res) {
            alert("打印结果" + res);
          }
        });
      }

    </script>
    
    </html>

Js代码:这一步网上鲜为人写,不知道为啥,这才是关键,下面这个类似于微信的Jsdk,你只要给你们前端的同学作为js引入,那么他的调用就可以非常的优美。 html引入: <script src="./tommy.js"></script>

// js 于 native 交互模块
window.tommy = {
    isReady: false,
    scallback: {},
    onReady: function () {
        tommy.isReady = true;
        tommy.ready();
    },
    ready: function() {

    },
    scanCallBack: function(r) {
        tommy.scallback(r);
    },
    scan: function(object) {
        if (!tommy.isReady) return;
        window.tommyApp.callFunction("scan","");
        tommy.scallback = object.onSuccess;
    },
};

这个时候小白跳出来说了,怎么是 window.tommyApp.callFunction("scan", ""); 跟上面不一样了,嗯。。是不一样,稍微改了点,我源码是这么写的懒得改了因为总不能每个方法都写一个,肯定要统一一下滴。

Android代码稍微改了下:根据传进来的方法名,去那啥,这样就不用写很多方法了。。。。

    //定义h5要调用的本地方法
    @JavascriptInterface
    public void callFunction(String methodName, String json) {
        switch (methodName) {
            //回调方法名
            case "scan":
                if (interfaceCallback != null) {
                    interfaceCallback.scan();
                }
                break;
            case "print":
                if (interfaceCallback != null) {
                    interfaceCallback.print(json);
                }
                break;
        }
    }

6.总结

到此,讲解完毕,为表示诚意,附上所有源码:下载地址

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

s清风s

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值