Android Hybrid 方案之 离线文件加载

Hybrid App目测大家都不陌生了, 详情介绍参考 http://blog.csdn.net/u012014301/article/details/55251519
本文主要介绍Android WebView离线资源加载的方案


为了使打开H5页面更快速, 通常会把H5的文件和资源打包做离线缓存, 这里主要介绍两种方式缓存加载的方式,以及两种方式的优缺点对比

H5文件资源打包和解压这里不做过多介绍, 通常涉及一个压缩包的下载,解压,版本升级等问题,不是本文的重点,本文假定已经资源文件以及离线缓存在了App的Cache目录下

首先介绍两个核心相关的方法

shouldOverrideUrlLoading

    /**
     * Give the host application a chance to take over the control when a new
     * url is about to be loaded in the current WebView. If WebViewClient is not
     * provided, by default WebView will ask Activity Manager to choose the
     * proper handler for the url. If WebViewClient is provided, return true
     * means the host application handles the url, while return false means the
     * current WebView handles the url.
     * This method is not called for requests using the POST "method".
     *
     * @param view The WebView that is initiating the callback.
     * @param url The url to be loaded.
     * @return True if the host application wants to leave the current WebView
     *         and handle the url itself, otherwise return false.
     * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
     *             shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
     */
    @Deprecated
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return false;
    }

从注释上可以看出, 上面这个方法能够让应用决定是否重载对一个url加载,如果该方法返回true, 则表示应用处理了这个Url,返回false则由WebView处理url, WebView处理实际上就是打开该url, 该方法只会在触发页面内跳转的时候(href=’xxx’)的时候才会调用, loadUrl的时候并不会调用该方法

shouldInterceptRequest

    /**
     * Notify the host application of a resource request and allow the
     * application to return the data.  If the return value is null, the WebView
     * will continue to load the resource as usual.  Otherwise, the return
     * response and data will be used.  NOTE: This method is called on a thread
     * other than the UI thread so clients should exercise caution
     * when accessing private data or the view system.
     *
     * @param view The {@link android.webkit.WebView} that is requesting the
     *             resource.
     * @param url The raw url of the resource.
     * @return A {@link android.webkit.WebResourceResponse} containing the
     *         response information or null if the WebView should load the
     *         resource itself.
     * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest)
     *             shouldInterceptRequest(WebView, WebResourceRequest)} instead.
     */
    @Deprecated
    public WebResourceResponse shouldInterceptRequest(WebView view,
            String url) {
        return null;
    }

从这个方法的名字可以看出, 它具有拦截的功能, 类似于OkHttp的网络拦截器,对于一个请求, 应用可以主动拦截这个请求的返回, 如果该返回null, 则表示交由WebView处理, 应用不做拦截

一 File本地文件传输协议加载

要使用File协议,基本的格式如下:file:///文件路径
通常File本地文件与WebViewClient的shouldOverrideUrlLoading结合使用, 可以实现简单的离线文件加载效果
当我们请求一个H5页面的时候,如果该H5文件以及在本地缓存中存在,我们可以使用用file文件加载协议加载该页面,同时让shouldOverrideUrlLoading方法返回true则可以实现我们的离线文件加载,下面用代码演示该方案.

public class MainActivity extends Activity {

    protected WebView mWebView;
    private static final String sURL = "http://www.dy2018.com/i/97824.html";
    private long mStarTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = (WebView) findViewById(R.id.web_view);
        initWebView();

