webview 缓存机制详解
来源:互联网 发布:arp查看mac 编辑:程序博客网 时间:2024/05/17 07:05
WebView 的缓存场景与物理路径
Android APP加载Html页面时,在以下路径会产生缓存文档
旧版本Android(图一):
/data/data/package_name/cache/xxxwebviewCachexxx (xxx在2.x和4.x有所不同,4.0是webviewCache,文件夹存储的是css、js、image等)
/data/data/package_name/database/webview.db
/data/data/package_name/database/webviewCache.db (存储url、filepath、mimetype、expires、httpstatus等,图四)
新版本Android(图二):
/data/data/package_name/app_webview/Cache (该文件夹内存储的是css、js、image等)
/data/data/package_name/app_webview/Cookies(CookieManager维护的cookie数据库,如图三)
/data/data/package_name/app_webview/Local Storage/http_m.kxh1688.com_0.localstorage (里面只有ItemTable(key,value))
/data/data/package_name/app_webview/Web Data (里面是若干个autofill前缀的自动填充表,主要存储个人、公司邮箱、手机、地址都联系信息)
/data/data/package_name/app_database/xxxx (其中database是由
getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();里的名称生成的,存储url、filepath、mimetype、expires、httpstatus等、名称未测试不知道)
图一
红米 Android 4.4 真机上看到的 图二
Cookies数据库中的cookies表 图三
图四(图四来自网络)
WebView 的缓存种类
WebView中存在着两种缓存:网页缓存(存储打开过的页面及资源)、数据缓存(AppCache和DOM Storage(Web Storage))。
一、页面数据缓存
1、缓存模式(5种)
LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式
LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
LOAD_CACHE_ELSE_NETWORK 解释:
Use cache if content is there, even if expired (eg, history nav) If it is not in the cache, load from network. Use with setCacheMode(int)
.
如果内容已经存在cache 则使用cache,即使是过去的历史记录。如果cache中不存在,从网络中获取!
例子:
www.taobao.com的cache-control为no-cache,在模式LOAD_DEFAULT下,无论如何都会从网络上取数据,如果没有网络,就会出现错误页面;在LOAD_CACHE_ELSE_NETWORK模式下,无论是否有网络,只要本地有缓存,都使用缓存。本地没有缓存时才从网络上获取。
www.360.com.cn的cache-control为max-age=60,在两种模式下都使用本地缓存数据。
关于cache-control详情,请参阅大神MSNSHOW的
Http头介绍:Expires,Cache-Control,Last-Modified,ETag
及星之羽憶的 HTTP头的Expires与Cache-control
3、清除缓存
clearCache(boolean)CacheManager.clear高版本中需要调用隐藏API。
4、控制大小
无系统API支持。可选方式:定时统计缓存大小、按时间顺序删除缓存。
从缓存里读取图片:
/** * 从缓存获取图片 * * @return */ private Bitmap getPictureFromCache(){ Bitmap bitmap=null; try { //这里写死,在实际开发项目中要灵活使用 File file=new File(getCacheDir()+"/webviewCache/10d8d5cd"); FileInputStream inStream=new FileInputStream(file); bitmap=BitmapFactory.decodeStream(inStream); } catch (Exception e) { e.printStackTrace(); } return bitmap; }
删除此时之前的缓存:
/** * clear the cache before time numDays,like this:clearCacheFolder(Activity.getCacheDir(),System.currentTimeMillis()) * @param dir * @param numDays * @return */ private int DeleteFolder(File dir,long numDays){ int deletedFiles = 0; if (dir!=null && dir.isDirectory()) { try { for (File child:dir.listFiles()) { if (child.isDirectory()) { deletedFiles += DeleteFolder(child, numDays); } if (child.lastModified() < numDays) { if (child.delete()) { deletedFiles++; } } } } catch(Exception e) { e.printStackTrace(); } } return deletedFiles; }
退出应用前删除缓存:
File file = CacheManager.getCacheFileBaseDir(); if (file != null && file.exists() && file.isDirectory()) { for (File item : file.listFiles()) { item.delete(); } file.delete(); } context.deleteDatabase("webview.db"); context.deleteDatabase("webviewCache.db");
h5离线缓存java端:
WebSettings webseting = m_webview.getSettings(); webseting.setDomStorageEnabled(true); webseting.setAppCacheMaxSize(1024*1024*8);//设置缓冲大小,我设的是8M 。该方法在高版本已废弃。String appCacheDir = this.getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath(); webseting.setAppCachePath(appCacheDir); webseting.setAllowFileAccess(true); //maybe not needwebseting.setAppCacheEnabled(true); webseting.setCacheMode(WebSettings.LOAD_DEFAULT); m_webview.setWebChromeClient(m_chromeClient); private WebChromeClient m_chromeClient = new WebChromeClient(){ //扩充缓存的容量。<span style="font-family: Arial, Helvetica, sans-serif;">该方法在高版本已废弃,扩充由系统自动维护。</span>@Override public void onReachedMaxAppCacheSize(long spaceNeeded, long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) { quotaUpdater.updateQuota(spaceNeeded * 2); } };
h5离线缓存html端:
在html标签中声明 <html manifest="clock.manifest">
更新缓存机制分为手动更新和自动更新2种,
自动更新:在cache manifest文件本身发生变化时更新缓存 资源文件发生变化不会触发更新
手动更新:使用window.applicationCache
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {window.applicationCache.update();}结合在线状态检测,HTML5 提供了两种检测是否在线的方式:navigator.online(true/false) 和 online/offline事件。
h5离线需要提供一个cache manifest文件,列出所有需要在离线状态下使用的资源,如下面的clock.manifest文件:
CACHE MANIFEST #这是注释images/sound-icon.pngimages/background.pngclock.html clock.css clock.js NETWORK: test.cgiCACHE: style/default.cssFALLBACK: /files/projects /projects
“text/cache-manifest mf manifest”,重启服务器即可。这一步很重要,我就是因为服务器端没有配置这个,所以失败了好多次,最后是在附录链接1的回复中找到的线索。
经过以上设置Webview就可以支持HTML5的离线应用了。
附录链接1中说缓冲目录应该是getApplicationContext().getCacheDir().getAbsolutePath();但我经过试验后发现设置那个目录不起作用,可能是Android版本不同吧,我的是Android4.0.3,而他的可能是以前的Android版本吧。
缓冲目录使用getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath()是从附录链接2中找到的线索。
完整代码:
package com.example.webviewtest; import java.io.File; import android.app.Activity; import android.graphics.Bitmap; import android.os.Bundle; import android.util.Log; import android.view.View; import android.webkit.JsPromptResult; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebSettings.RenderPriority; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleName(); private static final String APP_CACAHE_DIRNAME = "/webcache"; private TextView tv_topbar_title; private RelativeLayout rl_loading; private WebView mWebView; private String url; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //url:http://m.dianhua.cn/detail/31ccb426119d3c9eaa794df686c58636121d38bc?apikey=jFaWGVHdFVhekZYWTBWV1ZHSkZOVlJWY&app=com.yulore.yellowsdk_ios&uid=355136051337627 url = "http://m.dianhua.cn/detail/31ccb426119d3c9eaa794df686c58636121d38bc?apikey=jFaWGVHdFVhekZYWTBWV1ZHSkZOVlJWY&app=com.yulore.yellowsdk_ios&uid=355136051337627"; findView(); } private void findView() { tv_topbar_title = (TextView) findViewById(R.id.tv_topbar_title); rl_loading = (RelativeLayout) findViewById(R.id.rl_loading); mWebView = (WebView) findViewById(R.id.mWebView); initWebView(); mWebView.setWebViewClient(new WebViewClient() { @Override public void onLoadResource(WebView view, String url) { Log.i(TAG, "onLoadResource url="+url); super.onLoadResource(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView webview, String url) { Log.i(TAG, "intercept url="+url); webview.loadUrl(url); return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { Log.e(TAG, "onPageStarted"); rl_loading.setVisibility(View.VISIBLE); // 显示加载界面 } @Override public void onPageFinished(WebView view, String url) { String title = view.getTitle(); Log.e(TAG, "onPageFinished WebView title=" + title); tv_topbar_title.setText(title); tv_topbar_title.setVisibility(View.VISIBLE); rl_loading.setVisibility(View.GONE); // 隐藏加载界面 } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { rl_loading.setVisibility(View.GONE); // 隐藏加载界面 Toast.makeText(getApplicationContext(), "", Toast.LENGTH_LONG).show(); } }); mWebView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { Log.e(TAG, "onJsAlert " + message); Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); result.confirm(); return true; } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { Log.e(TAG, "onJsConfirm " + message); return super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { Log.e(TAG, "onJsPrompt " + url); return super.onJsPrompt(view, url, message, defaultValue, result); } }); mWebView.loadUrl(url); } private void initWebView() { mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setRenderPriority(RenderPriority.HIGH); mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); //设置 缓存模式 // 开启 DOM storage API 功能 mWebView.getSettings().setDomStorageEnabled(true); //开启 database storage API 功能 mWebView.getSettings().setDatabaseEnabled(true); String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME; // String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME; Log.i(TAG, "cacheDirPath="+cacheDirPath); //设置数据库缓存路径 mWebView.getSettings().setDatabasePath(cacheDirPath); //设置 Application Caches 缓存目录 mWebView.getSettings().setAppCachePath(cacheDirPath); //开启 Application Caches 功能 mWebView.getSettings().setAppCacheEnabled(true); } /** * 清除WebView缓存 */ public void clearWebViewCache(){ //清理Webview缓存数据库 try { deleteDatabase("webview.db"); deleteDatabase("webviewCache.db"); } catch (Exception e) { e.printStackTrace(); } //WebView 缓存文件 File appCacheDir = new File(getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME); Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath()); File webviewCacheDir = new File(getCacheDir().getAbsolutePath()+"/webviewCache"); Log.e(TAG, "webviewCacheDir path="+webviewCacheDir.getAbsolutePath()); //删除webview 缓存目录 if(webviewCacheDir.exists()){ deleteFile(webviewCacheDir); } //删除webview 缓存 缓存目录 if(appCacheDir.exists()){ deleteFile(appCacheDir); } } /** * 递归删除 文件/文件夹 * * @param file */ public void deleteFile(File file) { Log.i(TAG, "delete file path=" + file.getAbsolutePath()); if (file.exists()) { if (file.isFile()) { file.delete(); } else if (file.isDirectory()) { File files[] = file.listFiles(); for (int i = 0; i < files.length; i++) { deleteFile(files[i]); } } file.delete(); } else { Log.e(TAG, "delete file no exists " + file.getAbsolutePath()); } } }
二、数据缓存
数据缓存分为两种:AppCache和DOM Storage(Web Storage)。
他们是因为页面开发者的直接行为而产生。所有的缓存数据都由开发者直接完全地掌控。
1. AppCache 缓存
AppCache缓存需设置以下3步骤:
开启缓存:setAppCacheEnabled(true)
设置路径:setAppCachePath (应用程序整个运行周期只调用一次)
设置容量:setAppCacheMaxSize(long appCacheMaxSize)设置缓存最大容量,默认为Max Integer。但不知怎么的,有时设置后无效。
若想定义超出容量时采取的措施,可重载方法 WebChromeClient.onReachedMaxAppCacheSize(long requiredStorage, long quota,WebStorage.QuotaUpdater quotaUpdater),这两个方法在高版本中已废弃。
缓存路径:my_path/ApplicationCache.db
2. DOM Storage 缓存
主要是H5的Session Storage和Local Storage
Session Storage:在会话结束后缓存自动失效,不需要开发者维护。
Local Storage:数据不会过期,开发者不删除app的缓存数据,缓存永远存在。且删除时,除了删除数据,还要杀死当前程序运行的当前进程,然后重启才能干净。
DOM Storage 缓存需设置以下2步骤:
开启缓存:setDomStorageEnabled(true)
设置路径:setDatabasePath 如下说明,从android 4.4开始已废弃,由浏览器自动维护。
//设置数据库缓存路径 API level 19 , Android 4.4 KitKat, in which the browser engine is switched from Android webkit to chromium webkit缓存路径:
my_path/localstorage/http_h5.m.taobao.com_0.localstorage
my_path/localstorage/Databases.db
webview 设置缓存完整代码:
package utility;import android.annotation.SuppressLint;import android.content.Context;import android.graphics.Bitmap;import android.net.Uri;import android.net.http.SslError;import android.view.KeyEvent;import android.webkit.GeolocationPermissions;import android.webkit.JsPromptResult;import android.webkit.JsResult;import android.webkit.SslErrorHandler;import android.webkit.ValueCallback;import android.webkit.WebChromeClient;import android.webkit.WebSettings;import android.webkit.WebView;import android.webkit.WebViewClient;/** * Author: jamky * Date: 2015/6/4 * Description: 主要应用于 WebView 设置 */public class WebViewConfig { @SuppressLint("JavascriptInterface") public static void Config(Context context,WebView wv){ WebSettings ws = wv.getSettings(); //android 4.0-, API 14- 写法 /data/data/packagename/files/webcache //String cacheDirPath = context.getFilesDir().getAbsolutePath()+"/webcache"; //android 4.0+, API 14+ 写法 String cacheDirPath = context.getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath(); String geolocationDatabasePath = context.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); //ws.setDefaultTextEncodingName("GBK"); //设置字符编码 默认UTF-8 //support open local files, default value is true; ws.setAllowFileAccess(true); ws.setJavaScriptEnabled(true); //缓存模式 ws.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // 缓存模式遵从web页面header信息cache-control,如其值为max-age=60,即60秒过期 //s.setCacheMode(WebSettings.LOAD_DEFAULT); //support geolocation and set the path of geolocation ws.setGeolocationEnabled(true); ws.setGeolocationDatabasePath(geolocationDatabasePath); /* 以下为 页面缓存 设置 */ //设置 Application Caches 缓存目录 该设置比较特殊,网友说整个应用程序的整个生命同期只调用一次,未测试 ws.setAppCachePath(cacheDirPath); //开启 Application Caches 功能 ws.setAppCacheEnabled(true); /* 以下为 DOM Storage(Web Storage)数据缓存 设置 */ //开启 database storage API 功能 ws.setDatabaseEnabled(true); //设置数据库缓存路径 API level 19 , Android 4.4 KitKat, in which the browser engine is switched from Android webkit to chromium webkit ws.setDatabasePath(cacheDirPath); // 开启 DOM storage API 功能 ws.setDomStorageEnabled(true); /* set WebViewClient */ wv.setWebViewClient(new WebViewClient() { //if load many images, this method will also call many times @Override public void onLoadResource(WebView view, String url) { L.i("onLoadResource url=" + url); super.onLoadResource(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView webview, String url) { L.i("intercept url=" + url); webview.loadUrl(url); return true; } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { L.i("onPageStarted"); //rl_loading.setVisibility(View.VISIBLE); } @Override public void onPageFinished(WebView view, String url) { String title = view.getTitle(); L.i("onPageFinished WebView title=" + title); // rl_loading.setVisibility(View.GONE); // 隐藏加载界面 } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { // rl_loading.setVisibility(View.GONE); // 隐藏加载界面 } //support https @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { super.onReceivedSslError(view, handler, error); } //catch web element key event,if return true,webview will not handler the key event @Override public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) { return super.shouldOverrideKeyEvent(view, event); } }); /* set WebChromeClient */ wv.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { super.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { super.onReceivedTitle(view, title); } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { L.e("onJsAlert " + message); result.confirm(); return true; } @Override public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { L.e("onJsConfirm " + message); return super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { L.e("onJsPrompt " + url); return super.onJsPrompt(view, url, message, defaultValue, result); } @Override public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) { callback.invoke(origin, true, false); super.onGeolocationPermissionsShowPrompt(origin, callback); } @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { return super.onShowFileChooser(webView, filePathCallback, fileChooserParams); } }); //调用java方法 不安全/* wv.addJavascriptInterface(new Object() { @JavascriptInterface public void CallJavaMethod(String url, String title) { //javascript call java this method,it's not safe. like this: window.OpenObj.CallJavaMethod("param","param"); } }, "OpenObj");*/ //wv.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY); //滚动条放webview里面 不占空间 wv.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY); //滚动条放webview外面 wv.requestFocus(); //调用javascript方法 //wv.loadUrl("javascript:CallJavascriptMethod('param')"); //wv.loadDataWithBaseURL(null,"", "text/html", "utf-8", null); //不使用loadData,因为loadData方法的data参数不能包含特殊字符'#', '%', '\', '?' //wv.loadUrl("url"); // please call this method outside }}
参考:
Android老码农 的 Android WebView缓存分析
脚本之家 Android的webview支持HTML5的离线应用功能详细配置
- webview 缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- Android WebView缓存机制详解
- 【专题】Android WebView缓存机制详解
- Android WebView 缓存机制和模式详解
- Android webView缓存机制
- Android WebView缓存机制
- Android WebView缓存机制
- webview的缓存机制
- webView 离线缓存机制
- android webview的缓存机制
- 手指头离心脏还很遥远呢,人不至于死掉吧
- 高斯滤波
- C++对象模型
- String StringBuilder StringBuffer 对比 总结得非常好
- MongoDB介绍及安装
- webview 缓存机制详解
- 欢迎使用CSDN-markdown编辑器
- PHP5中的反射
- 正则表达式
- (总结)Nginx配置文件nginx.conf中文详解
- Hadoop别人的学习总结
- ImageView 使用 setAlpha() 设置控件透明
- SIFT算法的应用--目标识别之Bag-of-words模型(转)
- 10.3修改