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" />
接下来就是简单的异常处理了,主要就是重写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" />
好了就先说到这里吧!
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上传文件