使用WebView實(shí)現(xiàn)離線閱讀

1.先看效果

加載動(dòng)畫

這里寫圖片描述

加載完成瓮栗,注意當(dāng)前為飛行模式!

這里寫圖片描述

2.使用

1.讓你的javabean實(shí)現(xiàn)OffLineLevelItem接口,因?yàn)槲业倪@個(gè)離線閱讀支持多級(jí)下載摧玫,比如Demo中的每個(gè)頻道下面的第一頁(yè)item都可以緩存推穷。

package com.zgh.offlinereader;

import java.util.List;

/**
 * Created by zhuguohui on 2016/6/7.
 */
public interface OffLineLevelItem  {
    //是否有下一級(jí)
    boolean haveNextLevel();
    //內(nèi)容url
    String getWebUrl();
    //下一級(jí)的url
    String getNextLevelListUrl();
    //生成下一級(jí)
    List<OffLineLevelItem> getNextLevelList(String jsonStr);
}

public class Channel implements OffLineLevelItem {
    String title;
    String url;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public boolean haveNextLevel() {
        return true;
    }

    @Override
    public String getWebUrl() {
        return null;
    }

    @Override
    public String getNextLevelListUrl() {
        return url;
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        List<OffLineLevelItem> items = GsonUtil.jsonToBeanList(jsonStr, NewsItem.class);
        return items;
    }
}

2.初始化

   OfflineReaderServer.init(this, getCacheDir(), new MyFirstLevel(),new WaterWaveProgressUI(this));

3.啟動(dòng)

 @Override
    public void onClick(View v) {
        Intent intent=new Intent(this, OfflineReaderServer.class);
        startService(intent);
    }

4.記得在你的webview使用前調(diào)用

    //設(shè)置緩存目錄
    WebViewHelper.setWebViewConfig(webView);

就這么簡(jiǎn)單慧耍!

實(shí)現(xiàn)

首先我們?yōu)槭裁匆褂脀ebview實(shí)現(xiàn)離線閱讀,因?yàn)楹?jiǎn)單概龄。webview自帶的緩存機(jī)制可以實(shí)現(xiàn)圖片还惠,js,css的緩存。不然你自己得實(shí)現(xiàn)數(shù)據(jù)庫(kù)私杜,html下載蚕键,js下載救欧,css保存,html的拼裝锣光。下面我將講解一些webview設(shè)置緩存笆怠,實(shí)現(xiàn)多級(jí)下載,webview遍歷url誊爹,webview顯示完成監(jiān)聽蹬刷。

1.WebView設(shè)置緩存

這一部分比較簡(jiǎn)單,主要是緩存目錄的設(shè)置频丘,然后設(shè)置緩存模式為WebSettings.LOAD_CACHE_ELSE_NETWORK办成,這種模式下webview會(huì)優(yōu)先加載本地緩存,如果沒(méi)有緩存的話再加載網(wǎng)絡(luò)搂漠。

        mWebView.getSettings().setRenderPriority(WebSettings.RenderPriority.HIGH);
        // 建議緩存策略為迂卢,判斷是否有網(wǎng)絡(luò),有的話状答,使用LOAD_DEFAULT,無(wú)網(wǎng)絡(luò)時(shí)冷守,使用LOAD_CACHE_ELSE_NETWORK

        mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); // 設(shè)置緩存模式
        // 開啟DOM storage API 功能
        mWebView.getSettings().setDomStorageEnabled(true);
        // 開啟database storage API功能
        mWebView.getSettings().setDatabaseEnabled(true);
        // String cacheDirPath = getFilesDir().getAbsolutePath()
        //         + APP_CACHE_DIRNAME;
        String cacheDirPath = ConfigUtil.getCacheDir()
                + APP_CACHE_DIRNAME;
        Log.i(TAG, "cachePath=" + cacheDirPath);
        // 設(shè)置數(shù)據(jù)庫(kù)緩存路徑
        mWebView.getSettings().setDatabasePath(cacheDirPath); // API 19 deprecated
        // 設(shè)置Application caches緩存目錄
        mWebView.getSettings().setAppCachePath(cacheDirPath);
        // 開啟Application Cache功能
        mWebView.getSettings().setAppCacheEnabled(true);
        mWebView.getSettings().setAppCacheMaxSize(MAX_SIZE);

2.多級(jí)緩存

