android實現離線緩衝
在這個無法缺失互連網的世界裡[android離線緩衝][6]將解決在沒有互連網的環境中APP的使用,目前使用這項技術的有網易新聞,神馬新聞,主要的優點是: 實現離線瀏覽,在離線狀態下,瀏覽新聞(也可以用H5實現) 減輕伺服器壓力,降低伺服器的並發性操作 提高用戶端的響應速度
其實現的方式主要有: 儲存到本地檔案儲存體 儲存到SQLite資料庫儲存 儲存到本地檔案儲存體
將網路資料儲存到本地:首先需要寫一個將網路資料儲存到本地的方法。
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//files下面),然後使用java io中的ObjectOutputStream將序列化的對象寫入(writeObject)到得到的檔案中,你可以看到上面的實現過程有兩個關鍵方法:openFileOutput、writeObject以及調用它們的兩個關鍵對象Context和ObjectOutputStream。
這是將一個序列化的對象儲存在本地,跟我們的離線緩衝儲存網路資料有什麼關係呢。
有關係,因為網上擷取的資料大多可以轉換成String類型的字串,現在服務端返回的資料一般是json格式的字串。
用上面的saveObject方法我們可以將資料儲存在本地,為了能夠取出這個檔案我們必須想好如何為這個儲存的檔案命名,如果是單純的一篇文章的資料,我們可以直接將檔案名稱命名為這篇文章的id,因為id是唯一的,為了儘可能的不和其他資料發生衝突,你還可以在這個id之前加一個首碼,比如這篇文章是java欄目下的我們可以這樣 arc_java_id。如果是文章列表我們可以這樣命名:文章類別_分頁頁碼,總之命名的原則是能和其他離線資料區別,有唯一性。為什麼不用url作為檔案名稱呢。url肯定是唯一的,但是url不一定符合檔案的命名規範。
讀取本地的快取資料:
讀取緩衝的時候根據檔案名稱讀取緩衝,下面的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),而當網路不可用時,直接從本地讀取緩衝的資料,不跟伺服器發生互動。 拓展:
有時候我們還有這樣的需求,當使用者在指定間隔時間內讀取同一資料來源時,從本地擷取,超過這個時間間隔從網路擷取,這樣做的目的是節省使用者的流量,同時也避免了每次從網路擷取資料造成的介面延遲。
下面實現了如何根據時間間隔判斷是否需要重新整理伺服器資料,true表示不需要,false表示需要
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;}
完善功能:
上面的步驟對於一般應用來說已經夠用了,但是在要求比較高的情況下,我們還得考慮隨著時間的流逝,快取資料會越來越多,因此我們需要增加刪除到期緩衝的功能,原理就是設定一個閥值,在儲存緩衝的時候,判斷當前緩衝的總量是否大於閥值,如果是則刪除時間較早的緩衝,前提是你儲存了時間屬性。
這個實現起來有點複雜,可以考慮更簡單的方案,定期檢查(或者使用者每開啟一次程式)緩衝總量,當大於閥值,提示使用者主動刪除。 儲存到SQlite資料庫儲存
實現方法:這種方法是在下載完資料檔案後,把檔案的相關資訊如url,路徑,下載時間,到期時間等存放到資料庫,當然我個人建議把url作為唯一的標識。下次下載的時候根據url先從資料庫中查詢,如果查詢到目前時間並未到期,就根據路徑讀取本地檔案,從而實現緩衝的效果。
優點:這種方法可以靈活存放檔案的屬性,進而提供了很大的擴充性,可以為其它的功能提供一定的支援。
缺點:從操作上需要建立資料庫,每次查詢資料庫,如果到期還需要更新資料庫,清理緩衝的時候還需要刪除資料庫資料,稍顯麻煩,而資料庫操作不當又容易出現一系列的效能,ANR問題,指標錯誤問題,實現的時候要謹慎,具體實現的話,但也只是增加一個工具類或方法的事情。同時,緩衝的資料庫是存放在/data/data//databases/目錄下,是佔用記憶體空間的,如果緩衝累計,容易浪費記憶體,需要及時清理緩衝。
有利有弊,開發人員根據使用者自己的操作習慣和使用情境選擇對應的方式。