Android中WebView - litonghui/TechBlog GitHub Wiki
WebView 作为H5加载的组件对每一位移动开发者都不是很默认,项目中或多或少的也用到了,但是真实了解有多少哪?下面从WebView 优点、H5 加载过程、Js 与 Java 相互通信、WebViewClient 和 WebChromeClient 以及常用的重要方法、开发中需要注意的问题这几个方面聊聊WebView 。
移动端开发软件发布方式是以打包为主,包括Android 的APK、iOS 的IPA、WindowsPhone 的XAP等,这要发布形式相比于网页开发,新功能更新和软件修复及时性较差,虽然针对移动端软件修复不同系统都有各自的修复,比如基于Android 的Dexposed、ClassLoader,插件化都不是很成熟和方便,WebView 相比较可以更好的做到新功能发布和软件修复,当然更加推荐使用Hybrid 和 React Native 。
-------AndroidManifest-------
<uses-permission android:name="android.permission.INTERNET" />
-------Layout.xml-----------
<WebView
android:id="@+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/back_button" >
</WebView>
-------XXXXActivity.java-----------
mWebView = (WebView) findViewById(R.id.webview);
if (!TextUtils.isEmpty(url)) {
mWebView.loadUrl(url);
}
-------JavaScript.js-----------
<script type="text/javascript">
function setPageTitle(title){
window.webpage.onSetTitle(title);
}
</script>
-------XXXXActivity.java-----------
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
JsInterface jsInterface = new JsInterface();
mWebView.addJavascriptInterface(jsInterface, "webpage");
public class JsInterface implements NonProguard{
@JavascriptInterface
public void onSetTitle(String title) {}
}
JsInterface 继承接口NonProguard ,在代码混淆时候,记得-keep class com.android.interfaces.NonProguard,否则JsInterface 类被混淆之后JavaScript 找不到该类,里面方法也就查找失败
Android 开发切忌UI 线程被阻塞,因此不能再UI线程操作js,推荐使用方法如下:
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript:wave()");
}
当然在Android 4.4 中提供了方法:evaluateJavascript
mWebView.evaluateJavascript(script, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//TODO
}
});
-
WebViewClient,从自带方法中,可以理解为在页面加载过程中请求时间,处理通知等.重定向、WebSite
public void onPageStarted(WebView view, String url, Bitmap favicon){ super.onPageStarted(view, url, favicon); } // 页面开始加载,可以设置加载动画 public boolean shouldOverrideUrlLoading(WebView view, String url) { if (Uri.parse(url).getHost().equals("www.example.com")) { // This is my web site, so do not override; let my WebView load the page return false; // 避免重定向问题,需要 return false } // Otherwise, the link is not for a page on my site, so launch another Activity that handles URLs Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); startActivity(intent); return true; } onPageFinished(WebView view, String url) ..... -
WebChromeClient ,在网页加载网页的渲染,图标展示,js 交互。
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {} // js 通过该方法确认弹窗 public void onProgressChanged(WebView view, int newProgress) {} //显示更新加载进度 -
DownloadListener检测来自H5的下载,通过监听下载可以启动浏览器下载或者拦截Url 本地下载:
mWebView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { if(!TextUtils.isEmpty(url)&& Utils.isApk(url)) { MyDownloadManager.getInstance().add(url); Toast.makeText(mContext, "正在下载...", Toast.LENGTH_SHORT).show(); } } }); 或者启动浏览器下载: mWebView.setDownloadListener(new DownloadListener() { @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } });
- 内存泄露,造成ANR
- Js 漏洞,信息泄露
webSettings.setDomStorageEnabled(true);
webSettings.setAppCacheMaxSize(1024*1024*8);
String appCachePath = getApplicationContext().getCacheDir().getAbsolutePath();
webSettings.setAppCachePath(appCachePath);
webSettings.setAllowFileAccess(true);
webSettings.setAppCacheEnabled(true);
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}
Html代码下载到WebView 后,webkit 开始解析网页各个节点,对于外部文件或者脚本文件会异步网络请求下载文件,解析image 节点,也势必会发起网络请求下载相应的图片,网络情况较差的情况下,过多的网络请求会造成带宽紧张,影响到css和js加载完成时间,造成loading过久。解决方法是高速webview先不要自动加载图片,等页面finish后再发起图片加载
// 加快HTML网页加载完成速度
if (Build.VERSION.SDK_INT >= 19) {
webSettings.setLoadsImagesAutomatically(true);
} else {
webSettings.setLoadsImagesAutomatically(false);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (!view.getSettings().getLoadsImagesAutomatically()) {
view.getSettings().setLoadsImagesAutomatically(true);
}
}