標籤:android 緩衝
離線緩衝就是在網路暢通的情況下將從伺服器收到的資料儲存到本地,當網路斷開之後直接讀取本地檔案中的資料。
將網路資料儲存到本地:
你可以自己寫一個儲存資料成本地檔案的方法,儲存在android系統的任意目錄(當然是有許可權的才行),但是在這種情況下使用Context的openFileOutput方法最簡便也最符合我們的情境,下面的saveObject方法示範了如何用openFileOutput將資料儲存在本地的一個檔案中:
saveObject
public static boolean saveObject(Serializable ser, String file) { FileOutputStream fos = null; ObjectOutputStream oos = null; try { fos = AppContext.getInstance().openFileOutput(file, AppContext.getInstance().MODE_PRIVATE); oos = new ObjectOutputStream(fos); oos.writeObject(ser); oos.flush(); return true; } catch (Exception e) { e.printStackTrace(); return false; } finally { try { oos.close(); } catch (Exception e) { } try { fos.close(); } catch (Exception e) { } }}
openFileOutput可以直接獲得一個和應用關聯的檔案路徑(在/data/data/<package name>/files下面),然後使用java io中的ObjectOutputStream將序列化的對象寫入(writeObject)到得到的檔案中,你可以看到上面的實現過程有兩個關鍵方法:openFileOutput、writeObject以及調用它們的兩個關鍵對象Context和ObjectOutputStream。關於序列化可以參看這篇文章:Java對象的序列化和還原序列化實踐
這是將一個序列化的對象儲存在本地,跟我們的離線緩衝儲存網路資料有什麼關係呢?
有關係,因為網上擷取的資料大多可以轉換成String類型的字串,現在服務端返回的資料一般是json格式的字串。而String類型的字串其實就是可序列化的對象。下面是一個伺服器返回json資料的例子(其實就是jcodecraeer網站的一個移動用戶端升級介面):
{"url":"http://jcodecraeer.com/uploads/soft/android/CodeBox.apk","versionCode":"7","updateMessage":"增加離線緩衝,分類篩選功能修正了版本相容性問題 "}
用上面的saveObject方法我們可以將資料儲存在本地,為了能夠取出這個檔案我們必須想好如何為這個儲存的檔案命名,如果是單純的一篇文章的資料,我們可以直接將檔案名稱命名為這篇文章的id,因為id是唯一的,為了儘可能的不和其他資料發生衝突,你還可以在這個id之前加一個首碼,比如這篇文章是java欄目下的我們可以這樣 arc_java_id。如果是文章列表我們可以這樣命名:文章類別_分頁頁碼,總之命名的原則是能和其他離線資料區別,有唯一性。為什麼不用url作為檔案名稱呢?url肯定是唯一的,但是url不一定符合檔案的命名規範。
下面來講解如何讀取本機快取的資料
讀取緩衝的時候我們只需要知道檔案名稱就可以了,下面的readObject方法實現了根據檔案名稱讀取快取資料。其實很多東西是和上面儲存資料對應的。
readObject
/** * 讀取對象 * * @param file * @return * @throws IOException */public static Serializable readObject(String file) { FileInputStream fis = null; ObjectInputStream ois = null; try { fis = AppContext.getInstance().openFileInput(file); ois = new ObjectInputStream(fis); return (Serializable) ois.readObject(); } catch (FileNotFoundException e) { } catch (Exception e) { e.printStackTrace(); } finally { try { ois.close(); } catch (Exception e) { } try { fis.close(); } catch (Exception e) { } } return null;}
運用
下面的代碼示範了如何用上面的知識儲存和讀取網路資料
String key = "codelist_" + mCategory.getValue() + "_" + + page ;String result = "";//cacheif (HttpUtil.isNetworkConnected()) { result = HttpUtil.http_get(AppContext.getInstance(), url ); HttpUtil.saveObject(result, key); result = (String) HttpUtil.readObject(key);} else { result = (String) HttpUtil.readObject(key); if (result == null) result = "erro";}
當網路暢通時,從伺服器擷取資料( HttpUtil.http_get(AppContext.getInstance(), url )),同時將資料儲存到本地(HttpUtil.saveObject),而當網路不可用時,直接從本地讀取緩衝的資料,不跟伺服器發生互動。
其中HttpUtil是跟網路相關的工具類,這裡涉及到它的三個方法:
isNetworkConnected()判斷網路是否可用saveObject上面已經給出了實現readObject上面已經給出了實現http_get讀取指定url的伺服器資料
而AppContext.getInstance()是我自己寫的,是為了方便在HttpUtil的靜態方法中獲得Context對象。
這裡的key就是檔案名稱。
額外的需求
有時候我們還有這樣的需求,當使用者在指定間隔時間內讀取同一資料來源時,從本地擷取,超過這個時間間隔從網路擷取,這樣做的目的是節省使用者的流量,同時也避免了每次從網路擷取資料造成的介面延遲。
下面實現了如何根據時間間隔判斷是否需要重新整理伺服器資料,true表示不需要,false表示需要(很彆扭是吧,這跟isCacheDataFailure這個命名有關係):
public static boolean isCacheDataFailure(String cachefile) { boolean failure = false; File data = AppContext.getInstance().getFileStreamPath(cachefile); if (data.exists() && (System.currentTimeMillis() - data.lastModified()) > CACHE_TIME) failure = true; else if (!data.exists()) failure = true; return failure;}
將目前時間和檔案的修改時間做比較 ,CACHE_TIME是一個固定值(毫秒),你可以替換成任意int類型。
將這個判斷條件加入,然後上面的代碼改成:
String key = "codelist_" + mCategory.getValue() + "_" + + page ;String result = "";//cacheif (HttpUtil.isNetworkConnected() && HttpUtil.isCacheDataFailure(key)) { result = HttpUtil.http_get(AppContext.getInstance(), url ); HttpUtil.saveObject(result, key); result = (String) HttpUtil.readObject(key);} else { result = (String) HttpUtil.readObject(key); if (result == null) result = "erro";}
完善
上面的步驟對於一般應用來說已經夠用了,但是在要求比較高的情況下,我們還得考慮隨著時間的流逝,快取資料會越來越多,因此我們需要增加刪除到期緩衝的功能,原理就是設定一個閥值,在儲存緩衝的時候,判斷當前緩衝的總量是否大於閥值,如果是則刪除時間較早的緩衝。
這個實現起來有點複雜,可以考慮更簡單的方案,定期檢查(或者使用者每開啟一次程式)緩衝總量,當大於閥值,提示使用者主動刪除。具體實現就不多說了。
註:openFileOutput()方法的第一參數用於指定檔案名稱,不能包含路徑分隔字元“/” ,如果檔案不存在,Android 會自動建立它。建立的檔案儲存在/data/data/<package name>/files目錄,如: /data/data/cn.itcast.action/files/itcast.txt ,通過點擊Eclipse菜單“Window”-“Show View”-“Other”,在交談視窗中展開android檔案夾,選擇下面的File Explorer視圖,然後在File Explorer視圖中展開/data/data/<package name>/files目錄就可以看到該檔案。
openFileOutput()方法的第二參數用於指定操作模式,有四種模式,分別為: Context.MODE_PRIVATE = 0
Context.MODE_APPEND = 32768
Context.MODE_WORLD_READABLE = 1
Context.MODE_WORLD_WRITEABLE = 2
Context.MODE_PRIVATE:為預設操作模式,代表該檔案是私人資料,只能被應用本身訪問,在該模式下,寫入的內容會覆蓋原檔案的內容,如果想把新寫入的內容追加到原檔案中。可以使用Context.MODE_APPEND
Context.MODE_APPEND:模式會檢查檔案是否存在,存在就往檔案追加內容,否則就建立新檔案。
Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE用來控制其他應用是否有許可權讀寫該檔案。
MODE_WORLD_READABLE:表示當前檔案可以被其他應用讀取;MODE_WORLD_WRITEABLE:表示當前檔案可以被其他應用寫入。
如果希望檔案被其他應用讀和寫,可以傳入:
openFileOutput(“itcast.txt”, Context.MODE_WORLD_READABLE + Context.MODE_WORLD_WRITEABLE)
本文來源於我們的網站上 android中如何?離線緩衝 一文。
android中如何?離線緩衝