        mStarTime = System.currentTimeMillis();
        openUrl(sURL);
    }

    private void initWebView() {
        WebSettings settings = mWebView.getSettings();
        settings.setJavaScriptEnabled(true);

        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Log.d("LJTAG", "shouldOverrideUrlLoading: " + url);
                return MainActivity.this.shouldOverrideUrlLoading(url);
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                Log.d("LJTAG", "useTime: " + (System.currentTimeMillis() - mStarTime));
            }
        });
    }

    /**
     * shouldOverrideUrlLoading 是在页面内跳转的时候会被调用的方法
     * 第一次loadUrl的时候 是不会调用shouldOverrideUrlLoading方法的
     * 所以打开指定页面的时候要通过我们自己的方法去打开指定页面
     */
    private void openUrl(String url) {
        String cacheFileUrl = getCacheFileUrl(url);
        if (null != cacheFileUrl) {
            mWebView.loadUrl(cacheFileUrl);
        } else {
            mWebView.loadUrl(url);
        }
    }

    /**
     * 根据url判断本地缓存文件是否存在
     * 如果存在 则应用拦截本次url加载同时使用file协议访问该文件
     *
     * @param url
     * @return
     */
    private boolean shouldOverrideUrlLoading(String url) {
        String cacheFileUrl = getCacheFileUrl(url);
        if (cacheFileUrl == null) return false;

        mWebView.loadUrl(cacheFileUrl);
        return true;
    }

    /**
     * 如果Cache文件命中, 返回Cache文件的file协议地址,否则返回null
     *
     * @param aimUrl
     * @return
     */
    private String getCacheFileUrl(String aimUrl) {
        try {
            URL url = new URL(aimUrl);
            Log.d("LJTAG", "getCacheFileUrl: " + url);
            if (url.getProtocol().startsWith("http")) {
                // 如果本地已缓存该文件 则返回该文件的file协议地址
                // 这里只是简单的以本地SD卡的应用缓存文件夹 作为url根目录拼接起来
                // 比如说对于请求 http://www.dy2018.com/i/97729.html
                // 在本地的路径就是 getExternalCacheDir() + "/i/97729.html"
                // 真实环境中要考虑SD卡是否存在的问题以及具体的缓存目录选择
                File cacheFile = new File(getExternalCacheDir(), url.getPath());
                if (cacheFile.exists()) {
                    Log.d("LJTAG", cacheFile.getAbsolutePath() + " 文件存在, 直接使用file协议访问");
                    return "file://" + cacheFile.getAbsolutePath();
                } else {
                    Log.d("LJTAG", cacheFile.getAbsolutePath() + " 文件不存在, 直接访问线上资源");
                }
            }
        } catch (MalformedURLException e) {
        }
        return null;
    }
}

布局文件如下, 很简单, 就一个WebView

<?xml version="1.0" encoding="utf-8"?>
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/web_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.liujian.hybridproject.MainActivity" />

这里简单的加载了电影天堂的一个页面http://www.dy2018.com/i/97824.html, 我们看看运行的效果

因为当前缓存路径中没有任何文件, 所以在加载的时候直接走了线上, 我们看一下日志的打印

D/LJTAG: getCacheFileUrl: http://www.dy2018.com/i/97824.html
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/i/97824.html 文件不存在, 直接访问线上资源
D/LJTAG: useTime: 1467

从日志可以看出 没有使用缓存, 从loadUrl 到 onPageFinished总耗时1467ms(时间不是绝对的,更很多因素有关,只做对比参考)

把该网页保存下来, 对标题做一些修改,用来区分线下资源和线上资源, 再Push到cache目录之后

adb push 97824.html /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/i
[100%] /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/i

我们再看看效果

D/LJTAG: getCacheFileUrl: http://www.dy2018.com/i/97824.html
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/i/97824.html 文件存在, 直接使用file协议访问
D/LJTAG: useTime: 281

可以看出已经成功的加载出了本地缓存的文件,但是可以发现图片资源和页面布局都乱掉了,我们查看一下这个页面的html代码

<link href="/css/dygod.css" rel="stylesheet" type="text/css" />
<script src="/js/search.js" language="javascript"></script>

这里的css和js文件路径, 都是以相对路径的形式加载的,当我们以file文件协议访问的时候,都是相对根路径做文件访问
比如说访问以下地址

file:///storage/emulated/0/Android/data/com.liujian.hybridproject/cache/i/97824.html

如果该HTML文件中引用了 /css/dygod.css文件,那么浏览器实际上会去访问

file:///css/dygod.css

这个文件当然不存在, 所以页面的css和js都会加载失败
这个问题的解决方式有两种

  • 修改css和js的引用路径,使用绝对路径加载, 比如说 /css/dygod.css 修改成 http://www.dy2018.com/css/dygod.css, 或者把相对根目录改为相对当前目录, 比如说把/css/dygod.css修改成 css/dygod.css (这个方案对H5的改动较大,目录结构都需要调整)
  • 通过base标签指定 href, 这个其实和上面的换成绝对路径的方法差不多, 不过这个方法一劳永逸,相当于把当前页面中的跟目录指定为 href 所对应的地址, 相对于根目录的路径都会被替换成 href + 相对路径来加载, 下面的代码,即使当前的文件链接是file://, 最终请求的dygod.css的路径依然是 http://www.dy2018.com/css/dygod.css
