最近研究webview与js交互,看了几个开源库实现,感觉不尽如人意,存在主要问题是,耦合较高,使用不够简洁,后来参考Uri设定规则,格局Uri类似协议自定义了类似的js交互协议

比较简洁,自定义协议内容样式如:jsbridge://android-app/method123?a=123&b=345#jsMethod1(p1,p2)

协议说明:

scheme定义为jsbridge,用于区分别的网络请求(http),
authority定义为android-app,区分不同平台处理
path定义为 调用本地方法名称    method123
Query定义为调用本地方法参数    a=123&b=345
Fragment定义为回调js方法    #jsMethod1(p1,p2)

这样就可以做到精简,灵活,良好的可扩展,灵活使用的特点。再看解析实现·

  1. public boolean parseJsBridge(String url,WebAppInterface webNative){//WebAppInterface为被调用本地方法类实例
  2. if(!isProtocol(url)) return false;
  3. int i = -1;
  4. Uri uri = Uri.parse(url);//借助URI解析协议
  5. String methodName = uri.getPath();//调用本地方法,method123
  6. methodName = methodName.replace("/", "");
  7.  
  8. String params = uri.getQuery();//调用本地方法参数。a=123&b=345
  9. String callback = uri.getFragment();//解析js回调方法,#jsMethod1(p1,p2)
  10.  
  11. i = callback.lastIndexOf('(');
  12. String jsMethod = callback.substring(0,i);
  13. String jsParams = callback.substring(i+1,callback.length()-1);
  14. //将解析的结果封装为JsResponse,便于后续使用
  15. mJsRes = new JsResponse(methodName,jsMethod);
  16. mJsRes.parseJsCallbackParams(jsParams);
  17. mJsRes.parseNativeParams(params);
    //这里通过反射方式调用本地方法
  18. String[] args = mJsRes.getMethodArgs();
  19. if(null==args){//使用反射调用无参数方法
  20. jsCallNoParamMethod(webNative,methodName);
  21. }else{//调用反射有参数方法
  22. int count = args.length;
  23. Class[] javaParamsType = new Class[count];
  24. for(int t=0;t<count;t++){
  25. javaParamsType[t] = String.class;
  26. }
  27. jsCallParamMethod(webNative,methodName,javaParamsType,mJsRes.getMethodArgs());
  28. }
  29. return true;
  30. }

协议解析完整实现类:

  1. public class JsProcessor {
  2.  
  3. // private JsProcessor(){}
  4.  
  5. private JsResponse mJsRes;
  6.  
  7. public JsResponse getJsResponse(){
  8. return mJsRes;
  9. }
  10.  
  11. public static boolean isProtocol(String url) {
  12. return !TextUtils.isEmpty(url) && url.startsWith("jsbridge://");
  13. }
  14.  
  15. // jsbridge://android-app/method123?a=123&b=345#jsMethod1(p1,p2)"
  16. public boolean parseJsBridge(String url,WebAppInterface webNative){
  17. if(!isProtocol(url)) return false;
  18. int i = -1;
  19. Uri uri = Uri.parse(url);
  20. String methodName = uri.getPath();//method1
  21. methodName = methodName.replace("/", "");
  22.  
  23. String params = uri.getQuery();//a=123&b=345
  24. String callback = uri.getFragment();//#jsMethod1(p1,p2)
  25.  
  26. i = callback.lastIndexOf('(');
  27. String jsMethod = callback.substring(0,i);
  28. String jsParams = callback.substring(i+1,callback.length()-1);
  29.  
  30. mJsRes = new JsResponse(methodName,jsMethod);
  31. mJsRes.parseJsCallbackParams(jsParams);
  32. mJsRes.parseNativeParams(params);
  33.  
  34. String[] args = mJsRes.getMethodArgs();
  35. if(null==args){
  36. jsCallNoParamMethod(webNative,methodName);
  37. }else{
  38. int count = args.length;
  39. Class[] javaParamsType = new Class[count];
  40. for(int t=0;t<count;t++){
  41. javaParamsType[t] = String.class;
  42. }
  43. jsCallParamMethod(webNative,methodName,javaParamsType,mJsRes.getMethodArgs());
  44. }
  45. return true;
  46. }
  47.  
  48. public boolean jsCallNoParamMethod(Object obj,String mName){
  49. boolean success = false;
  50. try {
  51. Method method=obj.getClass().getMethod(mName);
  52. method.invoke(obj);
  53. success = true;
  54. } catch (NoSuchMethodException e) {
  55. e.printStackTrace();
  56. } catch (IllegalAccessException e) {
  57. e.printStackTrace();
  58. } catch (InvocationTargetException e) {
  59. e.printStackTrace();
  60. }catch (Exception ex){
  61. ex.printStackTrace();
  62. }
  63. return success;
  64. }
  65.  
  66. //getMethod("sayHello", String.class,int.class);
  67. public boolean jsCallParamMethod(Object obj,String mName,Class<?>[] parameterTypes,Object[] args){
  68. if(parameterTypes.length!=args.length) return false;
  69. boolean success = false;
  70. try {
  71. Method method = obj.getClass().getMethod(mName,parameterTypes);
  72. method.invoke(obj, args);
  73. success = true;
  74. } catch (NoSuchMethodException e) {
  75. e.printStackTrace();
  76. } catch (IllegalAccessException e) {
  77. e.printStackTrace();
  78. } catch (InvocationTargetException e) {
  79. e.printStackTrace();
  80. }catch (Exception ex){
  81. ex.printStackTrace();
  82. }
  83. return success;
  84. }
  85.  
  86. }

