android webview 使用以及一些異常處理及上傳文件問題

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" />

ok,測試了一下相關連接也能夠出現正常訪問,以上是以csdn網站爲例,若是將網站換成http://www.qq.com開始也沒有問題,點擊導航欄也能夠正常訪問,再點擊圖片鏈接就會出現 eventhub.removemessages(int what = 107) is not supported before the webviewcore is set up異常信息,有人說是沒有以http://開頭,這個也試了一下,沒有解決問題,期待有人能解決。回過頭來在首頁點擊相關新聞連接後會發現出現空白頁沒法正常訪問,後來研究發現這個和網站結構有關係,看來webview並不能徹底實現瀏覽器功能。

接下來就是簡單的異常處理了,主要就是重寫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上傳文件

相關文章
相關標籤/搜索