WebView 常识
使用 Alert 提供消息
我在页面经常用 Alert 提供消息, 但 Android 需要你编写 MyWebChromeClient
mWebView.setWebChromeClient(new MyWebChromeClient());
final class MyWebChromeClient extends WebChromeClient {
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(LOG_TAG, message);
result.confirm();
return true;
}
}
当然类似还有 onJsConfirm , onJsPrompt 等, ref: http://developer.android.com/reference/android/webkit/WebChromeClient.html
Android 与 JavaScript 的交互
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");
final class DemoJavaScriptInterface {
public void clickOnAndroid() {
mHandler.post(new Runnable() {
public void run() {
mWebView.loadUrl("javascript:wave()");
}
});
}
}
<html>
<script language="javascript">
/* This function is invoked by the activity */
function wave() {
alert("1");
document.getElementById("droid").src="android_waving.png";
alert("2");
}
</script>
<body>
<!-- Calls into the javascript interface for the activity -->
<a onClick="window.demo.clickOnAndroid()"><div style="width:80px;
margin:0px auto;
padding:10px;
text-align:center;
border:2px solid #202020;" >
<img id="droid" src="android_normal.png"/><br>
Click me!
</div></a>
</body>
</html>
程序详细内容 ref: http://bolutes.iteye.com/blog/1320344
实战
最近跑 0xbench 中 Android WebView 测试 sunspider 测试, 发现 sunspider 跑一阵就停留在白色屏幕,不知为何?
打开 0xbench/src/org/zeroxlab/benchmark/TesterJavascript.java 看了看, 这个testcase比较简单
public class TesterJavascript extends Tester {
protected WebView mWebView;
protected WebSettings mSettings;
private double mTotalTime = 0.0;
private String mResult = "";
private String mFormattedResult = "";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.javascript);
mWebView = (WebView) findViewById(R.id.web);
mSettings = mWebView.getSettings();
mSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new MsgCallback(), "ANDROID_OBJ");
startTester();
}
@Override
public void onResume() {
super.onResume();
}
@Override
protected String getTag() {
return "JavaScript";
}
@Override
protected int sleepBeforeStart() {
return 1000;
}
@Override
protected int sleepBetweenRound() {
return 1000;
}
@Override
protected void oneRound() {
mWebView.loadUrl("file:///android_asset/driver.html");
}
@Override
protected boolean saveResult(Intent intent) {
intent.putExtra(CaseJavascript.SUNSPIDER_RESULT, mResult);
intent.putExtra(CaseJavascript.SUNSPIDER_FORMATTED_RESULT, mFormattedResult);
intent.putExtra(CaseJavascript.SUNSPIDER_TOTAL, mTotalTime);
return true;
}
class MsgCallback {
public void finish(String result, String formatted_result) {
mResult = result;
mFormattedResult = formatted_result;
decreaseCounter();
}
}
}
就是打开 driver.html, 让其自动运行其中的 JS, 而后通过 JS callback MsgCallback.finish 把测试结果数据返回
在看看 0xbench/assets/driver.html 的代码段, 其中
function finish()
{
initialize();
computeItemTotals();
computeTotals();
computeMeans();
computeStdDevs();
computeStdErrors();
var formattedOutput = getOutputForUpload();
var finalOutput = getOutput();
window.ANDROID_OBJ.finish(finalOutput, formattedOutput);
}
其中: window.ANDROID_OBJ.finish(finalOutput, formattedOutput); 正是回调, ANDROID_OBJ 是 inject 的 obj 就是 MsgCallback 的实例
让 html 调试方便
每次都改 assets中的 html, 再安装调试是痛苦的
在 TesterJavascript.java 把 mWebView.loadUrl("file:///android_asset/driver.html"); 改为
mWebView.loadUrl("file:///sdcard/sunspider/driver.html");
再把 asset 中的中文件 copy 到 sdcard/sunspider/ 下面
以后每次调试只需要改写下 html ,而后可直接重新运行
定位问题:
window.alert("before calling finish")
window.ANDROID_OBJ.finish(finalOutput, formattedOutput)
window.alert("after calling finish")
发现只弹出第一个对话框, 看来回调不成功, 后来又看了 log, 发现与设想一致
E/Web Console( 343): Uncaught TypeError: Object org.zeroxlab.zeroxbenchmark.TesterJavascript$MsgCallback@a5972b68 has no method 'finish' at file ...
http://stackoverflow.com/questions/7424510/uncaught-typeerror-when-using-a-javascriptinterface
于是又回到那个简单的webviewdemo 程序, 检查build 系统是否出问题
运行以前那个小程序, 发现同样问题, 再用 jd-gui 查看build系统 progruad刚处理过 proguard.classes.jar 果然方法被过滤掉了, 而proguard 处理之前的 jar 是有的
在 Android.mk 中加入
LOCAL_PROGUARD_FLAG_FILES := proguard.cfg
在 Android.mk 同级目录中建立 proguard.cfg 内容是:
-keepclassmembers class com.pnp.webview.WebViewDemo.DemoJavaScriptInterface {
<methods>;
}
但编译过程中, proguard 不认
Proguard 编译命令是
external/proguard/bin/proguard.sh -injars out/target/common/obj/APPS/WebView_intermediates/classes.jar -outjars out/target/common/obj/APPS/WebView_intermediates/proguard.classes.jar -libraryjars out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes.jar -include build/core/proguard.flags -forceprocessing -printmapping out/target/common/obj/APPS/WebView_intermediates/proguard_dictionary -include out/target/common/obj/APPS/WebView_intermediates/proguard_options -include webview/proguard.cfg
报错是
Reading program jar [/home/payne/2jb/out/target/common/obj/APPS/WebView_intermediates/classes.jar]
Reading library jar [/home/payne/2jb/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes.jar]
Note: the configuration refers to the unknown class 'com.pnp.webview.WebViewDemo.DemoJavaScriptInterface'
Note: there were 1 references to unknown classes.
You should check your configuration for typos.
( 后来查明查明应该用)
-keepclassmembers class com.pnp.webview.WebViewDemo$DemoJavaScriptInterface {
<methods>;
}
另外还可以用
-keep class com.pnp.webview.JavascriptCallback
-keep class * implements com.pnp.webview.JavascriptCallback
-keepclassmembers class * implements com.pnp.webview.JavascriptCallback {
<methods>;
}
用jd-gui 打开, 却找不到相应的类
终于想到了, jd-gui 也有可能出问题. 所以不管 jd-gui 了, 直接安装程序运行, 得到了想要的结果.