JSResponse解析后的封装实体类

  1. public class JsResponse {
  2. //call native
  3. public String methodName;
  4. private Map<String,String> nativeParams = new HashMap<>();
  5. //callback js method
  6. public String callJsMethod;
  7. // public Map<String,String> jsParams = new HashMap<>();
  8. private int jsParamCount;
  9.  
  10. public JsResponse(String javaMethod,String jsMethod){
  11. this.methodName = javaMethod;
  12. this.callJsMethod = jsMethod;
  13. }
  14.  
  15. public String[] getMethodArgs(){
  16. if(0==nativeParams.size()) return null;
  17. String[] params = new String[nativeParams.size()];
  18. int n = 0;
  19. for(Map.Entry<String,String> entry:nativeParams.entrySet()){
  20. params[n] = entry.getValue();
  21. n++;
  22. }
  23. return params;
  24. }
  25.  
  26. public void parseNativeParams(String params){
  27. // int i = 0,len = params.length();
  28. if(!nativeParams.isEmpty())
  29. nativeParams.clear();
  30.  
  31. String[] eles = params.split("&");
  32. for(String ele:eles){
  33. int eq = ele.indexOf('=');
  34. String value = ele.substring(eq + 1);
  35. String name = ele.substring(0,eq);
  36. nativeParams.put(name,value);
  37. }
  38.  
  39. }
  40.  
  41. public void parseJsCallbackParams(String jsparams){
  42. String[] eles = jsparams.split(",");
  43. jsParamCount = eles.length;
  44. }
  45.  
  46. }

js使用规则协议,触发调用本地方法如下,即可

  1. function startRequest()
  2. {
  3. var iframe = document.createElement('iframe');
  4. iframe.setAttribute('src', 'jsbridge://android-app/showHello?a=hello-&b=i_from_js&#callbackHello(p1,p2)');
  5. document.body.appendChild(iframe);
  6. iframe.parentNode.removeChild(iframe);
  7. iframe = null;
  8. }

本地调用js回调方法

  1. void executeJsCmd(String jsCmd){
  2. Log.d(TAG,"pending execute js command="+jsCmd);
  3.  
  4. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  5. //async execute
  6. mWebView.evaluateJavascript(jsCmd, new ValueCallback<String>() {
  7. @Override
  8. public void onReceiveValue(String value) {
  9. Log.d(TAG,"async execute result="+value);
  10. }
  11. });
  12. }else {
  13. // This code is BAD and will block the UI thread
  14. mWebView.loadUrl(jsCmd);
  15. }
  16. }

