0

    HybridCache:一种简单的native与webview共享缓存的设计

    2023.08.01 | admin | 124次围观

    HybridCache简而言之其实是一套native和webview共享缓存的解决方案。不过在了解HybridCache的实现细节以及能够解决的问题之前,先大概了解一下web开发中涉及到的缓存机制

    Web缓存机制

    实际上,web开发当中已经具备相当完善的缓存机制,并且Android系统的WebView对这些已有的缓存机制基本上都提供了完备的支持。

    web的缓存机制有以下两大类:

    浏览器缓存机制

    浏览器自身的缓存机制是基于http协议层的Header中的信息实现的

    这些技术都是协议层所定义的,在Android的webview当中我们可以通过配置决定是否采纳这几个协议的头部属性。设置如下:

    webView.settings.cacheMode=WebSettings.LOAD_DEFAULT
    // cacheMode的取值定义如下:
    @IntDef({LOAD_DEFAULT, LOAD_NORMAL, LOAD_CACHE_ELSE_NETWORK, LOAD_NO_CACHE, LOAD_CACHE_ONLY})
    @Retention(RetentionPolicy.SOURCE)
    public @interface CacheMode {}
    复制代码

    web开发中的缓存机制

    关于以上这几个web开发中的缓存机制,可以参考这篇文章Android:手把手教你构建 全面的WebView 缓存机制 & 资源加载方案

    认识HybridCache

    HybridCache旨在提供一种native和webview之间共享缓存的解决方案,尤其是共享native中的图片缓存。在native开发中,我们广泛的使用着各种图片加载库,比如:

    这些存在native的图片加载框架为我们提供了非常良好的图片缓存体验。HybridCache的一种具体运用,就是把在webview中的图片交由我们的native的图片加载框架(或者是我们自己实现的文件缓存)进行缓存,这样的好处就是:

    当然图片缓存只是一个相当具体的运用,实际上HybridCache提供的是更为广泛的webview资源加载拦截的功能,通过拦截webview中渲染网页过程中各种资源(包括图片、js文件、css样式文件、html页面文件等)的下载,根据业务的场景考虑缓存的策略,可以从app端提供webview的缓存技术方案(不需要前端人员感知的)。

    实现原理

    Android的webview在加载网页的时候,用户能够通过系统提供的API干预各个中间过程。而HybridCache要拦截的就是网页资源请求的环节。这个过程,WebViewClient当中提供了以下两个入口:

    public class WebViewClient {
    	// android5.0以上的版本加入
       public WebResourceResponse shouldInterceptRequest(WebView view,
                WebResourceRequest request) {
            return shouldInterceptRequest(view, request.getUrl().toString());
        }
    	  @Deprecated
        public WebResourceResponse shouldInterceptRequest(WebView view,
                String url) {
            return null;
        }
    }
    复制代码

    上面的两个API是在调用了WebView#loadUrl()之后,请求网页资源(包括html文件、js文件、css文件以及图片文件)的时候回调。关于这两个API有几个点需要注意:

    只要在这两个入口构造正确的WebResourceResponse对象,就可以替换默认的请求为我们提供的资源。因此,webview和native缓存共享的方案就是通过这两个入口,在每次请求资源的时候根据请求的URL/WebResourceRequest判断是否存在本地的缓存,并在缓存存在的情况下将缓存的输入流返回,示意图如下所示:

    方案设计

    先放上一张方案实现的设计类图:

    ps:这张类图是一开始设计方案的时候画的浏览器缓存机制有几种,后续经过了多次重构和调整,部分已经不尽一致,不过基本保持了核心的概念和结构

    HybridCache的核心任务就是拦截资源请求,下载资源并缓存资源,因此整个库的设计就分为了下面三个核心点:

    资源请求拦截

    参考okhttp拦截器的思想设计了WebResInterceptor和Chain两个接口,定义了拦截的动作以及驱动拦截器的链条。实际上,这两个接口都只是类库内部可见。具体的实现是BaseInterceptor和DefaultInterceptorChain两个对象。

    BaseInterceptor是拦截发生和资源响应的核心对象,内部处理了包括寻找缓存资源、下载资源和写缓存的基本逻辑。同时它是一个抽象类,子类只需要实现它并根据对应的资源请求定义是否参与拦截、以及选择性的自定义配置下载和缓存的行为即可。

    DefaultInterceptorChain仅仅只是用于用于驱动拦截器链条的流转,类库内部可见

    资源响应

    资源响应有两种情况:

    当对应的资源缓存不存在的时候,会直接触发资源的下载。在类库内部,会通过HttpConnectionDownloader直接建立一个HttpURLConnection进行资源的下载,获得资源的文件流。

    同时参考代理模式,设计了边读边写的动作。即下载的资源流通过被封装为一个WebResInputStreamWrapper对象后直接返回。WebResInputStreamWrapper继承于InputStream,同时内部持有一个TempFileWriter的实例。在WebResInputStreamWrapper被浏览器读取的同时,TempFileWriter会把对应的资源写入到缓存当中浏览器缓存机制有几种,实现边读边写

    缓存

    CacheProvider定义了提供缓存的实现的规范,可以根据实际的业务场景提供任意的缓存实现方案。同时库内部通过LruCache提供了简单的文件缓存的实现SimpleCacheProvider。同时为了拓展共享图片缓存的实现,类库还提供了一个基于fresco的图片缓存提供实例FrescoImageProvider

    CacheKeyProvider使得业务可以根据实际的场景提供缓存的key的生成策略。

    关于方案的实现细节,可以关注我的GitHub仓库HybridCache

    接入使用

    在图片缓存以及简单的使用文件缓存资源这两个场景上,方案已经提供了直接的实现,可以简单的一键接入使用,总的接入步骤如下:

    根据业务需要定义你的拦截器。你只需要继承BaseInterceptor,并实现仅有的一个抽象方法即可。如果你需要图片拦截器,可以直接使用类库内部提供的ImageInterceptor在定义拦截器的同时,你可以实现你的缓存提供器,提供你的缓存管理策略。默认的情况下会使用SimpleCacheProvider提供文件缓存使用HybridCacheManager#addCacheInterceptor()将拦截器添加都管理器中。在初始化webview的时候,设置自定义的WebViewClient对象,并在其拦截资源请求的入口方法中调用HybridCacheManager#interceptWebResRequest()方法

    以上简单的几步即可拥有native和webview共享缓存的功能。具体的实例可以参考GitHub仓库中的demo。

    你可能会遇到的坑

    在使用webview的时候,你可能会遇到一些坑

    目前这个方案已经在我们的项目中实际使用。你可以在我的GitHub仓库HybridCache中看到简单的实例.欢迎大家表达对这个方案的设计的看法和改进意见,谢谢。

    版权声明

    本文仅代表作者观点。
    本文系作者授权发表,未经许可,不得转载。

    发表评论