最近研究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)

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

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

协议解析完整实现类:

public class JsProcessor {

//    private JsProcessor(){}

    private JsResponse  mJsRes;

    public  JsResponse getJsResponse(){
return mJsRes;
} public static boolean isProtocol(String url) {
return !TextUtils.isEmpty(url) && url.startsWith("jsbridge://");
} // jsbridge://android-app/method123?a=123&b=345#jsMethod1(p1,p2)"
public boolean parseJsBridge(String url,WebAppInterface webNative){
if(!isProtocol(url)) return false;
int i = -1;
Uri uri = Uri.parse(url);
String methodName = uri.getPath();//method1
methodName = methodName.replace("/", ""); String params = uri.getQuery();//a=123&b=345
String callback = uri.getFragment();//#jsMethod1(p1,p2) i = callback.lastIndexOf('(');
String jsMethod = callback.substring(0,i);
String jsParams = callback.substring(i+1,callback.length()-1); mJsRes = new JsResponse(methodName,jsMethod);
mJsRes.parseJsCallbackParams(jsParams);
mJsRes.parseNativeParams(params); String[] args = mJsRes.getMethodArgs();
if(null==args){
jsCallNoParamMethod(webNative,methodName);
}else{
int count = args.length;
Class[] javaParamsType = new Class[count];
for(int t=0;t<count;t++){
javaParamsType[t] = String.class;
}
jsCallParamMethod(webNative,methodName,javaParamsType,mJsRes.getMethodArgs());
}
return true;
} public boolean jsCallNoParamMethod(Object obj,String mName){
boolean success = false;
try {
Method method=obj.getClass().getMethod(mName);
method.invoke(obj);
success = true;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}catch (Exception ex){
ex.printStackTrace();
}
return success;
} //getMethod("sayHello", String.class,int.class);
public boolean jsCallParamMethod(Object obj,String mName,Class<?>[] parameterTypes,Object[] args){
if(parameterTypes.length!=args.length) return false;
boolean success = false;
try {
Method method = obj.getClass().getMethod(mName,parameterTypes);
method.invoke(obj, args);
success = true;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}catch (Exception ex){
ex.printStackTrace();
}
return success;
} }

JSResponse解析后的封装实体类

public class JsResponse {
//call native
public String methodName;
private Map<String,String> nativeParams = new HashMap<>();
//callback js method
public String callJsMethod;
// public Map<String,String> jsParams = new HashMap<>();
private int jsParamCount; public JsResponse(String javaMethod,String jsMethod){
this.methodName = javaMethod;
this.callJsMethod = jsMethod;
} public String[] getMethodArgs(){
if(0==nativeParams.size()) return null;
String[] params = new String[nativeParams.size()];
int n = 0;
for(Map.Entry<String,String> entry:nativeParams.entrySet()){
params[n] = entry.getValue();
n++;
}
return params;
} public void parseNativeParams(String params){
// int i = 0,len = params.length();
if(!nativeParams.isEmpty())
nativeParams.clear(); String[] eles = params.split("&");
for(String ele:eles){
int eq = ele.indexOf('=');
String value = ele.substring(eq + 1);
String name = ele.substring(0,eq);
nativeParams.put(name,value);
} } public void parseJsCallbackParams(String jsparams){
String[] eles = jsparams.split(",");
jsParamCount = eles.length;
} }

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

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

本地调用js回调方法

  void executeJsCmd(String jsCmd){
Log.d(TAG,"pending execute js command="+jsCmd); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//async execute
mWebView.evaluateJavascript(jsCmd, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.d(TAG,"async execute result="+value);
}
});
}else {
// This code is BAD and will block the UI thread
mWebView.loadUrl(jsCmd);
}
}

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

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

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

private class AppWebChromeClient extends WebChromeClient {
@Override
public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(spaceNeeded * 2);
} @Override
public boolean onConsoleMessage(ConsoleMessage cm) {
Log.d("MyApplication", cm.message() + " -- From line "
+ cm.lineNumber() + " of "
+ cm.sourceId() );
return true;
} @Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
if("1001".equals(message)){
//解析参数defaultValue,调用java方法并得到结果
//textexeJs();
}
//给js返回处理结果
result.confirm("result");
return false;
} @Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d("MyApplication",url);
AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
// Setting Dialog Title
alertDialog.setTitle("JS come message");
// Setting Dialog Message
alertDialog.setMessage(message);
alertDialog.show();
return false;
}
}

调用本地方法类对象

public class WebAppInterface {

    Context mContext;

    /** Instantiate the interface and set the context */
WebAppInterface(Context c) {
mContext = c;
} /** Show a toast from the web page */
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
} @JavascriptInterface
public void showDialog(String dialogMsg){
AlertDialog alertDialog = new AlertDialog.Builder(mContext).create();
// Setting Dialog Title
alertDialog.setTitle("JS triggered Dialog");
// Setting Dialog Message
alertDialog.setMessage(dialogMsg);
alertDialog.show();
} public void showHello(String title,String msg){
ToastUtils.showLong(mContext,title+msg);
} }