下面就是js与本地通信了,通过WebViewClient的shouldOverrideUrlLoading拦截内容判断处理

  1. private class MonitorWebClient extends WebViewClient {
  2. WebAppInterface webCallapp = new WebAppInterface(activity);
  3. .......
  4.  
  5. @Override
  6. public boolean shouldOverrideUrlLoading(WebView view, String url) {
  7. boolean handle = super.shouldOverrideUrlLoading(view, url);
  8. JsProcessor jsPro = null;
  9. if (!handle) {
  10. jsPro = new JsProcessor();
  11. handle = jsPro.parseJsBridge(url,webCallapp);
  12. }
  13. if(handle){
  14. handleJsCallback(jsPro,"hello","callback js method");
  15. }
  16. return handle;
  17. }
  18.  
  19. @Override
  20. public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
  21. //如果需要传文件,拦截WebView的资源请求,将文件以流的形式进行通讯
  22. //return new WebResourceResponse("image/jpeg", "UTF-8", new FileInputStream(new File("xxxx");
  23. return super.shouldInterceptRequest(view, request);
  24. }
  25. }
  1. void handleJsCallback(JsProcessor jsPro,String... args){
    //javascript:showJavaMessage('javaResult')"
    //拼接js要执行方法名字与参数规则,如上样例
  2. String jsM = jsPro.getJsResponse().callJsMethod;
  3. if(!TextUtils.isEmpty(jsM)){
  4. // String jsCmd = new StringBuilder().append("javascript:").append(jsM).
  5. // append('(').append("\'java\'").append(',').append("\'hello\'").append(')').toString();
  6. StringBuilder buf = new StringBuilder().append("javascript:").append(jsM).append('(');
  7. if(args!=null && args.length>0){
  8. // int pos = buf.length()-1;
  9. for(String p:args) {
  10. buf.append('\'').append(p).append('\'').append(',');
  11. }
  12. buf.deleteCharAt(buf.length()-1);
  13. }
  14. buf.append(')');
  15. executeJsCmd(buf.toString());
  16. }
  17. }

除了上面shouldOverrideUrlLoading方法拦截处理外,js也可以通过调用 alert,prompt 方法的方式传递数据 ,我们需要重写WebChromeClient 通过回调本地方法onJsAlert(), onJsPrompt()方法即可收到对应函数回调,进行数据处理

  1. private class AppWebChromeClient extends WebChromeClient {
  2. @Override
  3. public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
  4. quotaUpdater.updateQuota(spaceNeeded * 2);
  5. }
  6.  
  7. @Override
  8. public boolean onConsoleMessage(ConsoleMessage cm) {
  9. Log.d("MyApplication", cm.message() + " -- From line "
  10. + cm.lineNumber() + " of "
  11. + cm.sourceId() );
  12. return true;
  13. }
  14.  
  15. @Override
  16. public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
  17. if("1001".equals(message)){
  18. //解析参数defaultValue,调用java方法并得到结果
  19. //textexeJs();
  20. }
  21. //给js返回处理结果
  22. result.confirm("result");
  23. return false;
  24. }
  25.  
  26. @Override
  27. public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
  28. Log.d("MyApplication",url);
  29. AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
  30. // Setting Dialog Title
  31. alertDialog.setTitle("JS come message");
  32. // Setting Dialog Message
  33. alertDialog.setMessage(message);
  34. alertDialog.show();
  35. return false;
  36. }
  37. }

