Android 中利用WebViewJavascriptBridge 实现js和java的交互

本文详细介绍了WebViewJavascriptBridge的原理、使用方法及实现案例,旨在解决安卓UI开发中的屏幕适配问题,提升开发效率并增强安全性。通过结合Html5和原生控件,实现Java和JavaScript之间的灵活互操作,展示如何通过实例化WebView、设置WebChromeClient、注册Handler等步骤,实现在Java和JavaScript间的数据传递。同时,提供了HTML和JS的交互示例,包括本地文件读取、网络请求、本地位置获取等场景,强调了双向通信的重要性及其实现方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自:http://blog.youkuaiyun.com/sk719887916/article/details/47189607


安卓开发目前现状来说,开发者大部分时间还是花在UI的屏幕适配上,使用原生控件开发成本已不是那么理想,鉴于很多项目和iOS基于一致的ui界面,至使安卓UI开发成本花费更大的代价,因此目前结合Html5和原生控件是解决UI适配的一种很好的选择,处于APP性能也会用Java和native层进行结合。不管是哪种结合,其实原理都差不多,只要按照它的协议来,是很容易的,今天我们仅对于Html和Java层结合,学习下一个新的开源项目--WebViewJavascriptBridge。

一 什么是webViewjavascripBridge?

WebViewJavascriptBridge是移动UIView和Html交互通信的桥梁,用作者的话来说就是实现java(ios为oc)和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得我们的开发更加灵活和安全。





二 为什么要用webViewjavascripBridge?

对于安卓开发有一段时间的人来说,知道安卓4.4以前谷歌的webview存在安全漏洞,网站可以通过js注入就可以随便拿到客户端的重要信息,甚至轻而易举的调用本地代码进行流氓行为,谷歌后来发现有此漏洞后,增加了防御措施,如果要是js调用本地代码,开发者 必须在代码申明 JavascriptInterface,
列如在4.0之前我们要使得webView加载js只需如下代码:
  1. mWebView.addJavascriptInterface(newJsToJava(),"myjsfunction");

4.4之后调用需要在调用方法加入加入@JavascriptInterface注解,如果代码无此申明,那么也就无法使得js生效,也就是说这样就可以避免恶意网页利用js对安卓客户端的窃取和攻击。

但是即使这样,我们很多时候需要在js记载本地代码的时候,要做一些判断和限制,或者有可能也会做些过滤和对用户友好提示,因此JavascriptInterface也就无法满足我们的需求了,特此有大神就写出了WebViewJavascriptBridge框架。



三 怎么使用webViewjavascripBridge


1 将jsBridge.jar引入到我们的工程

此jar,或者jar源码我们可以到gitHub上下载。

2 WebView包需使用以上包的webView


Layout中使用第三方view,
EG:
  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:orientation="vertical">
  6. <!--button演示Java调用web-->
  7. <Button
  8. android:id="@+id/button"
  9. android:layout_width="match_parent"
  10. android:text="@string/button_name"
  11. android:layout_height="48dp"
  12. />
  13. <!--webview演示web调用Java-->
  14. <com.github.lzyzsd.jsbridge.BridgeWebView
  15. android:id="@+id/webView"
  16. android:layout_width="match_parent"
  17. android:layout_height="match_parent">
  18. </com.github.lzyzsd.jsbridge.BridgeWebView>
  19. </LinearLayout>

