Android三级缓存是什么意思,三级缓存的含义和如何实战使用? | TeachCourse

一.摘要

Android项目开发经常需要使用到网络访问数据,将获取到的数据保存到本地,本地数据使用时写入到内存,再次访问的时候从内存获取数据,这就是平时说的三级缓存,三级缓存听起来很“高大上”,其实集合了网络访问数据/本地访问数据/缓存访问数据三个级别,按理说不是什么困难的事情,前提是你对数据操作的的三种方式都熟悉。

68709101_1.gif

二.网络访问数据

Android提供网络访问数据的类是:HttpURLConnection(最基础的访问方式),在实际开发中,我一直使用的第三方的开发框架:android-async-http-0.4.5.jar,它的特点是:1. UI线程中调用,异步执行;2.实现接口AsyncHttpResponseHandler回调方法;3.涉及的类AsyncHttpClient/RequestParams/AsyncHttpResponseHandler,简单的例子:

publicclassBaseAPI {

publicstaticString BASE_URL ='http://122.76.77.16:8080';

protectedstaticAsyncHttpClient client;

static{

client = newAsyncHttpClient();

}

/**

* 设置http请求超时时间,默认为60s

*

* @param timeOut

*/

protectedstaticvoidsetTimeOut(inttimeOut) {

client.setTimeout(timeOut);

}

publicstaticvoidgetLoginState(String name,String psw,AsyncHttpResponseHandler responseHandler) {

RequestParams params = newRequestParams();

params.put('name', name);

params.put('psw', psw);

client.post(BASE_URL, params, responseHandler);

}

}

三.本地访问数据

将数据保存到本地,可以文件流方式写入sdcard的文件中,也可以通过SharedPreferences方式保存键值对,SharedPreferences是一种比较简单的保存数据的方式,封装成了SharedPreferencesUtils类,更加详细的使用说明,可以参考《Android开发之数据存储的四种方式:SharedPreferences》,这里主要使用文件流的方式将新闻数据写入到sdcard的文件中。

封装FileManager工具类

封装HttURLConnection工具类

访问服务器,将新闻数据写入文件

FileManager工具类

FileManager工具类传入需要保存的文件路径/文件名和文件内容,开辟输出流FileOutputStream/输入流FileInputStream,写入到本地sdcard的文件中,涉及到File类的操作:1.创建多级目录使用mkdirs(),2.创建一级目录使用mkdir(),3.判断是否文件目录isDirectory(),更多使用说明可以参考官方文档:java.io.File。

publicclassFileManager {

privateContext context;

publicFileManager(Context context) {

this.context = context;

}

/**

* 存数据到sdcard

*

* @param filename

*            :文件名

* @param body

*            : 文件内容

*/

publicvoidsaveToSdcard(String filename, String body)throwsException {

/**

* 存数据到sdcar的实现步骤: 1. 先检查sdcard状态 2. 指定存放的路径及开辟输出流,用于存数据 3. 把数据写入文件中 4.

* 记得加权限

*

*/

if(Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)) {

File rootPath = context

.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);

File file = newFile(rootPath, filename);

// 开辟输出流

FileOutputStream fos = newFileOutputStream(file);

fos.write(body.getBytes());

fos.close();

Toast.makeText(context, '存入手机外部存储',0).show();

}else{

Toast.makeText(context, '请插入SDcard!',0).show();

}

}

/**

* 从手机sdcard读数据

*

* @param filename

*            :文件名

* @return : FileInputStream :文件输入流 位置

*/

publicString getDataFromSDCard(String filename)throwsException {

if(Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)) {

File rootPath = context

.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);

FileInputStream openFileInput = newFileInputStream(rootPath'/'

filename);

BufferedReader bufferedReader = newBufferedReader(

newInputStreamReader(openFileInput));

String body = bufferedReader.readLine();// 取得一行

openFileInput.close();

returnbody;

}

returnnull;

}

}

HttpURLConnection工具类

