Android中Webview与JS交互

无简介

使用场景

Activity中或者Fragment中放置一个webview,然后使用这个webview来加载本地或者网络上的网页文件。如果网页文件比较复杂,那么不可避免的就要使用js,如果js在他之内使用还好,如果js要通过Android程序处理数据之后再获得Android的返回值,该怎么办?

需求说明

webview加载一个html,html里面是一个百度地图,页面上有一个按钮,点击之后通过js请求Android端进行定位,定位之后将经纬度返回到js中,js将调用地图api将经纬度显示到当前的百度地图上面。

如何对webview进行设置

webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// 当返回true时,你点任何链接都是失效的,需要你自己跳转。return false时webview会自己跳转。
view.loadUrl(url); //我们自己加载了地址(自己进行了跳转)
return true; //返回true
}
});

最重要的两行代码,第一行之后设置可以和js进行交互了。 第二行设置网页内的内容点击后在当前应用内打开,而不是使用第三方浏览器打开。

如何去和js进行交互

在了解了需求之后,首先要知道的就是如何和js进行交互。

js调用Android方法

一个按钮,点击后想要调用Android中的方法,按钮的代码如下

1:
2:

当按钮被单击时触发函数。此函数把文本从 Field1 复制到 Field2 中。

当按钮被单击时触发函数。此函数把文本从 Field1 复制到 Field2 中。 JS代码:

function copyText()
{
Android.getLocation(“88”);
}

发现了和其他js不同的地方了吧,在js中使用了这样的代码:

Android.getLocation(“88”);

注意开头的Android这个单词(这个单词是可以换的,而且,随便换) 当然现在还不能开始调用Android中的代码。 我们还需要对webview进行一下设置:

JavaScriptInterface jsif=new JavaScriptInterface();
webview.addJavascriptInterface(jsif, “Android”);//这句话里面的第二个参数那里写什么,在js里面就写什么,可以理解为这里给js传递了一个java对象

JavaScriptInterface的代码(这个类的名称也可以随便换)

public class JavaScriptInterface{
@JavascriptInterface
public void getLocation(String str){
System.out.println(str);
}
}

所以,可以看到,在js里面调用的

Android.getLocation(“88”);

其中,Android来自于

webview.addJavascriptInterface(jsif, “Android”);

的第二个参数,而它后面的方法名,来自于JavaScriptInterface类中的方法名,至于参数的传递,类似于java。 写完这些之后,就可以运行了。(如果不能运行,看看是不是忘了给JavaScriptInterface中的方法添加这个“@JavascriptInterface”【添加原因详解】) [dl href=‘http://pan.baidu.com/s/1i3D5neP’]测试代码下载[/dl]

JS调用Android的第二种方法

在JS中常用的方法有alert,Prompt等(可能是我别的不会,觉得这两个常用。。) 通过第一种方法当然可行。 下面来看看针对alert,prompt等的第二种方法。 首先来看js中的代码

function copyText()
{
document.getElementById(“field2”).value=prompt(document.getElementById(“field1”).value);
}

html中的代码没有变,再看看Android中添加的代码

webview.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// 本方法于2015年8月17日 下午9:21:51由sumile建立
System.out.println(message + “—>” + url);
result.confirm(message + “添加了东西”);
return true;
}
});

代码运行之后的结果:

08-21 09:33:53.160: I/System.out(31749): Hello World!—>file:/// 第二个输入框中的内容变成了"Hello World!添加了东西"

就是说,在webview中,某些特定名称的js方法可以在Android中被截获,如prompt,alert等。 prompt在js中的定义是这样的:

prompt() 方法用于显示可提示用户进行输入的对话框。

语法

prompt(text,defaultText)

参数

描述

text

可选。要在对话框中显示的纯文本(而不是 HTML 格式的文本)。

defaultText

可选。默认的输入文本。

说明

如果用户单击提示框的取消按钮,则返回 null。如果用户单击确认按钮,则返回输入字段当前显示的文本。 在用户点击确定按钮或取消按钮把对话框关闭之前,它将阻止用户对浏览器的所有输入。在调用 prompt() 时,将暂停对 JavaScript 代码的执行,在用户作出响应之前,不会执行下一条语句。