我的項(xiàng)目中需要將每個(gè)頻道的首頁(yè)中的每個(gè)item都緩存下來(lái),所以涉及到多級(jí)緩存于是我設(shè)計(jì)了一個(gè)接口在離線閱讀的時(shí)候最重要的是拿到葉子節(jié)點(diǎn)也就是每個(gè)item的url地址惊科,如果是每葉子節(jié)點(diǎn)也就是haveNextLevel()返回true的時(shí)候就調(diào)用getNextLevelListUrl獲取下一級(jí)的url,一般都是Jason字符串亮钦,再把json字符串傳入getNextLevelList()方法獲取下一級(jí)馆截,如果到達(dá)葉子節(jié)點(diǎn),則調(diào)用getWebUrl()獲取url地址保存在一個(gè)集合中蜂莉,當(dāng)所有的url都獲取以后蜡娶,就開始用webview遍歷url實(shí)現(xiàn)緩存。

public interface OffLineLevelItem  {
    //是否有下一級(jí)
    boolean haveNextLevel();
    //內(nèi)容url
    String getWebUrl();
    //下一級(jí)的url
    String getNextLevelListUrl();
    //生成下一級(jí)
    List<OffLineLevelItem> getNextLevelList(String jsonStr);
}

頻道的javabean

public class Channel implements OffLineLevelItem {
    String title;
    String url;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public boolean haveNextLevel() {
        return true;
    }

    @Override
    public String getWebUrl() {
        return null;
    }

    @Override
    public String getNextLevelListUrl() {
        return url;
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        List<OffLineLevelItem> items = GsonUtil.jsonToBeanList(jsonStr, NewsItem.class);
        return items;
    }
}

item的javabean

public class NewsItem implements OffLineLevelItem{
    String title;
    String url;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public String toString() {
        return title;
    }

    @Override
    public boolean haveNextLevel() {
        return false;
    }

    @Override
    public String getWebUrl() {
        return url;
    }

    @Override
    public String getNextLevelListUrl() {
        return null;
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        return null;
    }
}

當(dāng)然為了獲取到頻道列表需要一個(gè)第一級(jí)的目錄映穗,而這個(gè)目錄在初始化的時(shí)候就設(shè)置進(jìn)去了窖张。

public class MyFirstLevel implements OffLineLevelItem {
    @Override
    public boolean haveNextLevel() {
        return true;
    }

    @Override
    public String getWebUrl() {
        return null;
    }

    @Override
    public String getNextLevelListUrl() {
        return "raw://news_list";
    }

    @Override
    public List<OffLineLevelItem> getNextLevelList(String jsonStr) {
        List<OffLineLevelItem> items = GsonUtil.jsonToBeanList(jsonStr, Channel.class);
        return items;
    }
}

3.使用WebView遍歷URL,我原來(lái)的思路是給webview設(shè)置WebViewClient然后重寫onPageFinished方法蚁滋,在這個(gè)方法中獲取下一個(gè)需要換成的url宿接,然后再調(diào)用webview.loadurl()結(jié)果是很多頁(yè)面加載出來(lái)是空的。而且在Android4.4以上onPageFinished會(huì)調(diào)用兩次

這里寫圖片描述

于是乎辕录,我重寫了WebView的OnDraw()方法睦霎,在OnDraw()方法里設(shè)置了一個(gè)監(jiān)聽回調(diào),但是由于我的WebView是在Service中創(chuàng)建的所以ondraw方法根本不會(huì)調(diào)用走诞,但是這難得的我嗎副女?,呵呵蚣旱,于是我在service的onCreat方法中使用WindowManger將webview添加到屏幕碑幅,長(zhǎng)寬都是一個(gè)像素

   @Override
    public void onCreate() {
        super.onCreate();
        if (!haveInit) {
            throw new RuntimeException("請(qǐng)先調(diào)用init()方法戴陡,初始化OfflineReaderServer");
        }
        windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        params=new WindowManager.LayoutParams();
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        params.gravity = Gravity.LEFT | Gravity.TOP;
        params.width = 1;
        params.height = 1;
        initWebView();
        windowManager.addView(mWebView,params);
    }

結(jié)果還是很明顯的大部分的頁(yè)面都能緩存下來(lái),但是任然有部分頁(yè)面是空白的沟涨,后來(lái)發(fā)現(xiàn)webview的OnDraw()方法會(huì)多次持續(xù)恤批,webview的頁(yè)面加載時(shí)間隙的,于是參考這篇文章如何監(jiān)聽WebView顯示事件拷窜,我通過(guò)getContentHeight()判斷內(nèi)容高度來(lái)實(shí)現(xiàn)顯示完成的監(jiān)聽开皿,結(jié)果任然不理想。于是我最終版是這樣的

/**
 *  可以監(jiān)聽顯示完成的webview
 * Created by zhuguohui on 2016/6/24.
 */