调用本地方法类对象

  1. public class WebAppInterface {
  2.  
  3. Context mContext;
  4.  
  5. /** Instantiate the interface and set the context */
  6. WebAppInterface(Context c) {
  7. mContext = c;
  8. }
  9.  
  10. /** Show a toast from the web page */
  11. @JavascriptInterface
  12. public void showToast(String toast) {
  13. Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
  14. }
  15.  
  16. @JavascriptInterface
  17. public void showDialog(String dialogMsg){
  18. AlertDialog alertDialog = new AlertDialog.Builder(mContext).create();
  19. // Setting Dialog Title
  20. alertDialog.setTitle("JS triggered Dialog");
  21. // Setting Dialog Message
  22. alertDialog.setMessage(dialogMsg);
  23. alertDialog.show();
  24. }
  25.  
  26. public void showHello(String title,String msg){
  27. ToastUtils.showLong(mContext,title+msg);
  28. }
  29.  
  30. }

js示例内容

  1. <html>
  2. <head>
  3. <style>
  4. body{
  5. background-color: #FA5858;
  6. color:#fff;
  7. }
  8. input{
  9. background-color: #F7D358;
  10. width: 300px;
  11. padding:10px;
  12. color: #000;
  13. }
  14. div#content{
  15. padding:20px;
  16. background-color: #F7D358;
  17. color: #000;
  18. }
  19. </style>
  20. <script type="text/javascript">
  21. function showAndroidToast(toastmsg) {
  22. Android.showToast(toastmsg);
  23. }
  24. function showAndroidDialog(dialogmsg) {
  25. Android.showDialog(dialogmsg);
  26. }
  27. function moveToScreenTwo() {
  28. Android.moveToNextScreen();
  29. }
  30. function showPromptToJava()
  31. {
  32. var ret = prompt( "1001", "defaultMessage001" );
  33. //ret值即为java传回的”result”
  34. //根据返回内容作相应处理
  35. }
  36. function showJavaMessage(result)
  37. {
  38. alert("hello!"+result);
  39. }
  40. function startRequest()
  41. {
  42. var iframe = document.createElement('iframe');
  43. iframe.setAttribute('src', 'jsbridge://android-app/showHello?a=hello-&b=i_from_js&#callbackHello(p1,p2)');
  44. document.body.appendChild(iframe);
  45. iframe.parentNode.removeChild(iframe);
  46. iframe = null;
  47. }
  48. function callbackHello(p1,p2)
  49. {
  50. console.log("receive callback from app");
  51. alert("receive callback "+p1+p2);
  52. }
  53. </script>
  54. </head>
  55. <body>
  56. <center>
  57. <h3>Binding JavaScript code to Android code</h3>
  58. <div id="content">
  59. //some content here
  60. </div>
  61. <div>
  62. Here are few examples:
  63. </div>
  64. <div>
  65. <input type="button" value="Make Toast" onClick="showAndroidToast('Toast made by Javascript')" /><br/>
  66. <input type="button" value="Trigger Dialog" onClick="showAndroidDialog('This dialog is triggered by Javascript ')" /><br/>
  67. <input type="button" value="Take me to Next Screen" onClick="showPromptToJava()" /><br/>
  68. <input type="button" value="jsBridgeHandle" onClick="startRequest()" /><br/>
  69. </div>
  70. </center>
  71. </body>
  72. </html>

本地显示

mWebView.loadUrl("file:///android_asset/sample1.html");

以上就是整个js与本地方法调用整体流程

备注问题:

js与本地调用会产生的问题:

1,调用js函数参数注意
string 使用'' 需要单引号拼接,直接value或"value"则无法正常匹配
直接value为整数类型

2,onJsPrompt,onJsAlert,返回true,js默认不处理,导致下次无法调用(即无反应事件)

