一.摘要
Android項目開發(fā)經(jīng)常需要使用到網(wǎng)絡(luò)訪問數(shù)據(jù),將獲取到的數(shù)據(jù)保存到本地,本地數(shù)據(jù)使用時寫入到內(nèi)存,再次訪問的時候從內(nèi)存獲取數(shù)據(jù),這就是平時說的三級緩存,三級緩存聽起來很“高大上”,其實集合了網(wǎng)絡(luò)訪問數(shù)據(jù)/本地訪問數(shù)據(jù)/緩存訪問數(shù)據(jù)三個級別,按理說不是什么困難的事情,前提是你對數(shù)據(jù)操作的的三種方式都熟悉。

二.網(wǎng)絡(luò)訪問數(shù)據(jù)
Android提供網(wǎng)絡(luò)訪問數(shù)據(jù)的類是:HttpURLConnection(最基礎(chǔ)的訪問方式),在實際開發(fā)中,我一直使用的第三方的開發(fā)框架:android-async-http-0.4.5.jar,它的特點是:1. UI線程中調(diào)用,異步執(zhí)行;2.實現(xiàn)接口AsyncHttpResponseHandler回調(diào)方法;3.涉及的類AsyncHttpClient/RequestParams/AsyncHttpResponseHandler,簡單的例子:
- public class BaseAPI {
-
- public static String BASE_URL = 'http://122.76.77.16:8080';
- protected static AsyncHttpClient client;
-
- static {
- client = new AsyncHttpClient();
- }
- /**
- * 設(shè)置http請求超時時間,默認為60s
- *
- * @param timeOut
- */
- protected static void setTimeOut(int timeOut) {
- client.setTimeout(timeOut);
- }
-
- public static void getLoginState(String name,String psw,AsyncHttpResponseHandler responseHandler) {
- RequestParams params = new RequestParams();
- params.put('name', name);
- params.put('psw', psw);
- client.post(BASE_URL, params, responseHandler);
- }
- }
更加詳細的使用說明,可以參考《Android開發(fā)之?dāng)?shù)據(jù)存儲的四種方式之一:Network存儲》
三.本地訪問數(shù)據(jù)
將數(shù)據(jù)保存到本地,可以文件流方式寫入sdcard的文件中,也可以通過SharedPreferences方式保存鍵值對,SharedPreferences是一種比較簡單的保存數(shù)據(jù)的方式,封裝成了SharedPreferencesUtils類,更加詳細的使用說明,可以參考《Android開發(fā)之?dāng)?shù)據(jù)存儲的四種方式:SharedPreferences》,這里主要使用文件流的方式將新聞數(shù)據(jù)寫入到sdcard的文件中。
- 封裝FileManager工具類
- 封裝HttURLConnection工具類
- 訪問服務(wù)器,將新聞數(shù)據(jù)寫入文件
FileManager工具類
FileManager工具類傳入需要保存的文件路徑/文件名和文件內(nèi)容,開辟輸出流FileOutputStream/輸入流FileInputStream,寫入到本地sdcard的文件中,涉及到File類的操作:1.創(chuàng)建多級目錄使用mkdirs(),2.創(chuàng)建一級目錄使用mkdir(),3.判斷是否文件目錄isDirectory(),更多使用說明可以參考官方文檔:java.io.File。
- public class FileManager {
- private Context context;
-
- public FileManager(Context context) {
- this.context = context;
- }
- /**
- * 存數(shù)據(jù)到sdcard
- *
- * @param filename
- * :文件名
- * @param body
- * : 文件內(nèi)容
- */
- public void saveToSdcard(String filename, String body) throws Exception {
- /**
- * 存數(shù)據(jù)到sdcar的實現(xiàn)步驟: 1. 先檢查sdcard狀態(tài) 2. 指定存放的路徑及開辟輸出流,用于存數(shù)據(jù) 3. 把數(shù)據(jù)寫入文件中 4.
- * 記得加權(quán)限
- *
- */
- if (Environment.getExternalStorageState().equals(
- Environment.MEDIA_MOUNTED)) {
- File rootPath = context
- .getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
- File file = new File(rootPath, filename);
-
- // 開辟輸出流
- FileOutputStream fos = new FileOutputStream(file);
- fos.write(body.getBytes());
- fos.close();
- Toast.makeText(context, '存入手機外部存儲', 0).show();
-
- }else{
- Toast.makeText(context, '請插入SDcard!', 0).show();
- }
-
- }
-
- /**
- * 從手機sdcard讀數(shù)據(jù)
- *
- * @param filename
- * :文件名
- * @return : FileInputStream :文件輸入流 位置
- */
- public String getDataFromSDCard(String filename) throws Exception {
- if (Environment.getExternalStorageState().equals(
- Environment.MEDIA_MOUNTED)) {
- File rootPath = context
- .getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
-
- FileInputStream openFileInput = new FileInputStream(rootPath '/'
- filename);
- BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(openFileInput));
- String body = bufferedReader.readLine();// 取得一行
- openFileInput.close();
- return body;
- }
- return null;
- }
-
- }
HttpURLConnection工具類
流分為:字節(jié)流/字符流/文件流/數(shù)組流/緩沖流等,字節(jié)流是流操作的最小單位,字符流以字符為單位,文件流是特定對文件操作的一種流,對于其他的流操作也是字節(jié)流/字符流的直接或間接子類,比如:DataInputStream/DataOutputStream是InputStream/OutputStream的子類,操作方法是對底層流的“包裝”,代碼如下:
- InputStream is=new InputSteam();
- DataInputStream dis=new DataInputStream(is);
- OutputStream os=new OutputStream();
- DataOutputStream dos=new DataOutputStream(os);
- /*
- @author postmaster@
- @date 創(chuàng)建于:2016-3-27
- */
- public class HttpURLConn {
- private static HttpURLConnection mConnet;
-
- /**
- * 單例模式創(chuàng)建HttpURLConnection
- *
- * @return 返回HttpURLConnection實例
- */
- public static HttpURLConnection newIntance(String url) {
- if (mConnet == null) {
- try {
- mConnet = (HttpURLConnection) new URL(url).openConnection();
- } catch (MalformedURLException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- return mConnet;
- }
-
- /**
- * 訪問數(shù)據(jù)
- *
- * @return 返回請求的數(shù)據(jù)
- */
- public String get() {
- try {
- mConnet.setRequestMethod('get');//設(shè)置請求的方式get
- InputStream is = mConnet.getInputStream();
- DataInputStream dis = new DataInputStream(is);
- byte[] bytes = new byte[1024];// 指定每次讀取字節(jié)數(shù)
- int count = 0;// 記錄每次讀取的位置
- StringBuffer sb = new StringBuffer();// 保存每次字符
- String str = null;
- while ((count = dis.read(bytes)) != -1) {
- str = new String(bytes, 0, count);
- sb.append(str);
- }
- return sb.toString();
- } catch (IOException e) {
-
- e.printStackTrace();
- }
- return null;
-
- }
- }
MainActivity獲取數(shù)據(jù)
調(diào)用HttpURLConn中的get方法訪問服務(wù)器,獲取返回的json數(shù)據(jù),然后o把json寫入本地sdcard文件,再從sdcard的文件中讀取數(shù)據(jù)在ListView中展示,具體代碼如下:
返回的json類型格式(只羅列一條新聞數(shù)據(jù))如下:
- {
- 'code': 'success',
- 'result': '成功',
- 'data': [
- {
- 'id': 'b78e95f8bb974955aa704496d8a577b2',
- 'isNewRecord': false,
- 'createDate': '2016-01-06 17:31:40',
- 'updateDate': '2016-03-15 15:19:06',
- 'title': '并希望項目負責(zé)人要積極搶抓項目施工的晴好天氣',
- 'link': 'http://120.76.76.16:8080/smartpg-1.0/f/app/cdc4b9b87ac74a5a85806f48cc208890/b78e95f8bb974955aa704496d8a577b2.html',
- 'color': '',
- 'keywords': '',
- 'description': '并希望項目負責(zé)人要積極搶抓項目施工的晴好天氣,加大施工機械和人力組織,確保項目早日建成達產(chǎn)',
- 'weight': 0,
- 'hits': 13,
- 'posid': ',1,',
- 'categoryId': 'cdc4b9b87ac74a5a85806f48cc208890',
- 'imgUrl': 'http://120.76.76.16:8080/smartpg-1.0/userfiles/1/_thumbs/images/cms/article/2016/03/112342amokqmsmpnskfnkm.jpg',
- 'images': [
- 'http://120.76.76.16:8080/smartpg-1.0/userfiles/1/_thumbs/images/cms/article/2016/03/1f1ce870bca48ba3a106b19d1cbce4bc.jpg'
- ],
- 'offset': 106
- }
- ]
- }
解析JSON格式數(shù)據(jù)使用JSONArray和JSONObject,數(shù)學(xué)將:{}稱為大括號,將:[]稱為中括號,在返回的JSON字符串中,大括號使用JSONObject轉(zhuǎn)換成對象,中括號使用JSONArray轉(zhuǎn)換成對象,例如對面的字符串json,轉(zhuǎn)換代碼如下:
- JSONObject obj = new JSONObject(json);
- String data = obj.getString('data');//取出data嵌套的json數(shù)組
- mList = resolveJson(data);
四.內(nèi)存讀寫數(shù)據(jù)
內(nèi)存讀寫數(shù)據(jù)的位置在:/data/data/
/file,相對本地文件存儲/網(wǎng)絡(luò)存儲,內(nèi)存存儲數(shù)據(jù)的讀寫速度是最快的,在Android開發(fā)中,能夠做到三級緩存的APP,使用起來更加順暢,因為內(nèi)存保存數(shù)據(jù)的位置與當(dāng)前的包名相關(guān),所以需要Context的openFileInput()/openFileOutput()方法獲取輸入/輸出流,而sdcard讀取數(shù)據(jù)使用的是FileInputStream/FileOutputStream類獲取輸入/輸出流,這是他們兩者之間的區(qū)別。具體代碼,如下:
- /**
- * 存數(shù)據(jù)到內(nèi)存
- *
- * @param filename
- * :文件名
- * @param body
- * :文件內(nèi)容
- */
- public void saveToPhone(String filename, String body) throws Exception {
- /**
- * 開辟一個輸出流 filename: 文件名 ,有則打開,無則創(chuàng)建 Mode:文件的操作模式 : private: 私有的覆蓋模式 (默認)
- * 、append : 私有的追加模式 return: FileOutputStream
- * 文件的路徑:/data/data/<pachagename>/file
- *
- */
- FileOutputStream openFileOutput = context.openFileOutput(filename,
- Context.MODE_APPEND);
- openFileOutput.write(body.getBytes());// 寫字符串到文件輸出流
- openFileOutput.close();// 關(guān)閉流
- }
-
- /**
- * 從內(nèi)存讀數(shù)據(jù)
- *
- * @param filename
- * :文件名
- * @return : FileInputStream :文件輸入流 位置:/data/data/<pachagename>/file
- */
- public String getDataFromPhone(String filename) throws Exception {
-
- FileInputStream openFileInput = context.openFileInput(filename);
- BufferedReader bufferedReader = new BufferedReader(
- new InputStreamReader(openFileInput));
- String body = bufferedReader.readLine();// 取得一行
- openFileInput.close();
- return body;
- }
可以將上面新聞中的數(shù)據(jù)同時保存到內(nèi)存/sdcard,當(dāng)啟動APP時,首先從內(nèi)存讀取,如果內(nèi)存的數(shù)據(jù)不存在,再從sdcard中讀取,最后
從網(wǎng)絡(luò)加載,這是三級緩存的開發(fā)思路,結(jié)合上面的Demo,完成新聞列表的展示。
五.下一篇文章將介紹《如何讀寫sqlite數(shù)據(jù)庫中的新聞數(shù)據(jù)》
|