public class LoadWebView extends WebView {
    private boolean isRendered = false;
    private static final int MSG_FINISH=1;
    private static final int MIN_CONTENT_HEIGHT=1000;
    public LoadWebView(Context context) {
        this(context, null);
    }
    public LoadWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    private int contentHeight=MIN_CONTENT_HEIGHT;
    Handler handler=new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(Message msg) {
            if(msg.what==MSG_FINISH) {
                if (finishListenter != null) {
                    finishListenter.onFinish();
                    contentHeight=MIN_CONTENT_HEIGHT;
                }
            }
        }
    };
    @Override
    protected void onDraw(Canvas canvas) {
        //與上一次的contentHeight比較篮昧,如果比上一次大赋荆,說(shuō)明還在加載
        if(getContentHeight()>=contentHeight){
            //更新contentHeight
            contentHeight=getContentHeight();
            //取消消息
            handler.removeMessages(MSG_FINISH);
            //延遲200ms發(fā)送,如果在200ms內(nèi)webview又加載了則這條消息會(huì)被取消懊昨,知道webview加載完成窄潭,
            //這條消息會(huì)被發(fā)送,所以每離線一個(gè)頁(yè)面有200ms的延遲酵颁,但是與功能相比這點(diǎn)是可以接受的嫉你。
            handler.sendEmptyMessageDelayed(MSG_FINISH,200);
        }
    }

    public interface OnLoadFinishListenter{
        void onFinish();
    }
    
    private OnLoadFinishListenter finishListenter;
    public void setFinishListenter(OnLoadFinishListenter listenter){
        finishListenter=listenter;
    }
}

3.進(jìn)度提示

為了讓用戶知道離線的進(jìn)度我抽取出了一個(gè)接口

/**
 * Created by zhuguohui on 2016/6/8.
 */
public interface OffLineProgressUI {
    void showProgress();

    void closeProgress();

    void updateProgress(int progress);

}

并默認(rèn)實(shí)現(xiàn)了一個(gè)水波紋的進(jìn)度球

這里寫圖片描述

設(shè)置進(jìn)度提示有兩種方式,一種是在初始化的時(shí)候設(shè)置

        OfflineReaderServer.init(this, getCacheDir(), new MyFirstLevel(),new WaterWaveProgressUI(this));

還有一種是調(diào)用OfflineReaderServer的setProgressUI方法

   public static void setProgressUI(@NonNull OffLineProgressUI progressUI) {
        sProgressUI = progressUI;
    }

更多內(nèi)容請(qǐng)看我的Demo躏惋。

Demo

https://github.com/zhuguohui/OffLineReaderDemo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末幽污,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子簿姨,更是在濱河造成了極大的恐慌距误,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扁位,死亡現(xiàn)場(chǎng)離奇詭異准潭,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)域仇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門刑然,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人暇务,你說(shuō)我怎么就攤上這事泼掠。” “怎么了般卑?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵武鲁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蝠检,道長(zhǎng)沐鼠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮饲梭,結(jié)果婚禮上乘盖,老公的妹妹穿的比我還像新娘。我一直安慰自己憔涉,他們只是感情好订框,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著兜叨,像睡著了一般穿扳。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上国旷,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天矛物,我揣著相機(jī)與錄音,去河邊找鬼跪但。 笑死履羞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屡久。 我是一名探鬼主播忆首,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼被环!你這毒婦竟也來(lái)了糙及?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤筛欢,失蹤者是張志新(化名)和其女友劉穎丁鹉,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悴能,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年雳灾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漠酿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谎亩,死狀恐怖炒嘲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匈庭,我是刑警寧澤夫凸,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站阱持,受9級(jí)特大地震影響夭拌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一鸽扁、第九天 我趴在偏房一處隱蔽的房頂上張望蒜绽。 院中可真熱鬧,春花似錦桶现、人聲如沸躲雅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)相赁。三九已至,卻和暖如春慰于,著一層夾襖步出監(jiān)牢的瞬間钮科,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工东囚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跺嗽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓页藻,卻偏偏與公主長(zhǎng)得像桨嫁,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子份帐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,782評(píng)論 25 707
  • WebView·開車指南 目錄 WebView簡(jiǎn)介 WebView基本使用 WebView常用方法 WebSett...
    小莊bb閱讀 3,493評(píng)論 3 25
  • Tips 由于WebView的用法實(shí)在太多璃吧,如果您只是想查詢某個(gè)功能的使用——建議Ctrl+F(Commad+F)...
    BugDev閱讀 7,739評(píng)論 11 109
  • WebView·開車指南 目錄 WebView簡(jiǎn)介 WebView基本使用 WebView常用方法 WebSett...
    南城的人閱讀 4,743評(píng)論 0 19
  • 最近總想寫點(diǎn)什么卻怎么也寫不出來(lái),之前每天日更的時(shí)候废境,一句話畜挨,一個(gè)靈感都可以讓我長(zhǎng)篇大論的寫一通,至少是能寫出來(lái)噩凹,...
    高藝菲Sophia閱讀 889評(píng)論 0 6