[Android Studio] explores the cache mechanism of webView in depth, androidwebview

Source: Internet
Author: User

[Android Studio] explores the cache mechanism of webView in depth, androidwebview

Webview has been around recently. Android Developers may know that webView has its own caching mechanism. company needs to write its own caching mechanism instead of webView, wow, it's challenging. I wrote this blog to record my learning process. Not easy to write, Do not spray.
First, we need to understand what the cache mechanism of webView is?
WebView has two types of cache:
The first is the webpage data cache (that is, browsing resources on the webpage), but the H5 cache (that is, appCache ).
Cache directory of webView:
/Data/package_name/cache/
WebView cache mode:
LOAD_CACHE_ONLY: only reads local cache data without using the network
LOAD_DEFAULT: determines whether to retrieve data from the Network Based on cache-control.
LOAD_CACHE_NORMAL: API level 17 has been deprecated. The function starting from API level 11 is the same as that in LOAD_DEFAULT mode.
LOAD_NO_CACHE: no cache is used. Only data is obtained from the network.
LOAD_CACHE_ELSE_NETWORK: cache data is used as long as it exists locally, whether it expires or not, or no-cache.

Of course, the above is the result of my Baidu and query documents. Therefore, you need to verify whether it is correct.
So the first step is to write a demo.

MWebView = (WebView) findViewById (R. id. webview); mWebView. setWebViewClient (new WebViewClientImpl (this); mWebView. setWebChromeClient (new WebChromeClient (); mWebView. getSettings (). setBuiltInZoomControls (false); mWebView. getSettings (). setDomStorageEnabled (true); // obtain the mWebView from the network if there is cached data. getSettings (). setCacheMode (WebSettings. LOAD_CACHE_ELSE_NETWORK); mWebView. getSettings (). setDatabaseEnabled (true); mWebView. getSettings (). setAppCacheEnabled (true); mWebView. loadUrl ("http://www.csdn.net /");

Here, setCacheMode (WebSettings. LOAD_CACHE_ELSE_NETWORK) opens the webView's own cache.
After verification, the result is correct. The image text and everything are stored in the data directory. The first step is successful. However, this is 108,000 less than the requirement.
Next, we need to solve the following problems:
1. The webView is cached in the data directory. We all know that the internal storage of mobile phones is limited. How can we store the webView cache on the SD card?
2. As mentioned above, how can I write out my own cache mechanism if I solve the problem of storing it in the SD card?

Well, the idea is like this. Let's discuss the first question. Since webView is cached on the SD card, can we see the complete directory? Let's check the document.

 /**     * 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();

This is how webView obtains the cache directory. In this case, we can replace this method with the directory on our own SD card.

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

I put these codes in the application, overwrite getCacheDir (), and overwrite the address. We can get it in the webView Activity.

@Override    public File getCacheDir() {        File cacheDir = getApplicationContext().getCacheDir();        Log.d("cacheDir","cacheDir = "+cacheDir.getAbsolutePath());        return cacheDir;    }

After a test, it indicates that the cache is not stored on the SD card. After a whole morning, we found that Android4.4 was a ghost. Android4.4 is configured with permissions. You can understand this. It is not detailed here. However, it was just a few minutes to see the sky. Wow, haha...
It is effective to change to a testing machine. It is a step closer to the target. Next we will make a big move. Write your own cache mechanism. The requirement is that the cache is optional and cannot be cached at all. This is a bit difficult. First, let's look at the following code.

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

Here we overwrite the WebViewClient, which initializes a UrlCache class, which encapsulates the Cache method. Looking at the last method of the Code, shouldInterceptRequest intercepts all requests. I am doing a rough look here, matching the url marked in the register () method, you can filter what the cache wants to cache.

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

This is the encapsulated Cache. The load () method is to match the url intercepted on the webViewClient to the url of the CacheEntry object class. Cache started.
Okay, it's almost finished.

Copyright Disclaimer: This article is an original article by the blogger and cannot be reproduced without the permission of the blogger.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.