Android 中 Webview 与 JS 交互 – 热爱改变生活
我的GitHub GitHub |     登录
  • If you can't fly, then run; if you can't run, then walk; if you can't walk, then crawl
  • but whatever you do, you have to keep moving forward。
  • “你骗得了我有什么用,这是你自己的人生”
  • 曾有伤心之地,入梦如听 此歌

Android 中 Webview 与 JS 交互

Android控件 sinvader 7900℃ 0评论

使用场景

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 中的方法,按钮的代码如下

<!DOCTYPE html>
 <html>
 <head> 
 <script type="text/javascript" src="file:///android_asset/out2.js"></script>
 </head>
 <body>
 1: <input type="text" id="field1" value="Hello World!"><br> 
 2: <input type="text" id="field2"><br><br> <button onclick="copyText()"> 复制文本</button> 
 <p> 当按钮被单击时触发函数。此函数把文本从 Field1 复制到 Field2 中。</p> 
 </body>
 </html>

当按钮被单击时触发函数。此函数把文本从 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”【添加原因详解】)

测试代码下载

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

测试代码下载

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 去频繁的请求当前的这个方法,知道获得返回值。

下面是我的例子:

/////////////////////////////////////////////////////////
JS 代码 
/////////////////////////////////////////////////////////
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();
}
/////////////////////////////////////////////////////////
Android 代码 
/////////////////////////////////////////////////////////
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;
}
     上面的代码是 js 中点击按钮后的代码以及 Android 中 onJsPrompt 中的代码,首先会先请求一次 prompt 方法,可以看到它的第二个参数是”start”,这时候 Android 开始执行,但是因为是异步执行,并没有返回值,所以我主动给他返回了 false 字符串。然后开始循环调用 prompt 方法,这时候的第二个参数现在是 get。通过 start,get 来在 Android 中区分是应该开启异步任务还是检测异步任务是否已有结果。最后,终于等到你(返回值),还好我不放弃(while)。

下面的代码请在测试之前首先在 AndroidManifest 和 assets 下面的 baidujs.html 中将 key 替换掉。否则不能调用百度地图,但可能不影响测试。

测试代码下载

Mission completed

¥ 有帮助么?打赏一下~

转载请注明:热爱改变生活.cn » Android 中 Webview 与 JS 交互


本博客只要没有注明“转”,那么均为原创。 转载请注明链接:sumile.cn » Android 中 Webview 与 JS 交互

喜欢 (1)
发表我的评论
取消评论
表情

如需邮件形式接收回复,请注册登录

Hi,你需要填写昵称和邮箱~

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
(2)个小伙伴在吐槽
  1. 调游览器那个不对把。。我记得shouldOverrideUrlLoading是拦截URL是否加载的
    Astzessss2015-11-25 17:45 回复
    • 这个里面的参数url确实是点击时候的地址,但是如果不对这个方法里面的返回值进行修改的话,会打开系统默认浏览器进行加载的,所以如果要保证所有的网页都是通过自己的浏览器进行打开的话,那么返回值必须由自己写,且必须主动调用webview的loadUrl方法加载网页。至于拦截url,在loadUrl方法之前做就行,只是shouldOverrideUrlLoading能做的事情的一小部分。比如,如果一个地址是以tel://开头的,那么这个地址当然要被拦截,然后到打电话的界面了,这里就要返回false,浏览器对这个url进行处理。
      wudkj2015-11-26 10:47 回复