android webview与js简单的交互方案的更多相关文章

  1. [android] WebView与Js交互

    获取WebView对象 调用WebView对象的getSettings()方法,获取WebSettings对象 调用WebSettings对象的setJavaScriptEnabled()方法,设置j ...

  2. Android中webview和js之间的交互(转)

    http://www.cnblogs.com/leizhenzi/archive/2011/06/29/2093636.html 1.android中利用webview调用网页上的js代码. Andr ...

  3. Android Webview 与JS交互

    Android中 WebView控件支持JS与本地代码的交互. // 是否允许在webview中执行javascript webSettings.setJavaScriptEnabled(true); ...

  4. android控件之webview和js与java交互

    首先添加权限:<uses-permission android:name="android.permission.INTERNET"/> 布局文件: <Relat ...

  5. Android WebView 总结 —— Java和JavaScript交互

    交互如何实现 实现Java和js交互十分便捷.通常只需要以下几步. WebView开启JavaScript脚本执行 WebView设置供JavaScript调用的交互接口. 客户端和网页端编写调用对方 ...

  6. android Webview 实现js调用java代码实现Activity跳转

    今天有了一个需求,在android里webview加载的html页面,要求点击html页面的按钮实现Activity的跳转. 咱是是菜鸟,webview的接触不多,于是就和度娘来了次亲密接触.在其中也 ...

  7. android WebView中js的alert()失效

    WebView的设置代码 wv = (WebView) findViewById(R.id.webView1); wv.getSettings().setJavaScriptEnabled(true) ...

  8. android webview 中 js 模板引擎的使用

    最近在项目中要求用 webview 展示几个界面, 而后台返回的不是 html 而是 json 数据. 起初用 StringBuilder 一个一个拼 html, 后来感觉太繁琐,拼一个还行,拼多了就 ...

  9. Android Webview 调用JS跳转到指定activity

    JAVA: WebView wv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(save ...

  10. Xamarin Android Webview中JS调用App中的C#方法

    参考链接:https://github.com/xamarin/recipes/tree/master/Recipes/android/controls/webview/call_csharp_fro ...

随机推荐

  1. vue 组件之间传值(父传子,子传父)

    1.父传子 基本就用一个方式,props Father.vue(用v-bind(简写 : )  将父组件传的值绑定到子组件上) <template> <div> 我是爸爸:{{ ...

  2. pyCharm中下载包的速度慢的解决方案

    1.解决方案 使用阿里镜像 2.具体步骤 1.在项目里面新建一个xxx.py文件 2.然后将下面的代码复制进xxx.py文件 import os ini = "[global]\nindex ...

  3. cs231n学习笔记——lecture6 Training Neural Networks

    该博客主要用于个人学习记录,部分内容参考自:[基础]斯坦福cs231n课程视频笔记(三) 训练神经网络.[cs231n笔记]10.神经网络训练技巧(上).CS231n学习笔记-训练神经网络.整理学习之 ...

  4. 使用idea进行gitee代码管理

    目录 1.在idea插件市场安装gitee插件 2.把本地仓库的release分支上的代码合到dev分支上 3.把本地dev分支上的代码合到远程dev分支上去 1.在idea插件市场安装gitee插件 ...

  5. VSCode运行C/C++配置

    将MinGw安装目录下的 1.安装 VSCode 2.安装 MinGW 链接:点击跳转 3.MinGW 内安装两个模块 1.右键 Mark for Installation 勾选 (此处已安装好,所以 ...

  6. 迁移学习(IIMT)——《Improve Unsupervised Domain Adaptation with Mixup Training》

    论文信息 论文标题:Improve Unsupervised Domain Adaptation with Mixup Training论文作者:Shen Yan, Huan Song, Nanxia ...

  7. ORM执行原生SQL语句、双下划线数据查询、ORM外键字段的创建、外键字段的相关操作、ORM跨表查询、基于对象的跨表查询、基于双下划线的跨表查询、进阶查询操作

    今日内容 ORM执行SQL语句 有时候ROM的操作效率可能偏低 我们是可以自己编写sql的 方式1: models.User.objects.raw('select * from app01_user ...

  8. vue3实现一个抽奖小项目

    前言 在公司年会期间我做了个抽奖小项目,我把它分享出来,有用得着的可以看下. 浏览链接:http://xisite.top/original/luck-draw/index.html 项目链接:htt ...

  9. JS控制台打印星星,总有你要的那一款~呐~给你小心心哦~~~❤

    用JS语句,在控制台中打印星星,你要的是哪一款呢~来认领吧~ 1.左直角星星 效果: 代码: let readline=require("readline-sync"); cons ...

  10. 通俗易懂angular搭建