流分为:字节流/字符流/文件流/数组流/缓冲流等,字节流是流操作的最小单位,字符流以字符为单位,文件流是特定对文件操作的一种流,对于其他的流操作也是字节流/字符流的直接或间接子类,比如:DataInputStream/DataOutputStream是InputStream/OutputStream的子类,操作方法是对底层流的“包装”,代码如下:

InputStream is=newInputSteam();

DataInputStream dis=newDataInputStream(is);

OutputStream os=newOutputStream();

DataOutputStream dos=newDataOutputStream(os);

/*

@author postmaster@teachcourse.cn

@date 创建于:2016-3-27

*/

publicclassHttpURLConn {

privatestaticHttpURLConnection mConnet;

/**

* 单例模式创建HttpURLConnection

*

* @return 返回HttpURLConnection实例

*/

publicstaticHttpURLConnection newIntance(String url) {

if(mConnet ==null) {

try{

mConnet = (HttpURLConnection) newURL(url).openConnection();

} catch(MalformedURLException e) {

e.printStackTrace();

} catch(IOException e) {

e.printStackTrace();

}

}

returnmConnet;

}

/**

* 访问数据

*

* @return 返回请求的数据

*/

publicString get() {

try{

mConnet.setRequestMethod('get');//设置请求的方式get

InputStream is = mConnet.getInputStream();

DataInputStream dis = newDataInputStream(is);

byte[] bytes =newbyte[1024];// 指定每次读取字节数

intcount =0;// 记录每次读取的位置

StringBuffer sb = newStringBuffer();// 保存每次字符

String str = null;

while((count = dis.read(bytes)) != -1) {

str = newString(bytes,0, count);

sb.append(str);

}

returnsb.toString();

} catch(IOException e) {

e.printStackTrace();

}

returnnull;

}

}

MainActivity获取数据

调用HttpURLConn中的get方法访问服务器,获取返回的json数据,然后o把json写入本地sdcard文件,再从sdcard的文件中读取数据在ListView中展示,具体代码如下:

publicclassMainActivityextendsActivity {

privatestaticfinalString TAG ='MainActivity';

privateListView mListView;// 展示新闻的ListView列表

privateList mList =newArrayList();// 新闻实体

privateNewsBaseAdapter mAdapter =null;// 新闻适配器

/**

* 调用FileManager工具类保存数到sdcard

*/

privateFileManager manager =newFileManager(MainActivity.this);

privatestaticfinalintREFRESH_LISTVIEW =0x110;

@Override

protectedvoidonCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mListView = (ListView) findViewById(R.id.news_listview);

/**

* 获取服务器返回的json数据

*/

String url[] = { 'http://120.76.76.16:8080/smartpg-1.0/app/cms/listByCode?code=redian&rows=10'};

newLoadJsonTask().execute(url);

}

@Override

protectedvoidonResume() {

super.onResume();

/**

* 从sdcard文件读取新闻数据

*/

try{

String json = manager.getDataFromSDCard('json.der');

JSONObject obj = newJSONObject(json);

String data = obj.getString('data');

mList = resolveJson(data);

Log.d(TAG, '取出json= 'data);

} catch(Exception e) {

e.printStackTrace();

}

}

@Override

publicbooleanonCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.main, menu);

returntrue;

}

@Override

publicbooleanonOptionsItemSelected(MenuItem item) {

intid = item.getItemId();

if(id == R.id.action_settings) {

returntrue;

}

returnsuper.onOptionsItemSelected(item);

}

privateclassLoadJsonTaskextendsAsyncTask {

@Override

protectedString doInBackground(String... params) {

String json = HttpURLConn.newIntance(params[0]).get();

if(json ==null) {

return'获取不到数据';

}

returnjson;

}

@Override

protectedvoidonPostExecute(String result) {

super.onPostExecute(result);

Log.d(TAG, 'result= 'result);

/**

* 保存新闻数据到sdcard

*/

try{

manager.saveToSdcard('json.der', result);

} catch(Exception e) {

e.printStackTrace();

}

}

}