<base href="http://www.dy2018.com">
<link href="/css/dygod.css" rel="stylesheet" type="text/css" />

我们在本地的离线HTML文件中写入base之后,再看加载的效果

D/LJTAG: getCacheFileUrl: http://www.dy2018.com/i/97824.html
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/i/97824.html 文件存在, 直接使用file协议访问
D/LJTAG: useTime: 537

可以看出HTML依然是我们的App本地离线缓存文件,但是css已经正常的加载出来了,同时页面的加载时间明显得到了很大的提升

方案一的离线加载方式基本就是上面的模式,可以看出这个方案比较简单,相当于使用了一个本地的缓存路径来搭建了一个服务器,文件从本地离线缓存中直接获取,从而提高了页面加载的速度

但是该方案有如下几个问题

  • 非可链接跳转的资源请求离线加载,因为shouldOverrideUrlLoading是在页面跳转的时候才调用的方法,所以css,js文件要加载,只能使用上面两种方案解决文件不存在的问题,对HTML的开发和文件结构有一定的要求
  • JS存在跨域问题,因为与接口相关的请求肯定可能是走线上服务器的,但是当前的链接其实是file本地文件协议访问的,所以要解决JS跨域问题,通常是要服务端和前端配合,或者前端和客户端配合,把前端的所有接口请求通过JSBBridge调用,通过客户端访问接口再回传给前端

所以方案一其实是一个侵入性比较强的离线资源加载方案,对客户端和前端都有一些约束,据笔者所知,某度某米的Hybrid资源加载方案就是这样的

二 shouldInterceptRequest拦截请求

shouldInterceptRequest是WebViewClient的方法,之前已经提到过,它相当于一个拦截器,可以获取到WebView中的所有请求,我们先来看看代码

public class InterceptActivity extends Activity {

    protected WebView mWebView;
    private static final String URL = "http://www.dy2018.com/i/97824.html";
    private long mStarTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = (WebView) findViewById(R.id.web_view);
        initWebView();

        mStarTime = System.currentTimeMillis();
        mWebView.loadUrl(URL);
    }

    private void initWebView() {
        WebSettings settings = mWebView.getSettings();
        settings.setJavaScriptEnabled(true);

        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                Log.d("LJTAG", "shouldInterceptRequest: " + url);
                return null;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
//                Log.d("LJTAG", "useTime: " + (System.currentTimeMillis() - mStarTime));
            }
        });
    }

    /**
     * 如果Cache文件命中, 返回Cache文件的file协议地址,否则返回null
     *
     * @param aimUrl
     * @return
     */
    private String getCacheFileUrl(String aimUrl) {
        try {
            URL url = new URL(aimUrl);
            Log.d("LJTAG", "getCacheFileUrl: " + url);
            if (url.getProtocol().startsWith("http")) {
                // 如果本地已缓存该文件 则返回该文件的file协议地址
                // 这里只是简单的以本地SD卡的应用缓存文件夹 作为url根目录拼接起来
                // 比如说对于请求 http://www.dy2018.com/i/97729.html
                // 在本地的路径就是 getExternalCacheDir() + "/i/97729.html"
                // 真实环境中要考虑SD卡是否存在的问题以及具体的缓存目录选择
                File cacheFile = new File(getExternalCacheDir(), url.getPath());
                if (cacheFile.exists()) {
                    Log.d("LJTAG", cacheFile.getAbsolutePath() + " 文件存在, 直接使用file协议访问");
                    return "file://" + cacheFile.getAbsolutePath();
                } else {
                    Log.d("LJTAG", cacheFile.getAbsolutePath() + " 文件不存在, 直接访问线上资源");
                }
            }
        } catch (MalformedURLException e) {
        }
        return null;
    }
}
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/i/97824.html
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/css/dygod.css
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/css/searchpage.css
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/search.js
D/LJTAG: shouldInterceptRequest: http://tu.23juqing.com/d/file/html/gndy/dyzz/2017-03-15/01477a7ebe05a1aa4f5fd053a050454c.jpg
D/LJTAG: shouldInterceptRequest: http://pstatic.xunlei.com/js/webThunderDetect.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/base64.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/thunderForumLinker.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/tj.js
...

