Android和js、H5进行交互数据(面试必问)
android和h5、js进行交互调用
android开发过程中,我们或多或少都会用到webview,使用webview来展示一些经常变动的界面更加方便简单, 也已于维护。另一方面hybrid app开发现在用的也越来越多了。其中native和h5之间的交互更是必不可少的。 具体android中是如何和h5交互的?或者说android中是如何和js交互的。
1 webview加载页面
我们都知道在android中是通过webview来加载html页面的。根据html文件所在的位置不同写法也不同。
例如:加载assets文件夹下的test,html页面
mwebview.loadurl("file:///android_asset/test.html")
例如:加载网页
mwebview.loadurl("https://www.baidu.com")
如果只是这样调用webview.loadurl 加载的话,那么当你点击页面中的链接的时候,页面将会在你手机默认的上打开。那如果想要在页面在app内中打开的话,那么就得设置serwebviewclient:
mwebview.setwebviewclient(new webviewclient() { @override public boolean shouldoverrideurlloading(webview view, string url) { //我们可以在这里拦截特定的rl请求,然后进行自己要的操作 if (url.equals("file:///android_asset/test2.html")) { log.e(tag, "shouldoverrideurlloading: " + url); startactivity(new intent(mainactivity.this,main2activity.class)); return true; } else { //这里我们自己重新加载新的url页面,防止点击链接跳转到系统浏览器 mwebview.loadurl(url); return true; } } } });
重写activity的onbackpressed方法,使得返回按钮不会关闭当前页面,而是返回webview上一个历史页面。
@override public void onbackpressed() { if (webview.cangoback()) { //返回上一个页 webview.goback(); return ; } super.onbackpressed(); }
二、给webview添加加载新页面的进度条。
开启和关闭进度条
webview.setwebviewclient(new webviewclient() { //重写页面打开和结束的监听。打开时弹出菊花,关闭时隐藏菊花 /** * 界面打开的回调 */ @override public void onpagestarted(webview view, string url, bitmap favicon) { if (progressdialog != null && progressdialog.isshowing()) { progressdialog.dismiss(); } //弹出菊花 progressdialog = new progressdialog(jsactivity.this); progressdialog.settitle("提示"); progressdialog.setmessage("软软正在拼命加载……"); progressdialog.show(); } /** * 界面打开完毕的回调 */ @override public void onpagefinished(webview view, string url) { //隐藏菊花:不为空,正在显示。才隐藏关闭 if (progressdialog != null && progressdialog.isshowing()) { progressdialog.dismiss(); } } });
让进度条显示页面加载进度:
//设置进度条 //webchromeclient与webviewclient的区别 //webviewclient处理偏界面的操作:打开新界面,界面打开,界面打开结束 //webchromeclient处理偏js的操作 webview.setwebchromeclient(new webchromeclient() { /** * 进度改变的回调 * webview:就是本身 * newprogress:即将要显示的进度 */ @override public void onprogresschanged(webview view, int newprogress) { if (progressdialog != null && progressdialog.isshowing()) progressdialog.setmessage("软软正在拼命加载……" + newprogress + "%"); }
三、android本地通过java调用html页面中的javascript方法
想要调用js方法那么就必须让webview支持js的代码。
//首先设置webview支持js代码 webview.getsettings().setjavascriptenabled(true);
若调用的js方法没有返回值,则可以直接调用mwebview.loadurl(“javascript:do());其中do是js中的方法;
若有返回值时我们可以调用mwebview,evalutejavascript方法;
@targetapi(build.version_codes.kitkat) public void onsum(view view){ webview.evaluatejavascript("sum(1,2)", new valuecallback() { @override public void onreceivevalue(string value) { toast.maketext(getapplicationcontext(), "相加结果:"+value, toast.length_short).show(); } }); } public void ondoing(view view){ string msg = "测试"; webview.loadurl("javascript:showinfofromjava('"+msg+"')"); }
对应的js方法
function sum(a,b){ return a+b; } function showinfofromjava(){ document.getelementbyid("p").innerhtml="java成功调的js方法"; }
四、js调用android本地java方法
在android 4.2以上可以直接使用@javascriptinterface注解来声明,下面是在一个本地java方法
public void addjavascriptinterface(object object, string name);
1 object参数:在object对象里面添加我们想要在js里面调用的android方法,下面的代码中我们调用了showtoast方法
2 name参数:这里的name就是我们可以在js里面调用的对象名称,对应下面代码中的jstext
对应的js代码
function jsjava(){ //调用java的方法,*对象,java方法 //可以直接访问jstest,这是因为jstest挂载到js的window对象下了 jstest.showtoast("我是被js执行的android代码"); }
对应的java代码:
//java与js回调,自定义方法 //1.java调用js //2.js调用java //首先java暴露接口,供js调用 /** * obj:暴露的要调用的对象 * interfacename:对象的映射名称 ,object的对象名,在js中可以直接调用 * 在html的js中:jstest.showtoast(msg) * 可以直接访问jstest,这是因为jstest挂载到js的window对象下了 */ webview.addjavascriptinterface(new object() { //定义要调用的方法 //msg由js调用的时候传递 @javascriptinterface public void showtoast(string msg) { toast.maketext(getapplicationcontext(), msg, toast.length_short).show(); } }, "jstest");
五、重绘alert 、confirm和prompt的弹出效果,并把用户具体操作结果回调给js
alert弹窗:
重绘confirm弹窗:
重绘prompt弹窗:
具体代码:
/** * webview加载html中有alert()执行的时候,会回调这个方法 * url:当前webview显示的url * message:alert的参数值 * jsresult:java将结果回传到js中 */ @targetapi(build.version_codes.jelly_bean_mr1) @override public boolean onjsalert(webview view, string url, string message, final jsresult result) { alertdialog.builder builder = new alertdialog.builder(jsactivity.this); builder.settitle("提示:看到这个,说明java成功重写了js的alert方法"); builder.setmessage(message);//这个message就是alert传递过来的值 builder.setpositivebutton("确定", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理确定按钮,且通过jsresult传递,告诉js点击的是确定按钮 result.confirm(); } }); builder.show(); //自己处理 result.cancel(); return true; } /** * webview加载html中有confirm执行的时候,会回调这个方法 * url:当前webview显示的url * message:alert的参数值 * jsresult:java将结果回传到js中 */ @override public boolean onjsconfirm(webview view, string url, string message, final jsresult result) { alertdialog.builder builder = new alertdialog.builder(jsactivity.this); builder.settitle("提示:看到这个,说明java成功重写了js的confirm方法"); builder.setmessage(message);//这个message就是alert传递过来的值 builder.setpositivebutton("确定", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理确定按钮,且通过jsresult传递,告诉js点击的是确定按钮 result.confirm(); } }); builder.setnegativebutton("取消", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理取消按钮,且通过jsresult传递,告诉js点击的是取消按钮 result.cancel(); } }); builder.show(); //自己处理 result.cancel(); return true; } /** * webview加载html中有prompt()执行的时候,会回调这个方法 * url:当前webview显示的url * message:alert的参数值 *defaultvalue就是prompt的第二个参数值,输入框的默认值 * jspromptresult:java将结果重新回传到js中 */ @override public boolean onjsprompt(webview view, string url, string message, string defaultvalue, final jspromptresult result) { alertdialog.builder builder = new alertdialog.builder(jsactivity.this); builder.settitle("提示:看到这个,说明java成功重写了js的prompt方法"); builder.setmessage(message);//这个message就是alert传递过来的值 //添加一个edittext final edittext edittext = new edittext(jsactivity.this); edittext.settext(defaultvalue);//这个就是prompt 输入框的默认值 //添加到对话框 builder.setview(edittext); builder.setpositivebutton("确定", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //获取edittext的新输入的值 string newvalue = edittext.gettext().tostring().trim(); //处理确定按钮了,且过jsresult传递,告诉js点击的是确定按钮(参数就是输入框新输入的值,我们需要回传到js中) result.confirm(newvalue); } }); builder.setnegativebutton("取消", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理取消按钮,且过jsresult传递,告诉js点击的是取消按钮 result.cancel(); } }); builder.show(); //自己处理 result.cancel(); return true; } });
效果图:
界面上方是两个按钮,下方是一个webview控件,开启页面自动加载url,这里为了方便学习,
我已经写了一个html文件放置在了asset文件中,通过 file:///android_asset/test.html 来进行加载。
webview成功加载页面后,会出现四个新的按钮,点击不同的按钮,会产生不同的效果
asset文件夹下面的test.html文件
<meta charset="utf-8" /> <title></title> <script> function jsalert(){ var r = alert("我是alert的提示框"); document.getelementbyid("p").innerhtml="java成功调的js的alert方法"; } function jsconfirm(){ var r = confirm("我是confirm的弹出框"); if (r == true) { document.getelementbyid("p").innerhtml="用户点击了确认按钮"; }else{ document.getelementbyid("p").innerhtml="用户点击了取消按钮"; } } function jsprompt(){ //第一个参数是提示 //第二个参数是默认值 var r = prompt("请输入姓名:","小明同学"); if (r != null) { document.getelementbyid("p").innerhtml="用户输入的姓名为:"+r; }else{ document.getelementbyid("p").innerhtml="用户点击了取消按钮"; } } function jsjava(){ //调用java的方法,*对象,java方法 //可以直接访问jstest,这是因为jstest挂载到js的window对象下了 jstest.showtoast("我是被js执行的android代码"); } function sum(a,b){ return a+b; } function showinfofromjava(){ document.getelementbyid("p").innerhtml="js方法成功被java调用"; } </script>
界面成功初始化
调用jsalert方法
调用jsconfirm方法
调用jsprompt方法
调用jsjava方法
具体的java代码
public class jsactivity extends appcompatactivity { //assets下的文件的test.html所在的绝对路径 private static final string default_url = "file:///android_asset/test.html"; private webview webview; private progressdialog progressdialog;//加载界面的菊花 @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_js); initview(); initwebview(); } /** * 初始化控件 */ private void initview() { webview = (webview) findviewbyid(r.id.webview); webview.loadurl(default_url); } /** * 初始化webview */ private void initwebview() { //首先设置webview支持js代码 webview.getsettings().setjavascriptenabled(true); //webview自己处理超链接(webview的监听器非常多,封装一个特殊的监听类来处理) webview.setwebviewclient(new webviewclient() { /** * 当打开超链接的时候,回调的方法 * webview:自己本身webview * url:即将打开的url */ @override public boolean shouldoverrideurlloading(webview view, string url) { //自己处理新的url webview.loadurl(url); //true就是自己处理 return true; } //重写页面打开和结束的监听。打开时弹出菊花 /** * 界面打开的回调 */ @override public void onpagestarted(webview view, string url, bitmap favicon) { if (progressdialog != null && progressdialog.isshowing()) { progressdialog.dismiss(); } //弹出菊花 progressdialog = new progressdialog(jsactivity.this); progressdialog.settitle("提示"); progressdialog.setmessage("软软正在拼命加载……"); progressdialog.show(); } /** * 重写页面打开和结束的监听。打开时弹出菊花,关闭时隐藏菊花 * 界面打开完毕的回调 */ @override public void onpagefinished(webview view, string url) { //隐藏菊花:不为空,正在显示。才隐藏 if (progressdialog != null && progressdialog.isshowing()) { progressdialog.dismiss(); } } }); //设置进度条 //webchromeclient与webviewclient的区别 //webviewclient处理偏界面的操作:打开新界面,界面打开,界面打开结束 //webchromeclient处理偏js的操作 webview.setwebchromeclient(new webchromeclient() { /** * 进度改变的回调 * webview:就是本身 * newprogress:即将要显示的进度 */ @override public void onprogresschanged(webview view, int newprogress) { if (progressdialog != null && progressdialog.isshowing()) progressdialog.setmessage("软软正在拼命加载……" + newprogress + "%"); } /** * 重写alert、confirm和prompt的弹出效果,并把用户操作的结果回调给js */ /** * webview加载html中有alert()执行的时候,会回调这个方法 * url:当前webview显示的url * message:alert的参数值 * jsresult:java将结果回传到js中 */ @override public boolean onjsalert(webview view, string url, string message, final jsresult result) { alertdialog.builder builder = new alertdialog.builder(jsactivity.this); builder.settitle("提示:看到这个,说明java成功重写了js的alert方法"); builder.setmessage(message);//这个message就是alert传递过来的值 builder.setpositivebutton("确定", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理确定按钮了,且通过jsresult传递,告诉js点击的是确定按钮 result.confirm(); } }); builder.setoncancellistener(new dialoginterface.oncancellistener() { @override public void oncancel(dialoginterface dialog) { //防止用户点击对话框外围,再次点击按钮页面无响应 result.cancel(); } }); builder.show(); //自己处理 return true; } /** * webview加载html中有confirm执行的时候,会回调这个方法 * url:当前webview显示的url * message:alert的参数值 * jsresult:java将结果回传到js中 */ @override public boolean onjsconfirm(webview view, string url, string message, final jsresult result) { alertdialog.builder builder = new alertdialog.builder(jsactivity.this); builder.settitle("提示:" + "看到这个,说明java成功重写了js的confirm方法"); builder.setmessage(message);//这个message就是alert传递过来的值 builder.setpositivebutton("确定", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理确定按钮了,且通过jsresult传递,告诉js点击的是确定按钮 result.confirm(); } }); builder.setnegativebutton("取消", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理取消按钮,且通过jsresult传递,告诉js点击的是取消按钮 result.cancel(); } }); builder.setoncancellistener(new dialoginterface.oncancellistener() { @override public void oncancel(dialoginterface dialog) { //防止用户点击对话框外围,再次点击按钮页面无反应 result.cancel(); } }); builder.show(); //自己处理 return true; } /** * webview加载html中有prompt()执行的时候,会回调这个方法 * url:当前webview显示的url * message:alert的参数值 *defaultvalue就是prompt的第二个参数值,输入框的默认值 * jspromptresult:java将结果重新回传到js中 */ @override public boolean onjsprompt(webview view, string url, string message, string defaultvalue, final jspromptresult result) { alertdialog.builder builder = new alertdialog.builder(jsactivity.this); builder.settitle("提示:看到这个,说明java成功重写了js的prompt方法"); builder.setmessage(message);//这个message就是alert传递过来的值 //添加一个edittext final edittext edittext = new edittext(jsactivity.this); edittext.settext(defaultvalue);//这个就是prompt 输入框的默认值 //添加到对话框 builder.setview(edittext); builder.setpositivebutton("确定", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //获取edittext的新输入的值 string newvalue = edittext.gettext().tostring().trim(); //处理确定按钮了,且过jsresult传递,告诉js点击的是确定按钮(参数就是输入框新输入的值,我们需要回传到js中) result.confirm(newvalue); } }); builder.setnegativebutton("取消", new onclicklistener() { @override public void onclick(dialoginterface dialog, int which) { //处理取消按钮,且过jsresult传递,告诉js点击的是取消按钮 result.cancel(); } }); builder.setoncancellistener(new dialoginterface.oncancellistener() { @override public void oncancel(dialoginterface dialog) { //防止用户点击对话框外围,再次点击按钮页面无反应 result.cancel(); } }); builder.show(); //自己处理 return true; } }); //java与js回调,自定义方法 //1.java调用js //2.js调用java //首先java暴露接口,供js调用 /** * obj:暴露的要调用的对象 * interfacename:对象的映射名称 ,object的对象名,在js中可以直接调用 * 在html的js中:jstest.showtoast(msg) * 可以直接访问jstest,这是因为jstest挂载到js的window对象下了 */ webview.addjavascriptinterface(new object() { //定义要调用的方法 //msg由js调用的时候传递 @javascriptinterface public void showtoast(string msg) { toast.maketext(getapplicationcontext(), msg, toast.length_short).show(); } }, "jstest"); } @targetapi(build.version_codes.kitkat) public void onsum(view view){ webview.evaluatejavascript("sum(1,2)", new valuecallback() { @override public void onreceivevalue(string value) { toast.maketext(getapplicationcontext(), "相加结果:"+value, toast.length_short).show(); } }); } public void ondoing(view view){ string msg = "测试"; webview.loadurl("javascript:showinfofromjava('"+msg+"')"); } @override public void onbackpressed() { if (webview.cangoback()) { //返回上一个页 webview.goback(); return ; } super.onbackpressed(); } }
布局文件activity_js.xml
代码过程描述的废话我就不多说了,注释写的算是比较仔细了,另外再强调两点需要注意的地方:
1、不要忘记通过setjavascriptenabled(true)设置webview支持js代码
2、在使用addjavascriptinterface方法添加挂载对象时,要注意在android4.2之后需要给对象方法加上@javascriptinterface注解。
3、重绘alert、confirm和prompt的弹出效果之后,在对话框结束之后一定要调用result.confirm()或者result.cancel()两个方法中的一个
,否则会出现后续再次点击html页面按钮,页面无响应的情况
项目地址:https://github.com/afinalstone/webviewandjs-master