privateList resolveJson(String json) {

List mList = newArrayList();

try{

JSONArray mArray = newJSONArray(json);

inti =0;

intlength = mArray.length();

while(i 

JSONObject obj = mArray.getJSONObject(i);

String title = obj.getString('title');

String description = obj.getString('description');

String images = obj.getString('imgUrl');

NewsBean bean = newNewsBean(title, description, images);

mList.add(bean);

i ;

}

mHandler.sendEmptyMessage(REFRESH_LISTVIEW);

returnmList;

} catch(Exception e) {

e.printStackTrace();

}

returnnull;

}

Handler mHandler = newHandler() {

@Override

publicvoidhandleMessage(Message msg) {

// TODO Auto-generated method stub

super.handleMessage(msg);

switch(msg.what) {

caseREFRESH_LISTVIEW:

/**

* 刷新新闻列表

*/

mAdapter = newNewsBaseAdapter(mList, MainActivity.this);

mListView.setAdapter(mAdapter);

break;

default:

break;

}

}

};

}

返回的json类型格式(只罗列一条新闻数据)如下:

{

'code': 'success',

'result': '成功',

'data': [

{

'id': 'b78e95f8bb974955aa704496d8a577b2',

'isNewRecord': false,

'createDate': '2016-01-06 17:31:40',

'updateDate': '2016-03-15 15:19:06',

'title': '并希望项目负责人要积极抢抓项目施工的晴好天气',

'link': 'http://120.76.76.16:8080/smartpg-1.0/f/app/cdc4b9b87ac74a5a85806f48cc208890/b78e95f8bb974955aa704496d8a577b2.html',

'color': '',

'keywords': '',

'description': '并希望项目负责人要积极抢抓项目施工的晴好天气,加大施工机械和人力组织,确保项目早日建成达产',

'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格式数据使用JSONArray和JSONObject,数学将:{}称为大括号,将:[]称为中括号,在返回的JSON字符串中,大括号使用JSONObject转换成对象,中括号使用JSONArray转换成对象,例如对面的字符串json,转换代码如下:

JSONObjectobj=newJSONObject(json);

String data=obj.getString('data');//取出data嵌套的json数组

mList=resolveJson(data);

四.内存读写数据

内存读写数据的位置在:/data/data/

/file,相对本地文件存储/网络存储,内存存储数据的读写速度是最快的,在Android开发中,能够做到三级缓存的APP,使用起来更加顺畅,因为内存保存数据的位置与当前的包名相关,所以需要Context的openFileInput()/openFileOutput()方法获取输入/输出流,而sdcard读取数据使用的是FileInputStream/FileOutputStream类获取输入/输出流,这是他们两者之间的区别。具体代码,如下:

/**

* 存数据到内存

*

* @param filename

*            :文件名

* @param body

*            :文件内容

*/

public void saveToPhone(String filename, String body) throws Exception {

/**

* 开辟一个输出流 filename: 文件名 ,有则打开,无则创建 Mode:文件的操作模式 : private: 私有的覆盖模式 (默认)

* 、append : 私有的追加模式 return: FileOutputStream

* 文件的路径:/data/data//file

*

*/

FileOutputStream openFileOutput=context.openFileOutput(filename,

Context.MODE_APPEND);

openFileOutput.write(body.getBytes());// 写字符串到文件输出流

openFileOutput.close();// 关闭流

}

/**

* 从内存读数据

*

* @param filename

*            :文件名

* @return : FileInputStream :文件输入流 位置:/data/data//file

*/

public String getDataFromPhone(String filename) throws Exception {

FileInputStream openFileInput=context.openFileInput(filename);

BufferedReader bufferedReader=newBufferedReader(

new InputStreamReader(openFileInput));

String body=bufferedReader.readLine();// 取得一行

openFileInput.close();

return body;

}

可以将上面新闻中的数据同时保存到内存/sdcard,当启动APP时,首先从内存读取,如果内存的数据不存在,再从sdcard中读取,最后

从网络加载,这是三级缓存的开发思路,结合上面的Demo,完成新闻列表的展示。

五.下一篇文章将介绍《如何读写sqlite数据库中的新闻数据》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值