这里只是简单的请求了这样一个链接http://www.dy2018.com/i/97824.html,但是这个方法拦截到到了里面所有的请求,如果我们在请求的时候把请求结果替换成本地文件, 应该就可以实现我们想要的效果了,这里我们看看这个方法的详细参数

public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
    return null;
}

从上文可以知道, 如果在这个方法返回null, 表示不拦截, WebView会自己去执行原有的加载逻辑, 如果我们返回一个WebResourceResponse, 那么WebView会用这个对象当做请求的结果,从而起到一个拦截的效果, 我们看看WebResourceResponse的构造方法

public WebResourceResponse(String mimeType, String encoding, InputStream data) {
    mMimeType = mimeType;
    mEncoding = encoding;
    setData(data);
}

这是其中的一个构造方法, 熟悉HTML的同学应该知道这三个参数的作用, 我们先写个简单的测试样例, 当我们加载上面的电影天堂的链接的时候, 构造一个本地HTML的WebResourceResponse返回过去, 看看效果如何

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <META http-equiv=Content-Type content="text/html; charset=utf-8">
    </head>
    <body style="background-color: red">
    </body>
</html>

HTML代码如上, 很简单, 就是一个全红色的网页, 我们首先不把它Push到Cache目录中, Android代码如下

public class InterceptActivity extends Activity {

    protected WebView mWebView;
    private static final String URL = "http://www.dy2018.com/i/97824.html";
    private long mStarTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = (WebView) findViewById(R.id.web_view);
        initWebView();

        mStarTime = System.currentTimeMillis();
        mWebView.loadUrl(URL);
    }

    private void initWebView() {
        WebSettings settings = mWebView.getSettings();
        settings.setJavaScriptEnabled(true);

        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                Log.d("LJTAG", "shouldInterceptRequest: " + url);
                if (URL.equals(url)) {
                    File file = new File(getExternalCacheDir(), "local.html");
                    if (file.exists()) {
                        try {
                            InputStream inputStream = new FileInputStream(file);
                            return new WebResourceResponse("text/html", "utf-8", inputStream);
                        } catch (FileNotFoundException e) {
                            Log.e("LJTAG", e.getMessage());
                        }
                    }
                }
                return null;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);
                Log.d("LJTAG", "useTime: " + (System.currentTimeMillis() - mStarTime));
            }
        });
    }
}

我们看一下运行效果

因为当前没有HTML文件在Cache目录中不存在, 所以还是加载出了线上的资源文件, 现在我们把HTML文件Push到Cache目录中去再看看效果

adb push local.html /storage/emulated/0/Android/data/com.liujian.hybridproject/cache
[100%] /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/local.html

重新运行之后

可以看出, 已经把我们想要加载出来的内容成功的显示出来了.

看到这里大家应该基本了解了这个方案的主要流程了,下面总结一下这个方案的大致流程

  1. 把所有的请求都拦截下来
  2. 根据请求链接判断本地是否有对应的缓存文件
  3. 如果本地没有缓存文件, 直接返回null, 由WebView去请求
  4. 如果本地有缓存文件, 根据文件类型, 构造WebResourceResponse返回拦截结果

我们对代码再做一点点修改, 把文件类型和MimeType(MimeType列表参考)做一个关联

首先我们把这一个网页加载中请求的所有链接打印出来(为了方便缓存文件判断,只打印域名为www.dy2018.com的请求)

D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/i/97824.html
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/css/dygod.css
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/css/searchpage.css
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/search.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/base64.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/thunderForumLinker.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/js/tj.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/jsdd/f.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/css/index.css?1
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/css/db.css?1
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/logo.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/menubg.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/search_02.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/search_01.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/search_03.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/search_btn.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/images/tbg.gif
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/jsdd/a.js
D/LJTAG: shouldInterceptRequest: http://www.dy2018.com/favicon.ico

然后修改我们的Android代码

public class InterceptActivity extends Activity {

    private static HashMap<String, String> sMimeTypeMap = new HashMap<>();

