刚学Android用WebView来做应用
Android Studio下载: http://www.android-studio.org/
写此文时最新稳定版是2.3.3,预览版3.0.0,3.0.0支持使用Kotlin进行开发,有点坑,毕竟还不够成熟
安卓开发文档:https://developer.android.google.cn/develop/index.html
网上比较流行的js与java交互解决方案:https://github.com/lzyzsd/JsBridge
百度、Google过好多文章,都是对addJavascriptInterface
和evaluateJavascript
很粗暴的解释,很少具体实例代码,所以自己整理了一下,以下方案仅对android4.4以上可用
照着安卓官方开发文档新建一个安卓工程出来不难,就不详说了,直接上代码,看注释
MainActivity.java
package com.example.demo.mydemo;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.JavascriptInterface;
import android.webkit.ValueCallback;
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class MainActivity extends AppCompatActivity {
WebView view;
@Override
protected void onCreate(Bundle savedInstanceState) {
view = new WebView(this);
super.onCreate(savedInstanceState);
// 注释默认视图,稍候将我们创建的WebView渲染出来
// setContentView(R.layout.activity_main);
// 开启对javascript支持,WebView默认是不支持javascript的
view.getSettings().setJavaScriptEnabled(true);
// 允许chrome浏览器进行远程调试
view.setWebContentsDebuggingEnabled(true);
// 如果不重写shouldOverrideUrlLoading这个方法,默认使用外部浏览器打开网页
view.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if (Build.VERSION.SDK_INT >= 21) {
view.loadUrl(request.getUrl().toString());
} else {
view.loadUrl(request.toString());
}
return false;
}
});
// 注入可在webview用js调用java方法的对象和变量Android
view.addJavascriptInterface(new MyJs(), "Android");
// 加载首页
view.loadUrl("file:///android_asset/index.html");
// 使activity显示webview的内容
setContentView(view);
}
public class MyJs {
// 注册给js用的方法
@JavascriptInterface
public void showToast (final String id, final String context) {
// 在Android Monitor的logcat可以查看这句java的输出
Log.i("toast", context);
// 调用js的全局方法将数据返回给js(必须用UI线程,否则app会崩溃)
runOnUiThread(new Runnable() {
@Override
public void run() {
// 返回给js的回调数据,
// id 用于给js找到对应的局部回调函数,
// context 应该是java获取到的数据,这里为了方便,直接将js传过来的数据返回
String data = "{id:" + id + ", result: '" + context + "'}";
//执行js的方法(只能执行windows域下的全局方法)
view.evaluateJavascript("exec(" + data + ")", new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
// 当exec方法执行完成,会回调这个java方法
// s 是exec的return值(一般很少用到)
Log.i("callback", s);
}
});
}
});
}
}
// 处理返回键
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode==KeyEvent.KEYCODE_BACK)
{
if(view.canGoBack())
{
view.goBack();//返回上一页面
return true;
}
else
{
System.exit(0);//退出程序
}
}
return super.onKeyDown(keyCode, event);
}
}
在WebView里必须使用UI线程的官方原文:https://developer.android.google.cn/guide/webapps/migrating.html#Threads
If you call methods on WebView from any thread other than your app’s UI thread, it can cause unexpected results.
我自己翻译:如果你使用了非UI线程的其它线程去调用WebView里的方法,将会产生不被期待的结果。
上文我说APP会崩溃是实践所得。
创建index.html
和index.js
文件,放到assets文件夹里,如果项目里没assets文件夹,可以这样创建:File > New > Folder > Assets Folder
index.html
<html>
<head>
<meta charset="utf-8" />
<title>Hello Demo</title>
</head>
<body>
<button id="a">A</button>
<script src="index.js"></script>
</body>
</html>
index.js
// 用于区别回调
var id = 0
// 回调池,存放局部回调
window.pool = []
// 给java调用的全局方法
window.exec = function (data) {
for (var i = 0; i < pool.length; i++) {
if (pool[i][0] == data.id) {
pool[i][1](data.result)
pool.splice(i, 1)
return true
}
}
}
// 封闭java方法的全局空间,方便给js调用
window.orz = {
showToast: function (msg, callback) {
Android.showToast(++id, msg)
pool.push([id, callback])
}
}
document.getElementById('a').onclick = function () {
// 经过上面的一层封装,调用原生并执行回调就很方便了
orz.showToast('Call', function (txt) {
// 这里用了console输出,需要在谷歌浏览器Remote devices对应的Inspect的Console查看,不能用alert,alert方法还没实现
console.log(txt)
})
orz.showToast('Me', function (txt) {
console.log(txt)
})
orz.showToast('Superman', function (txt) {
console.log(txt)
})
}
总结:js和java交互的原理主要是用addJavascriptInterface
给WebView注入java方法,让js可以调用,然后用evaluateJavascript
执行js方法将数据传回js,也可以用view.loadUrl("javascript:...")
来代替evaluateJavascript
。