那么这样,我们在Android中通过onJsPrompt方法,实际上就实现了一个js与Android的同步交互。 在这句话中

document.getElementById(“field2”).value=prompt(document.getElementById(“field1”).value);

js执行了等号后面的话之后,会等待Android的返回结果,然后将这个返回结果赋值给等号前面。 其次,我们还看到Android的onJsPrompt方法有一个返回值,那么这个返回值的含义是什么?

prompt这个东东本来是会弹出一个对话框的,可以点击确定或者取消 返回true,不会继续弹出对话框 返回false,你猜。

这种方法中关于alert的请看测试代码,其他的,请自行google [dl href=‘http://pan.baidu.com/s/1sj7jp85’]测试代码下载[/dl]

Android调用js中的"方法"。

看代码,一看你就懂了,记得js里面的方法名字么?忘了就回去看看。

button.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
webview.loadUrl(“javascript:copyText()”);
}
});

JS调用Android方法,Android去执行异步操作,JS等待到返回值后才可以继续运行

这是一个新问题,其实也是我写这篇文章要解决的问题。 从上面的JS调用Android以及Android调用js来看,我们可以将这两种方法进行组合来完成这个功能:

首先通过js调用Android中的方法,Android去执行异步任务,异步任务执行完后主动调用js中的方法将值传递给js。

我不想用。。。 换! 然后我就想了,js调用Android方法可以得到返回值,可以通过prompt去做,但是在onJsPrompt中是同步去做的,就是不等我异步处理完结果,就得返回值了,那该怎么做。 让js去频繁的请求当前的这个方法,知道获得返回值。 下面是我的例子:

/////////////////////////////////////////////////////////
[v_qing]JS代码[/v_qing]
/////////////////////////////////////////////////////////
function getLocation()
{
var j=prompt(“x”,“start”);
var lan=“false”;
if(j==“false”){
do{
lan=prompt(“y”,“get”);
}while(lan==“false”);
}
var arr=lan.split(“,”);
document.getElementById(“latitude”).value=arr[0];
document.getElementById(“longitude”).value=arr[1];
theLocation();
}
/////////////////////////////////////////////////////////
[v_qing]Android代码[/v_qing]
/////////////////////////////////////////////////////////
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
System.out.println(message + “—>” + url + “—>” + defaultValue);
if (“start”.equals(defaultValue)) {
initLocation();
mLocationClient.start();// 定位SDK
// start之后会默认发起一次定位请求,开发者无须判断isstart并主动调用request
mLocationClient.requestLocation();
result.confirm(“false”);
}
if (“get”.equals(defaultValue)) {
((LocationApplication) getApplication()).setLocationInterface(new BDLocationInterface() {
@Override
public void getLocation(BDLocation location) {
newStr = String.valueOf(location.getLatitude()) + “,” + String.valueOf(location.getLongitude());
System.out.println(newStr);
}
});
if (!“”.equals(newStr)) {
result.confirm(newStr);
mLocationClient.stop();
System.out.println(“confirm”);
return true;
}
}
result.confirm(“false”);
return true;
}

[v_qing]     上面的代码是js中点击按钮后的代码以及Android中onJsPrompt中的代码,首先会先请求一次prompt方法,可以看到它的第二个参数是"start",这时候Android开始执行,但是因为是异步执行,并没有返回值,所以我主动给他返回了false字符串。然后开始循环调用prompt方法,这时候的第二个参数现在是get。通过start,get来在Android中区分是应该开启异步任务还是检测异步任务是否已有结果。最后,终于等到你(返回值),还好我不放弃(while)。[/v_qing] 下面的代码请在测试之前首先在AndroidManifest和assets下面的baidujs.html中将key替换掉。否则不能调用百度地图,但可能不影响测试。 [dl href=‘http://pan.baidu.com/s/1qWsYj2O’]测试代码下载[/dl]

Mission completed

-------------本文结束  感谢您的阅读-------------
下次一定