    static {
        sMimeTypeMap.put("html", "text/html");
        sMimeTypeMap.put("css", "text/css");
        sMimeTypeMap.put("js", "text/javascript");
        sMimeTypeMap.put("gif", "image/gif");
        sMimeTypeMap.put("ico", "image/x-icon");
        sMimeTypeMap.put("png", "image/png");
        sMimeTypeMap.put("jpg", "image/jpeg");
        sMimeTypeMap.put("jpeg", "image/jpeg");
    }

    protected WebView mWebView;
    private static final String URL = "http://www.dy2018.com/i/97824.html";
    private long mStarTime;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mWebView = (WebView) findViewById(R.id.web_view);
        initWebView();

        mStarTime = System.currentTimeMillis();
        mWebView.loadUrl(URL);
    }

    private void initWebView() {
        WebSettings settings = mWebView.getSettings();
        settings.setJavaScriptEnabled(true);

        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                if (url.contains("http://www.dy2018.com")) {
                    return getCacheResponse(url);
                }
                return null;
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                Log.d("LJTAG", "useTime: " + (System.currentTimeMillis() - mStarTime));
            }
        });
    }

    /**
     * 根据url获取WebResourceResponse, 如果不存在缓存文件, 返回null
     * 否则根据文件名后缀 查找对应的MimeType构造WebResourceResponse, 编码都使用utf-8
     *
     * @param aimUrl
     * @return
     */
    private WebResourceResponse getCacheResponse(String aimUrl) {
        try {
            URL url = new URL(aimUrl);
            // Log.d("LJTAG", "getCacheFileUrl: " + url);
            // 如果本地已缓存该文件 则返回该文件的file协议地址
            // 这里只是简单的以本地SD卡的应用缓存文件夹 作为url根目录拼接起来
            // 比如说对于请求 http://www.dy2018.com/i/97729.html
            // 在本地的路径就是 getExternalCacheDir() + "/i/97729.html"
            // 真实环境中要考虑SD卡是否存在的问题以及具体的缓存目录选择
            File cacheFile = new File(getExternalCacheDir(), url.getPath());
            if (cacheFile.exists()) {
                Log.d("LJTAG", cacheFile.getAbsolutePath() + " 文件存在, 直接拦截请求");
                InputStream inputStream = new FileInputStream(cacheFile);
                // 这里还可以是一个网络流 比如 InputStream inputStream = new URL("http://www.baidu.com").openConnection().getInputStream()
                String fileName = cacheFile.getName();
                int lastDot = fileName.lastIndexOf(".");
                String suffix = lastDot > -1 ? fileName.substring(lastDot + 1).toLowerCase() : null;
                return new WebResourceResponse(sMimeTypeMap.get(suffix), "utf-8", inputStream);
            } else {
                Log.d("LJTAG", cacheFile.getAbsolutePath() + " 文件不存在");
            }
        } catch (MalformedURLException e) {
            Log.e("LJTAG", e.getMessage());
        } catch (FileNotFoundException e) {
            Log.e("LJTAG", e.getMessage());
        }
        return null;
    }
}

首先Cache文件夹中没有缓存文件, 运行日志如下, 页面能够成功的加载

...
D/LJTAG: getCacheFileUrl: http://www.dy2018.com/images/menubg.gif
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/images/menubg.gif 文件不存在
D/LJTAG: getCacheFileUrl: http://www.dy2018.com/jsdd/a.js
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/jsdd/a.js 文件不存在
D/LJTAG: getCacheFileUrl: http://www.dy2018.com/images/search_02.gif
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/images/search_02.gif 文件不存在
...

我们把缓存文件放到Cache目录下之后, 日志文件如下, 页面也成功的加载出来, 说明我们的离线资源加载成功

...
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/jsdd/f.js 文件存在, 直接拦截请求
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/e/public/ViewClick 文件不存在
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/css/index.css 文件存在, 直接拦截请求
D/LJTAG: /storage/emulated/0/Android/data/com.liujian.hybridproject/cache/css/db.css 文件存在, 直接拦截请求
...

相对于方案一, 方案二的缓存加载方案更加强大, 相对于H5是透明的, 无任何侵入性, 实现也相对简单, 还能实现图片视频等其他资源的离线缓存, 目前我们项目中用的就是这套离线方案.

以上就是本人对目前的Hybrid 资源缓存方案的总结, 如有不足, 欢迎指正. 第一篇博客, 希望能帮助到大家 共同学习.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值