OSChina客戶端源碼學習(2)--緩存的設計

一、緩存的作用

請求數據的時候,首先進行判斷,是否可以從緩存中獲取數據,如果滿足條件,則直接從緩存中獲取數據。否則請求新的數據。這樣比沒有緩存的情況下,每次都要從服務器請求數據要快,而且,沒有網的情況下,也可以瀏覽已經緩存了的數據,極大的提高了用戶的體驗。

二、源碼解析

源碼中用到緩存的地方有:

1 請求數據之前,先判斷是否可以從緩存中獲取,如果滿足條件,則直接從緩存中讀取,否則,向服務器請求新的數據。

// 獲取緩存的目錄
 private String getCacheKey() {
       return new StringBuilder(getCacheKeyPrefix()).append("_")
                .append(mCurrentPage).toString();
}

// 請求數據,判斷是否可以從緩存中獲取數據
 protected void requestData(boolean refresh) {
        String key = getCacheKey();
        if (isReadCacheData(refresh)) {
            readCacheData(key);
        } else {
            // 取新的數據
            sendRequestData();
        }
    }


//判斷是否需要讀緩存數據
  protected boolean isReadCacheData(boolean refresh) {
        String key = getCacheKey();
        if (!TDevice.hasInternet()) {
            return true;
        }
        // 第一頁若不是主動刷新,且緩存存在,優先取緩存的
        if (CacheManager.isExistDataCache(getActivity(), key) && !refresh
                && mCurrentPage == 0) {
            return true;
        }
        // 其他頁數的,緩存存在以及還沒有失效,優先取緩存的
        if (CacheManager.isExistDataCache(getActivity(), key)
                && !CacheManager.isCacheDataFailure(getActivity(), key)
                && mCurrentPage != 0) {
            return true;
        }

        return false;
    }

2 對從服務器中獲取的最新數據進行保存到緩存的操作

private class SaveCacheTask extends AsyncTask<Void, Void, Void> {
        private final WeakReference<Context> mContext;
        private final Serializable seri;
        private final String key;

        private SaveCacheTask(Context context, Serializable seri, String key) {
            mContext = new WeakReference<Context>(context);
            this.seri = seri;
            this.key = key;
        }

        @Override
        protected Void doInBackground(Void... params) {
            CacheManager.saveObject(mContext.get(), seri, key);
            return null;
        }
    }

其中saveObject方法實現如下

public static boolean saveObject(Context context, Serializable ser, String file) {

    FileOutputStream fos = null;
    ObjectOutputStream oos = null;
    try {
        fos = context.openFileOutput(file, Context.MODE_PRIVATE);
        oos = new ObjectOutputStream(fos);
        oos.writeObject(ser);
        oos.flush();
        return true;

要注意的是
(1) data是ListEntity接口的對象,該接口繼承了Serializable接口,所以可以直接傳參。
(2) key 在這裏作爲文件路徑(緩存目錄)。

3 最後,值得一提的是(雖然和Cache沒有太大關係),解析完返回的數據後,刷新界面時,對返回數據的處理

 protected void executeOnLoadDataSuccess(List<T> data) {
  ...
        //對服務器返回來的數據和已有的數據進行對比,
        //如果相同,則將相同的item刪掉。最後看data集合的大小
        //如果data最後仍然有item ,則更新ListView

        for (int i = 0; i < data.size(); i++) {
            if (compareTo(mAdapter.getData(), data.get(i))) {
                data.remove(i);
                i--;
            }
        }
        int adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
        if ((mAdapter.getCount() + data.size()) == 0) {
            adapterState = ListBaseAdapter.STATE_EMPTY_ITEM;
        } else if (data.size() == 0
                || (data.size() < getPageSize() && mCurrentPage == 0)) {
            adapterState = ListBaseAdapter.STATE_NO_MORE;
            mAdapter.notifyDataSetChanged();
        } else {
            adapterState = ListBaseAdapter.STATE_LOAD_MORE;
        }
        mAdapter.setState(adapterState);
        mAdapter.addData(data);
        // 判斷等於1是因爲最後有一項是listview的狀態??
        if (mAdapter.getCount() == 1) {

            if (needShowEmptyNoData()) {
                mErrorLayout.setErrorType(EmptyLayout.NODATA);
            } else {
                mAdapter.setState(ListBaseAdapter.STATE_EMPTY_ITEM);
                mAdapter.notifyDataSetChanged();
            }
        }
    }

總結:
1 以上是自己初步從源碼中總結出來的一點關於緩存設計的認識和理解,肯定會有很多不周或者不準確的地方,希望能夠得到廣大博友的指點。自己也會盡量抽空完善的。
2 源碼中有些地方,現階段我也沒有完全看懂,但只要不影響大體上的把握,還是可以將自己的理解和整理的東西先分享出來。

作者:項昂之
時間:2015.7.15

發佈了34 篇原創文章 · 獲贊 5 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章