3 Java代码

  1. publicclassMainActivityextendsActivityimplementsOnClickListener{
  2. privatefinalStringTAG="MainActivity";
  3. BridgeWebViewwebView;
  4. Buttonbutton;
  5. intRESULT_CODE=0;
  6. ValueCallback<Uri>mUploadMessage;
  7. staticclassLocation{
  8. Stringaddress;
  9. }
  10. staticclassUser{
  11. Stringname;
  12. Locationlocation;
  13. StringtestStr;
  14. }
  15. @Override
  16. protectedvoidonCreate(BundlesavedInstanceState){
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_main);
  19. webView=(BridgeWebView)findViewById(R.id.webView);
  20. button=(Button)findViewById(R.id.button);
  21. button.setOnClickListener(this);
  22. webView.setDefaultHandler(newDefaultHandler());
  23. webView.setWebChromeClient(newWebChromeClient(){
  24. @SuppressWarnings("unused")
  25. publicvoidopenFileChooser(ValueCallback<Uri>uploadMsg,StringAcceptType,Stringcapture){
  26. this.openFileChooser(uploadMsg);
  27. }
  28. @SuppressWarnings("unused")
  29. publicvoidopenFileChooser(ValueCallback<Uri>uploadMsg,StringAcceptType){
  30. this.openFileChooser(uploadMsg);
  31. }
  32. publicvoidopenFileChooser(ValueCallback<Uri>uploadMsg){
  33. mUploadMessage=uploadMsg;
  34. pickFile();
  35. }
  36. });
  37. //加载本地网页
  38. //webView.loadUrl("file:///android_asset/demo.html");
  39. //加载服务器网页
  40. webView.loadUrl("https://www.baidu.com");
  41. //必须和js同名函数,注册具体执行函数,类似java实现类。
  42. webView.registerHandler("submitFromWeb",newBridgeHandler(){
  43. @Override
  44. publicvoidhandler(Stringdata,CallBackFunctionfunction){
  45. Stringstr="这是html返回给java的数据:"+data;
  46. //例如你可以对原始数据进行处理
  47. makeText(MainActivity.this,str,LENGTH_SHORT).show();
  48. Log.i(TAG,"handler=submitFromWeb,datafromweb="+data);
  49. function.onCallBack(str+",Java经过处理后截取了一部分:"+str.substring(0,5));
  50. }
  51. });
  52. //模拟用户获取本地位置
  53. Useruser=newUser();
  54. Locationlocation=newLocation();
  55. location.address="上海";
  56. user.location=location;
  57. user.name="Bruce";
  58. webView.callHandler("functionInJs",newGson().toJson(user),newCallBackFunction(){
  59. @Override
  60. publicvoidonCallBack(Stringdata){
  61. makeText(MainActivity.this,"网页在获取你的位置",LENGTH_SHORT).show();
  62. }
  63. });
  64. webView.send("hello");
  65. }
  66. publicvoidpickFile(){
  67. IntentchooserIntent=newIntent(Intent.ACTION_GET_CONTENT);
  68. chooserIntent.setType("image/*");
  69. startActivityForResult(chooserIntent,RESULT_CODE);
  70. }
  71. @Override
  72. protectedvoidonActivityResult(intrequestCode,intresultCode,Intentintent){
  73. if(requestCode==RESULT_CODE){
  74. if(null==mUploadMessage){
  75. return;
  76. }
  77. Uriresult=intent==null||resultCode!=RESULT_OK?null:intent.getData();
  78. mUploadMessage.onReceiveValue(result);
  79. mUploadMessage=null;
  80. }
  81. }
  82. @Override
  83. publicvoidonClick(Viewv){
  84. if(button.equals(v)){
  85. webView.callHandler("functionInJs","datafromJava",newCallBackFunction(){
  86. @Override
  87. publicvoidonCallBack(Stringdata){
  88. //TODOAuto-generatedmethodstub
  89. Log.i(TAG,"reponsedatafromjs"+data);
  90. }
  91. });
  92. }
  93. }
  94. }

通过实例化webView,用法和安卓原生的view没多大区别,设置WebChromClient, 设置加载的html(同样支持网络和本地文件),接着我们需要给web注册和html端约定好的js方法名。代码列举的 submitFromweb和js的执行的方法名一致,玩过NDK的JNI调用的朋友也知道必须和c代码之间有个约定,其实js桥和jni有点类似,

