【Android Studio】深入探究webView的缓存机制
来源:互联网 发布:复制信息淘宝打不开 编辑:程序博客网 时间:2024/06/05 10:16
最近一直都在搞webview,搞过Android的人可能会知道,webView本身自带了缓存机制,company的需求是不用webView 的缓存机制,写自己的缓存机制,哇哈哈,有挑战性咯。写这篇博客主要是记录一下我的学习过程。写的不好,勿喷。
首先我们要搞明白webView的缓存机制是什么?
webView中有两种缓存:
一是网页数据缓存(即浏览网页中的资源),而是H5缓存(即appCache)。
webView的缓存目录:
/data/data/package_name/cache/
webView的缓存模式:
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,都使用缓存中的数据。
当然上面是我几番百度和查文档得到的结果。因此需要验证一下对不对。
所以第一步我写了一个demo.
mWebView = (WebView) findViewById(R.id.webview); mWebView.setWebViewClient(new WebViewClientImpl(this)); mWebView.setWebChromeClient(new WebChromeClient()); mWebView.getSettings().setBuiltInZoomControls(false); mWebView.getSettings().setDomStorageEnabled(true); //如果有缓存 就使用缓存数据 如果没有 就从网络中获取 mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); mWebView.getSettings().setDatabaseEnabled(true); mWebView.getSettings().setAppCacheEnabled(true); mWebView.loadUrl("http://www.csdn.net/");
这里setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK)打开了webView的本身缓存。
验证之后的结果是对的,图片文字的什么的都存在于data目录下。第一步已经成功了。可是这与需求还差十万八千里啊。
接下来我们又要解决以下几个问题:
1、webView的缓存在data目录下的,我们都知道手机内部存储是有限的,如何将webView的缓存存储到sd卡上呢?
2、如上述,如果解决了存储在SD卡中,如何写出自己的缓存机制呢?
好吧,思路就是这样,我们来探讨第一个问题。既然要缓存到SD卡上,但是webView是缓存在内部,我们是否可以看到的完整的目录呢?我们去查查文档,果然是有的。
/** * Returns the absolute path to the application specific cache directory * on the filesystem. These files will be ones that get deleted first when the * device runs low on storage. * There is no guarantee when these files will be deleted. * * <strong>Note: you should not <em>rely</em> on the system deleting these * files for you; you should always have a reasonable maximum, such as 1 MB, * for the amount of space you consume with cache files, and prune those * files when exceeding that space.</strong> * * @return The path of the directory holding application cache files. * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir */ public abstract File getCacheDir();
这个是webView拿到缓存目录的方法。既然是这样的话,我们把这个方法覆盖,换成我们自己SD卡上的目录不就可以了,说干就干。
@Override public void onCreate() { super.onCreate(); if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){ File externalStorageDir = Environment.getExternalStorageDirectory(); if (externalStorageDir != null){ // {SD_PATH}/Android/data/ extStorageAppBasePath = new File(externalStorageDir.getAbsolutePath() + File.separator +"Android" + File.separator + "data"+File.separator + getPackageName()); Log.i(TAG,extStorageAppBasePath+"====="); } if (extStorageAppBasePath != null){ extStorageAppCachePath = new File(extStorageAppBasePath.getAbsolutePath()+File.separator + "webViewCache"); Log.d("extStorageAppCachePath","extStorageAppCachePath = "+extStorageAppCachePath); boolean isCachePathAvailable = true; if (!extStorageAppCachePath.exists()){ isCachePathAvailable = extStorageAppCachePath.mkdirs(); if (!isCachePathAvailable){ extStorageAppCachePath = null; } } } } } @Override public File getCacheDir() { if (extStorageAppCachePath != null){ return extStorageAppCachePath; }else{ return super.getCacheDir(); } }
这些代码我放在application中,重写了getCacheDir(),把地址覆盖了。我们在webView的那个Activity中拿到即可。
@Override public File getCacheDir() { File cacheDir = getApplicationContext().getCacheDir(); Log.d("cacheDir","cacheDir = "+cacheDir.getAbsolutePath()); return cacheDir; }
测试了一下,表示坑爹了,怎么也缓存不到了SD卡上,搞得整整搞了一上午,发现原来Android4.4搞得鬼。Android4.4 设置了权限。这个大家可以去了解了解。这里不详细说。不过总算是拨的云雾见苍天了,哇哈哈。。。
换了一个测试机,果然是有效的。离目标更近了一步。接下来要出大招了。写自己的缓存机制。可以需求是缓存是有选择的,不能什么都缓存,这个可是有点难度的。首先我们来看下面一段代码。
public class WebViewClientImpl extends WebViewClient { private Activity activity = null; private UrlCache urlCache = null; public WebViewClientImpl(Activity activity) { this.activity = activity; this.urlCache = new UrlCache(activity); this.urlCache.register("http://www.csdn.net/", "cache", "text/html", "UTF-8", 5 * UrlCache.ONE_MINUTE); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if(url.indexOf("csdn.net") > -1 ) return false; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); activity.startActivity(intent); return true; } @Override public void onLoadResource(WebView view, String url) { super.onLoadResource(view, url); } @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { Log.d(TAG,"webResourceResponse ==== "+ url); return this.urlCache.load(url); } }
这里我们重写了WebViewClient,里面初始化了一个UrlCache类,这个类封装了Cache的方法。看代码最后一个方法,shouldInterceptRequest中拦截了所有请求,我这里做的粗略了一点,去匹配register()方法中标注的url,可以筛选缓存想要缓存的东西。
public class UrlCache { public static final long ONE_SECOND = 1000L; public static final long ONE_MINUTE = 60L * ONE_SECOND; public static final long ONE_HOUR = 60L * ONE_MINUTE; public static final long ONE_DAY = 24 * ONE_HOUR; private static class CacheEntry { public String url; public String fileName; public String mimeType; public String encoding; public long maxAgeMillis; private CacheEntry(String url, String fileName, String mimeType, String encoding, long maxAgeMillis) { this.url = url; this.fileName = fileName; this.mimeType = mimeType; this.encoding = encoding; this.maxAgeMillis = maxAgeMillis; } } protected Map<String, CacheEntry> cacheEntries = new HashMap<String, CacheEntry>(); protected Activity activity = null; protected File rootDir = null; public UrlCache(Activity activity) { this.activity = activity; // this.rootDir = this.activity.getFilesDir(); this.rootDir = this.activity.getCacheDir(); } public UrlCache(Activity activity, File rootDir) { this.activity = activity; this.rootDir = rootDir; } public void register(String url, String cacheFileName, String mimeType, String encoding, long maxAgeMillis) { CacheEntry entry = new CacheEntry(url, cacheFileName, mimeType, encoding, maxAgeMillis); this.cacheEntries.put(url, entry); } public WebResourceResponse load(final String url){ final CacheEntry cacheEntry = this.cacheEntries.get(url); if(cacheEntry == null) return null; final File cachedFile = new File(this.rootDir.getPath() + File.separator + cacheEntry.fileName); Log.d(Constants.LOG_TAG, "cacheFile from cache----: " + cachedFile.toString()+"=="+url); if(cachedFile.exists()){ long cacheEntryAge = System.currentTimeMillis() - cachedFile.lastModified(); if(cacheEntryAge > cacheEntry.maxAgeMillis){ cachedFile.delete(); //cached file deleted, call load() again. Log.d(Constants.LOG_TAG, "Deleting from cache: " + url); return load(url); } //cached file exists and is not too old. Return file. Log.d(Constants.LOG_TAG, "Loading from cache: " + url); try { return new WebResourceResponse( cacheEntry.mimeType, cacheEntry.encoding, new FileInputStream(cachedFile)); } catch (FileNotFoundException e) { Log.d(Constants.LOG_TAG, "Error loading cached file: " + cachedFile.getPath() + " : " + e.getMessage(), e); } } else { try{ downloadAndStore(url, cacheEntry, cachedFile); //now the file exists in the cache, so we can just call this method again to read it. return load(url); } catch(Exception e){ Log.d(Constants.LOG_TAG, "Error reading file over network: " + cachedFile.getPath(), e); } } return null; } private void downloadAndStore(String url, CacheEntry cacheEntry, File cachedFile) throws IOException { URL urlObj = new URL(url); URLConnection urlConnection = urlObj.openConnection(); InputStream urlInput = urlConnection.getInputStream(); FileOutputStream fileOutputStream = this.activity.openFileOutput(cacheEntry.fileName, Context.MODE_PRIVATE); int data = urlInput.read(); while( data != -1 ){ fileOutputStream.write(data); data = urlInput.read(); } urlInput.close(); fileOutputStream.close(); Log.d(Constants.LOG_TAG, "Cache file: " + cacheEntry.fileName + " stored. "); }
这个就是封装好的Cache,load()这个方法就是把webViewClient上拦截到的url来匹配CacheEntry实体类的url。开始缓存了。
好吧,差不多写完了。
- 【Android Studio】深入探究webView的缓存机制
- 深入探究Glide的缓存机制
- android webview的缓存机制
- android webview的缓存机制
- Android WebView 的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android中图片加载框架Glide解析3----深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android图片加载框架最全解析(三),深入探究Glide的缓存机制
- Android 中的Handler机制的深入探究
- Glide系列之三:深入探究Glide的缓存机制
- Android webView缓存机制
- Android WebView缓存机制
- 程序员的自我提升
- Unity 5.0 无法通过GUIText类型在脚本中调用画布中的Text文字
- MP3信息
- Android实现在横竖屏切换时信息保留不会丢失
- Java:IO流的一些典型例子
- 【Android Studio】深入探究webView的缓存机制
- Linode VPS
- vs2010 问题 >LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
- 对链表的基本的操作
- Linux下安装Tomcat服务器和部署Web应用
- Zookeeper 介绍 [To Be Continued]
- HTML--html简介
- 面试题08_旋转数组的最小数字——剑指offer系列
- #ifdef #else #endif #fi #ifndef 的用法