Android经验: proguard 阻碍 webview 正常工作

WebView 常识

使用 Alert  提供消息

我在页面经常用 Alert 提供消息,  但 Android 需要你编写 MyWebChromeClient

mWebView.setWebChromeClient(new MyWebChromeClient());

 

 

[html] view plaincopy

  1. final class MyWebChromeClient extends WebChromeClient {
  2.         @Override
  3.         public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
  4.             Log.d(LOG_TAG, message);
  5.             result.confirm();
  6.             return true;
  7.         }
  8.     }

当然类似还有 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”);

 

[html] view plaincopy

  1. final class DemoJavaScriptInterface {
  2.         public void clickOnAndroid() {
  3.             mHandler.post(new Runnable() {
  4.                 public void run() {
  5.                     mWebView.loadUrl(“javascript:wave()”);
  6.                 }
  7.             });
  8.         }
  9.     }

 

 

[html] view plaincopy

  1. <html>
  2.     <script language=”javascript”>
  3.         /* This function is invoked by the activity */
  4.         function wave() {
  5.             alert(“1”);
  6.             document.getElementById(“droid”).src=”android_waving.png”;
  7.             alert(“2”);
  8.         }
  9.     </script>
  10.     <body>
  11.         <!– Calls into the javascript interface for the activity –>
  12.         <a onClick=”window.demo.clickOnAndroid()”><div style=”width:80px;
  13.             margin:0px auto;
  14.             padding:10px;
  15.             text-align:center;
  16.             border:2px solid #202020;” >
  17.                 <img id=”droid” src=”android_normal.png”/><br>
  18.                 Click me!
  19.         </div></a>
  20.     </body>
  21. </html>

程序详细内容 ref: http://bolutes.iteye.com/blog/1320344

 

实战

最近跑 0xbench 中 Android WebView 测试 sunspider 测试, 发现 sunspider 跑一阵就停留在白色屏幕,不知为何?

打开 0xbench/src/org/zeroxlab/benchmark/TesterJavascript.java 看了看, 这个testcase比较简单

 

[html] view plaincopy

  1. public class TesterJavascript extends Tester {
  2.     protected WebView mWebView;
  3.     protected WebSettings mSettings;
  4.     private double mTotalTime = 0.0;
  5.     private String mResult = “”;
  6.     private String mFormattedResult = “”;
  7.     @Override
  8.     public void onCreate(Bundle savedInstanceState) {
  9.         super.onCreate(savedInstanceState);
  10.         setContentView(R.layout.javascript);
  11.         mWebView = (WebView) findViewById(R.id.web);
  12.         mSettings = mWebView.getSettings();
  13.         mSettings.setJavaScriptEnabled(true);
  14.         mWebView.addJavascriptInterface(new MsgCallback(), “ANDROID_OBJ”);
  15.         startTester();
  16.     }
  17.     @Override
  18.     public void onResume() {
  19.         super.onResume();
  20.     }
  21.     @Override
  22.     protected String getTag() {
  23.         return “JavaScript”;
  24.     }
  25.     @Override
  26.     protected int sleepBeforeStart() {
  27.         return 1000;
  28.     }
  29.     @Override
  30.     protected int sleepBetweenRound() {
  31.         return 1000;
  32.     }
  33.     @Override
  34.     protected void oneRound() {
  35.         mWebView.loadUrl(“file:///android_asset/driver.html”);
  36.     }
  37.     @Override
  38.     protected boolean saveResult(Intent intent) {
  39.         intent.putExtra(CaseJavascript.SUNSPIDER_RESULT, mResult);
  40.         intent.putExtra(CaseJavascript.SUNSPIDER_FORMATTED_RESULT, mFormattedResult);
  41.         intent.putExtra(CaseJavascript.SUNSPIDER_TOTAL, mTotalTime);
  42.         return true;
  43.     }
  44.     class MsgCallback {
  45.         public void finish(String result, String formatted_result) {
  46.             mResult = result;
  47.             mFormattedResult = formatted_result;
  48.             decreaseCounter();
  49.         }
  50.     }
  51. }

就是打开 driver.html, 让其自动运行其中的 JS, 而后通过 JS callback MsgCallback.finish 把返回

 

在看看 0xbench/assets/driver.html 的代码段, 其中

 

[html] view plaincopy

  1. function finish()
  2. {
  3.     initialize();
  4.     computeItemTotals();
  5.     computeTotals();
  6.     computeMeans();
  7.     computeStdDevs();
  8.     computeStdErrors();
  9.     var formattedOutput = getOutputForUpload();
  10.     var finalOutput = getOutput();
  11.     window.ANDROID_OBJ.finish(finalOutput, formattedOutput);
  12. }

其中: 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 ,而后重新运行文件了

 

定位问题:

 

[html] view plaincopy

  1. window.alert(“before calling finish”)
  2. window.ANDROID_OBJ.finish(finalOutput, formattedOutput)
  3. 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 …

解决:
根据关键字google 了一些, 发现问题很可能是 proguard 把程序优化了(正如下面这篇文章提到的) , 因为 eclipse 编译的 0xbench 能在系统上跑, 而出问题的 0xbench 不是在 eclipse 中编译出来的

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 内容是:

 

[html] view plaincopy

  1. -keepclassmembers class com.pnp.webview.WebViewDemo.DemoJavaScriptInterface {
  2.     <methods>;
  3. }

但编译过程中, proguard 不认

 

Proguard 编译命令是

 

[html] view plaincopy

  1. 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

 

报错是

[html] view plaincopy

  1. Reading program jar [/home/payne/2jb/out/target/common/obj/APPS/WebView_intermediates/classes.jar]
  2. Reading library jar [/home/payne/2jb/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes.jar]
  3. Note: the configuration refers to the unknown class ‘com.pnp.webview.WebViewDemo.DemoJavaScriptInterface’
  4. Note: there were 1 references to unknown classes.
  5.       You should check your configuration for typos.

 

( 后来查明查明应该用)

 

[html] view plaincopy

  1. -keepclassmembers class com.pnp.webview.WebViewDemo$DemoJavaScriptInterface {
  2.     <methods>;
  3. }

 

另外还可以用

 

[html] view plaincopy

  1. -keep class com.pnp.webview.JavascriptCallback
  2. -keep class * implements com.pnp.webview.JavascriptCallback
  3. -keepclassmembers class * implements com.pnp.webview.JavascriptCallback {
  4.     <methods>;
  5. }

用jd-gui 打开, 却找不到相应的类

 

终于想到了, jd-gui 也有可能出问题. 所以不管 jd-gui 了, 直接安装程序运行, 得到了想要的结果.

标签