通过注册handler来实现回调,Java代码中通过js返回的数据,进行处理后在调用function.onCallback返回给js.这里不做过多解释

  1. //必须和js同名函数,注册具体执行函数,类似java实现类。
  2. webView.registerHandler("submitFromWeb",newBridgeHandler(){
  3. @Override
  4. publicvoidhandler(Stringdata,CallBackFunctionfunction){
  5. Stringstr="这是html返回给java的数据:"+data;
  6. //例如你可以对原始数据进行处理
  7. makeText(MainActivity.this,str,LENGTH_SHORT).show();
  8. Log.i(TAG,"handler=submitFromWeb,datafromweb="+data);
  9. function.onCallBack(str+",Java经过处理后截取了一部分:"+str.substring(0,5));
  10. }
  11. });

如果是webview刚开始就执行一段Java代码 ,可以通过webView.CallHandler()来实现 。当然我们注册的方法也要和js里面的方法名一致。
  1. webView.callHandler("functionInJs",newGson().toJson(user),newCallBackFunction(){
  2. @Override
  3. publicvoidonCallBack(Stringdata){
  4. makeText(MainActivity.this,"网页在获取你的位置",LENGTH_SHORT).show();
  5. }
  6. });

3 Html和js实现

html代码如下,效果图



  1. <html>
  2. <head>
  3. <metacontent="text/html;charset=utf-8"http-equiv="content-type">
  4. <title>
  5. js调用java
  6. </title>
  7. </head>
  8. <body>
  9. <p>
  10. <xmpid="show">
  11. </xmp>
  12. </p>
  13. <p>
  14. <xmpid="init">
  15. </xmp>
  16. </p>
  17. <p>
  18. <inputtype="text"id="text1"value="用户名(username)"/>
  19. </p>
  20. <p>
  21. <inputtype="text"id="text2"value="password"/>
  22. </p>
  23. <p>
  24. <inputtype="button"id="enter"value="发消息给Native"onclick="testClick();"
  25. />
  26. </p>
  27. <p>
  28. <inputtype="button"id="enter1"value="调用Native方法"onclick="testClick1();"
  29. />
  30. </p>
  31. <p>
  32. <inputtype="button"id="enter2"value="显示html"onclick="testDiv();"/>
  33. </p>
  34. <p>
  35. <inputtype="file"value="打开文件"/>
  36. </p>
  37. </body>
  38. </html>

js代码


[javascript] view plain copy
print ?
  1. <script>
  2. functiontestDiv(){
  3. document.getElementById("show").innerHTML=document.getElementsByTagName("html")[0].innerHTML;
  4. }
  5. functiontestClick(){
  6. varstr1=document.getElementById("text1").value;
  7. varstr2=document.getElementById("text2").value;
  8. //发送消息给java代码
  9. vardata="name="+str1+",pass="+str2;
  10. window.WebViewJavascriptBridge.send(
  11. data
  12. ,function(responseData){
  13. document.getElementById("show").innerHTML="repsonseDatafromjava,data="+responseData
  14. }
  15. );
  16. }
  17. functiontestClick1(){
  18. varstr1=document.getElementById("text1").value;
  19. varstr2=document.getElementById("text2").value;
  20. //调用本地java方法
  21. window.WebViewJavascriptBridge.callHandler(
  22. 'submitFromWeb'
  23. ,{'param':str1}
  24. ,function(responseData){
  25. document.getElementById("show").innerHTML="sendgetresponseDatafromjava,data="+responseData
  26. }
  27. );
  28. }
  29. functionbridgeLog(logContent){
  30. document.getElementById("show").innerHTML=logContent;
  31. }//注册事件监听
  32. functionconnectWebViewJavascriptBridge(callback){
  33. if(window.WebViewJavascriptBridge){
  34. callback(WebViewJavascriptBridge)
  35. }else{
  36. document.addEventListener(
  37. 'WebViewJavascriptBridgeReady'
  38. ,function(){
  39. callback(WebViewJavascriptBridge)
  40. },
  41. false
  42. );
  43. }
  44. }
  45. //注册回调函数,初始化函数
  46. connectWebViewJavascriptBridge(function(bridge){
  47. bridge.init(function(message,responseCallback){
  48. console.log('JSgotamessage',message);
  49. vardata={
  50. 'JavascriptResponds':'Wee!'
  51. };
  52. console.log('JSrespondingwith',data);
  53. responseCallback(data);
  54. });
  55. bridge.registerHandler("functionInJs",function(data,responseCallback){
  56. document.getElementById("show").innerHTML=("datafromJava:="+data);
  57. varresponseData="JavascriptSaysRightbackaka!";
  58. responseCallback(responseData);
  59. });
  60. })
  61. </script>

