我在页面经常用 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; } }
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>
最近跑 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(); } } }
在看看 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 的实例
每次都改 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 编译命令是
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 了, 直接安装程序运行, 得到了想要的结果.