android webview 使用以及一些异常处理及上传文件问题

android中的提供webview控件,可以方便开发人员是自己的应用嵌入网页浏览功能,但实际开发中却会遇到一些问题,这个稍后会介绍到,


先来看个实例:

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);
	}
	
}


相关权限:

<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()方法来进行处理了。

说完异常简单处理后再来说说提高网站的访问速度,尤其是带有大量的flash,swf动画和各种css样式功能,这个时候我们就应该使用缓存了,适当的设置缓存大小以及合适的模式来进行优化了,webview有两种模式可设置如下:

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,在两种模式下都使用本地缓存数据。

总结:根据以上两种模式,建议缓存策略为,判断是否有网络,有的话,使用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上传文件





posted @ 2013-07-25 15:25  HappyCode002  阅读(667)  评论(0编辑  收藏  举报