这段代码不难理解,我们对上面的id为enter的Button注册了一个点击事件,点击后执行以下testClick()方法,依次类推,其他Button注册事件相同,


当点击“发消息给Native”的按钮时,Js通过webWiew的jsBridage.send()发送一条数据给java层(密码和用户名),同时在此用function()来执行应java层回调函数的。此demo中是把java返回的数据插入到"show"的div里面去。

testClick1():此方法中调用callHandler来调用Java代码的submitFromweb同名函数,可以结合上面的Activty的代码理解下,此函数调用我们已在java注册实现好的

  1. //必须和js同名函数,注册具体执行函数,类似java实现类。
  2. webView.registerHandler("submitFromWeb",newBridgeHandler(){
  3. @Override
  4. publicvoidhandler(Stringdata,CallBackFunctionfunction){
  5. Stringstr="这是html返回给java的数据:"+data;
  6. //例如你可以对原始数据进行处理
  7. makeText(MainActivity.this,str,LENGTH_SHORT).show();
  8. Log.i(TAG,"handler=submitFromWeb,datafromweb="+data);
  9. function.onCallBack(str+",Java经过处理后截取了一部分:"+str.substring(0,5));
  10. }
  11. });

java注册Js函数如上面列子 ,那么在js中注册方法来注册呢,

在js中我们同样可以直接注册一个回调函数,通过bridge.registerHandler()来注册,接着调用responseCallback(responseData)来返回数据给java代码的同名函数。f

也可以直接调用init()来指定网页首次加载上面注册java代码。

  1. connectWebViewJavascriptBridge(function(bridge){
  2. bridge.init(function(message,responseCallback){
  3. console.log('JSgotamessage',message);
  4. vardata={
  5. 'JavascriptResponds':'Wee!'
  6. };
  7. console.log('JSrespondingwith',data);
  8. responseCallback(data);
  9. });
  10. bridge.registerHandler("functionInJs",function(data,responseCallback){
  11. document.getElementById("show").innerHTML=("datafromJava:="+data);
  12. varresponseData="JavascriptSaysRightbackaka!";
  13. responseCallback(responseData);
  14. });
  15. })


四 总结

通过以上的代码示例,不难发现此框架的优雅和简便,对js和java双方来说,如果html中的js需要调用java代码,而java代码没做任何实现,那么这个js也是无效的,反之java代码注册的回调函数,没在js里去实现,那么Java也无法获取js远程数据,由此可见此,此通信是双方的,必须由双发来约定,这样就避免了安卓之前存在js注入漏洞,也很大的提高了安全性,也可以保证我们的网页数据不被第三方的APP获取,怎么说呢,比如你的项目某一html界面,被安卓浏览器或者其他第三方App恶意加载你们的网站或网页,那么它的java代码调用你的js函数,比如由你的Js事先注册或者实现,不然跟本无法调用。这样就保证了这个html的唯一性,第三方可以加载预览你的网页,但是第三方java 不能和你的js交互通信。同样加载第三方的网页时候,我们可以对第三方网页进行一些行为的过滤,进而对用户进行友好提示。
以上代码只是此开源框架一部分功能,还有很多的功能需要我们去挖掘,以后再给大家解剖下此开源项目的内部实现。欢迎阅读


项目开源地址:https://github.com/lzyzsd/JsBridge



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值