android中的提供webview控件,能夠方便開發人員是本身的應用嵌入網頁瀏覽功能,但實際開發中卻會遇到一些問題,這個稍後會介紹到,css
先來看個實例:java
public class MainActivity extends Activity { final String COMPANY_WEB="http://www.csdn.net"; private WebView mWebView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.webview); setWebView(); } private void setWebView(){ WebSettings webSettings = mWebView.getSettings(); webSettings.setJavaScriptEnabled(true); webSettings.setAllowFileAccess(true); webSettings.setBuiltInZoomControls(true); webSettings.setPluginsEnabled(true); mWebView.setWebViewClient(new MonitorWebClient()); mWebView.loadUrl(COMPANY_WEB); } private class MonitorWebClient extends WebViewClient{ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView view, final String url) { String website=Uri.parse(url).getHost(); if (COMPANY_WEB.equals(website)) { // This is my web site, so do not override; let my WebView load the page return false; }else{ view.loadUrl(url); return true; } // return super.shouldOverrideUrlLoading(view, url); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) { mWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); } }
相關權限:android
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
接下來就是簡單的異常處理了,主要就是重寫WebViewClient類中的onReceivedError()方法和onReceivedSslError()方法來進行處理了。web
說完異常簡單處理後再來講說提升網站的訪問速度,尤爲是帶有大量的flash,swf動畫和各類css樣式功能,這個時候咱們就應該使用緩存了,適當的設置緩存大小以及合適的模式來進行優化了,webview有兩種模式可設置以下:ajax
1,LOAD_DEFAULT,根據cache-control決定是否從網絡上取數據。
2,LOAD_CACHE_ELSE_NETWORK,只要本地有,不管是否過時,或者no-cache,都使用緩存中的數據。
如:m.taobao.com的cache-control爲no-cache,在模式LOAD_DEFAULT下,不管如何都會從網絡上取數據,若是沒有網絡,就會出現錯誤頁面;在LOAD_CACHE_ELSE_NETWORK模式下,不管是否有網絡,只要打開過一次,都使用緩存。
m.sina.com.cn的cache-control爲max-age=60,在兩種模式下都使用本地緩存數據。json
總結:根據以上兩種模式,建議緩存策略爲,判斷是否有網絡,有的話,使用LOAD_DEFAULT,無網絡時,使用LOAD_CACHE_ELSE_NETWORK。瀏覽器
好說的也差很少了,來看一下優化後的代碼:緩存
public class MainActivity extends Activity { final String COMPANY_WEB="http://www.deczh.com/"; private WebView mWebView; private Context activity; // private ProgressDialog progressDialog; //history web site // private Stack<String> webHistory=new Stack<String>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mWebView = (WebView) findViewById(R.id.webview); setWebView(); activity=this; mHandler.sendEmptyMessageDelayed(0, 1000); } private void setWebView(){ WebSettings webSettings = mWebView.getSettings(); //java script webSettings.setJavaScriptEnabled(true); webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // access Assets and resources webSettings.setAllowFileAccess(true); //zoom page webSettings.setBuiltInZoomControls(false); webSettings.setPluginsEnabled(true); webSettings.setPluginState(WebSettings.PluginState.ON); //提升渲染的優先級 webSettings.setRenderPriority(RenderPriority.HIGH); webSettings.setEnableSmoothTransition(true); //Cache開啓和設置 // 一個頁面的 圖片\js\css 載入過以後 //在服務器設置的文件有效期內,每次請求,會去服務器檢查文件最後修改時間,若是一致,不會從新下載,而是使用緩存 String appCachePath = mContext.getDir("netCache", Context.MODE_PRIVATE).getAbsolutePath(); webSettings.setAppCacheEnabled(true); webSettings.setAppCachePath(appCachePath); webSettings.setAppCacheMaxSize(1024*1024*5); //會影響時時刷新結果 // webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //LocalStorage相關設置 // 像首頁的DOM,第一次載入,須要從服務器ajax請求接口json配置數據,而後用js從模板中渲染拼接成DOM,顯示在頁面中 //因爲Android webview的JS處理很慢,這裏把第一次渲染後的DOM存入LocalStorage中,之後打開頁面不用請求API和JS渲染,優先加載頁面,和Cache配置,速度會快不少 //可是Android webview的LocalStorage有個問題,關閉APP或者重啓後,就清楚了,因此須要下面browser.getSettings().setDatabase相關的操做,把LocalStoarge存到DB中 webSettings.setDatabaseEnabled(true); webSettings.setDomStorageEnabled(true); String databasePath = mContext.getDir("databases", Context.MODE_PRIVATE).getPath(); webSettings.setDatabasePath(databasePath); mWebView.setWebViewClient(new MonitorWebClient()); mWebView.setWebChromeClient(new AppCacheWebChromeClient()); } private class MonitorWebClient extends WebViewClient{ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { //錯誤提示 Toast toast = Toast.makeText(getBaseContext(), "Oh no! " + description, Toast.LENGTH_LONG); toast.setGravity(Gravity.TOP | Gravity.CENTER, 0, 0); toast.show(); //錯誤處理 try { mWebView.stopLoading(); } catch (Exception e) { } try { mWebView.clearView(); } catch (Exception e) { } if (mWebView.canGoBack()) { mWebView.goBack(); } // super.onReceivedError(view, errorCode, description, failingUrl); } //當load有ssl層的https頁面時,若是這個網站的安全證書在Android沒法獲得認證,WebView就會變成一個空白頁,而並不會像PC瀏覽器中那樣跳出一個風險提示框 @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { //忽略證書的錯誤繼續Load頁面內容 handler.proceed(); //handler.cancel(); // Android默認的處理方式 //handleMessage(Message msg); // 進行其餘處理 // super.onReceivedSslError(view, handler, error); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { /*if (progressDialog == null) { // If no progress dialog, make one and set message progressDialog = new ProgressDialog(activity); progressDialog.setMessage("Loading please wait..."); progressDialog.show(); // Hide the webview while loading mWebView.setEnabled(false); }*/ // super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { /* if (progressDialog != null&&progressDialog.isShowing()) { progressDialog.dismiss(); progressDialog = null; mWebView.setEnabled(true); }*/ /*if(!webHistory.contains(url)) webHistory.push(url);*/ // super.onPageFinished(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView view, final String url) { Log.e(getClass().getSimpleName(), "website= "+url); // String website=Uri.parse(url).getHost(); String processUrl=url; if(!processUrl.startsWith("http://")) processUrl="http://"+processUrl; if (COMPANY_WEB.equals(url)) { // This is my web site, so do not override; let my WebView load the page return false; } else{ view.loadUrl(processUrl); return true; } // return super.shouldOverrideUrlLoading(view, url); } } private class AppCacheWebChromeClient extends WebChromeClient { @Override public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { // Log.e(APP_CACHE, "onReachedMaxAppCacheSize reached, increasing space: " + spaceNeeded); quotaUpdater.updateQuota(spaceNeeded * 2); } } private boolean pause=false; @Override public void onPause() { super.onPause(); if (mWebView != null) { mWebView.pauseTimers(); mWebView.onPause(); this.pause=true; } } @Override public void onResume() { super.onResume(); if (mWebView != null&&pause) { mWebView.resumeTimers(); mWebView.onResume(); this.pause=false; } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()){ mWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); } }
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
好了就先說到這裏吧!安全
webview內存泄漏:Activity destory處理方式:服務器
public void destroy() { if (mWebView != null) { // 若是先調用destroy()方法,則會命中if (isDestroyed()) return;這一行代碼,須要先onDetachedFromWindow(),再 // destory() ViewParent parent = mWebView.getParent(); if (parent != null) { ((ViewGroup) parent).removeView(mWebView); } mWebView.stopLoading(); // 退出時調用此方法,移除綁定的服務,不然某些特定系統會報錯 mWebView.getSettings().setJavaScriptEnabled(false); mWebView.clearHistory(); mWebView.clearView(); mWebView.removeAllViews(); try { mWebView.destroy(); } catch (Throwable ex) { } } }
2015-07-09更新:webview上傳文件問題
WebView默認是不支持文件上傳的,須要本身手動配置實現WebChromeClient,在內部使用intent action pick調用上傳文件功能
private WebView mWebView; private ValueCallback<Uri> mUploadMessage; private final static int FILECHOOSER_RESULTCODE = 1; public void onCreate(Bundle outState) { super.onCreate(outState); // setContentView(R.layout.activity_browser); // mWebView = (WebView) findViewById(R.id.webview); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.setWebChromeClient(new MyWebClient()); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { if (requestCode == FILECHOOSER_RESULTCODE) { if (null == mUploadMessage) return; Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); mUploadMessage.onReceiveValue(result); mUploadMessage = null; } } public class MyWebClient extends WebChromeClient { //根據不一樣版本設置不一樣category // For Android 3.0- public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("image/*"); startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE); } // For Android 3.0+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("*/*"); startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE); } // For Android 4.1 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { mUploadMessage = uploadMsg; Intent i = new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("image/*"); startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE); } }
測試時發如今4.4以上的Android版本依然不能夠, 搜了下openFileChooser方法在4.4之後不是public的,因此之後不建議這種直接在WebView上傳文件