js示例内容

<html>
<head>
<style>
body{
background-color: #FA5858;
color:#fff;
}
input{
background-color: #F7D358;
width: 300px;
padding:10px;
color: #000;
}
div#content{
padding:20px;
background-color: #F7D358;
color: #000;
}
</style>
<script type="text/javascript">
function showAndroidToast(toastmsg) {
Android.showToast(toastmsg);
}
function showAndroidDialog(dialogmsg) {
Android.showDialog(dialogmsg);
}
function moveToScreenTwo() {
Android.moveToNextScreen();
}
function showPromptToJava()
{
var ret = prompt( "1001", "defaultMessage001" );
//ret值即为java传回的”result”
//根据返回内容作相应处理
}
function showJavaMessage(result)
{
alert("hello!"+result);
}
function startRequest()
{
var iframe = document.createElement('iframe');
iframe.setAttribute('src', 'jsbridge://android-app/showHello?a=hello-&b=i_from_js&#callbackHello(p1,p2)');
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
function callbackHello(p1,p2)
{
console.log("receive callback from app");
alert("receive callback "+p1+p2);
}
</script>
</head>
<body>
<center>
<h3>Binding JavaScript code to Android code</h3>
<div id="content">
//some content here
</div>
<div>
Here are few examples:
</div>
<div>
<input type="button" value="Make Toast" onClick="showAndroidToast('Toast made by Javascript')" /><br/>
<input type="button" value="Trigger Dialog" onClick="showAndroidDialog('This dialog is triggered by Javascript ')" /><br/>
<input type="button" value="Take me to Next Screen" onClick="showPromptToJava()" /><br/>
<input type="button" value="jsBridgeHandle" onClick="startRequest()" /><br/>
</div>
</center>
</body>
</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. Mybatis-9.28

    Mybatis-9.28 环境: JDK1.8 Mysql 5.7 maven 3.6.1 IDEA 回顾: JDBC Mysql Java基础 Maven Junit SSM框架:配置文件的. 最好 ...

  2. java中json字符串与实体类对象相互转换

    1.问题描述 有一个需求是这样的,把实体类转为Json字符串存入redis中,然后再把redis中存放的实体类Json字符串插入数据库中.因此需要涉及到json字符串与实体类对象的相互转换. 2.产生 ...

  3. JUC源码学习笔记7——FutureTask源码解析,人生亦如是,run起来才有结果

    系列文章目录和关于我 一丶我们在哪里会使用到FutureTask 基本上工作中和Future接口 打交道比较多,比如线程池ThreadPoolExecutor#sumbit方法,返回值就是一个Futu ...

  4. CVE-2020-13933

    漏洞名称 Apache Shiro 身份验证绕过漏洞复现CVE-2020-13933 利用条件 Apache Shiro < 1.6.0 漏洞原理 Apache Shiro是一个强大且易用的Ja ...

  5. FFmpeg 解码内存泄漏汇总,sws_getContext函数无法释放问题

    使用FFmpeg库做的项目,调试项目的时候发现,连续解视频进行播放,会有明显内存增加问题.连续工作10个小时后就会 被linux 内核kill掉. 通过逐步注掉代码和网上查阅资料.最后发现内存泄漏有一 ...

  6. CF1779C Least Prefix Sum 题解

    CF链接:Least Prefix Sum Luogu链接:Least Prefix Sum $ {\scr \color {CornflowerBlue}{\text{Solution}}} $ 先 ...

  7. Python3+Selenium3自动化测试-(九)

    selenium的学习拖拉了很久,内容也是较基础的部分,实际应用时查询文档也是OK的,当然,整理出来会更有利于我们在实际使用时去应用. 所以这一篇把官方的API文档进行解读整理,在看完这些API,将能 ...

  8. 使用ProPerties集合存储数据-Properties集合中的方法store

    使用ProPerties集合存储数据 java.util.Properties`继承于Hashtable,来表示一个持久的属性集.它使用键值结构存储数据每个键及其对应值都是一个字符串.该类也被许多Ja ...

  9. 【分析笔记】NXP PCF85263 设备驱动分析笔记

    驱动移植 供应商无法提供相应的驱动程序,不过在 linux 最新的内核倒是有一份 pcf85363 的驱动,看代码并核对寄存器功能,是可以兼容 pcf85263 芯片.只是我们用的内核比较老 linu ...

  10. Fabric2.x中Raft共识算法核心数据结构

    一.共识算法可插拔的代码体现Chain接口 Hyperledger Fabric的共识算法是可插拔的,在代码上体现为Chain接口,所有不同的共识算法均可根据Chain接口进行具体实现,目前fabri ...