目录
一.摘要
Android项目开发经常需要使用到网络访问数据,将获取到的数据保存到本地,本地数据使用时写入到内存,再次访问的时候从内存获取数据,这就是平时说的三级缓存,三级缓存听起来很“高大上”,其实集合了网络访问数据/本地访问数据/缓存访问数据三个级别,按理说不是什么困难的事情,前提是你对数据操作的的三种方式都熟悉。
二.网络访问数据
Android提供网络访问数据的类是:HttpURLConnection(最基础的访问方式),在实际开发中,我一直使用的第三方的开发框架:android-async-http-0.4.5.jar,它的特点是:1. UI线程中调用,异步执行;2.实现接口AsyncHttpResponseHandler回调方法;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();
- }
- /**
- * 设置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开发之数据存储的四种方式之一:Network存储》
三.本地访问数据
将数据保存到本地,可以文件流方式写入sdcard的文件中,也可以通过SharedPreferences方式保存键值对,SharedPreferences是一种比较简单的保存数据的方式,封装成了SharedPreferencesUtils类,更加详细的使用说明,可以参考《Android开发之数据存储的四种方式:SharedPreferences》,这里主要使用文件流的方式将新闻数据写入到sdcard的文件中。
- 封装FileManager工具类
- 封装HttURLConnection工具类
- 访问服务器,将新闻数据写入文件
FileManager工具类
FileManager工具类传入需要保存的文件路径/文件名和文件内容,开辟输出流FileOutputStream/输入流FileInputStream,写入到本地sdcard的文件中,涉及到File类的操作:1.创建多级目录使用mkdirs(),2.创建一级目录使用mkdir(),3.判断是否文件目录isDirectory(),更多使用说明可以参考官方文档:java.io.File。
- public class FileManager {
- private Context context;
- public FileManager(Context context) {
- this.context = context;
- }
- /**
- * 存数据到sdcard
- *
- * @param filename
- * :文件名
- * @param body
- * : 文件内容
- */
- public void saveToSdcard(String filename, String body) throws Exception {
- /**
- * 存数据到sdcar的实现步骤: 1. 先检查sdcard状态 2. 指定存放的路径及开辟输出流,用于存数据 3. 把数据写入文件中 4.
- * 记得加权限
- *
- */
- 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读数据
- *
- * @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工具类
流分为:字节流/字符流/文件流/数组流/缓冲流等,字节流是流操作的最小单位,字符流以字符为单位,文件流是特定对文件操作的一种流,对于其他的流操作也是字节流/字符流的直接或间接子类,比如:DataInputStream/DataOutputStream是InputStream/OutputStream的子类,操作方法是对底层流的“包装”,代码如下:
- InputStream is=new InputSteam();
- DataInputStream dis=new DataInputStream(is);
- OutputStream os=new OutputStream();
- DataOutputStream dos=new DataOutputStream(os);
- /*
- @author postmaster@teachcourse.cn
- @date 创建于:2016-3-27
- */
- public class HttpURLConn {
- private static HttpURLConnection mConnet;
- /**
- * 单例模式创建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;
- }
- /**
- * 访问数据
- *
- * @return 返回请求的数据
- */
- public String get() {
- try {
- mConnet.setRequestMethod("get");//设置请求的方式get
- InputStream is = mConnet.getInputStream();
- DataInputStream dis = new DataInputStream(is);
- byte[] bytes = new byte[1024];// 指定每次读取字节数
- 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获取数据
调用HttpURLConn中的get方法访问服务器,获取返回的json数据,然后o把json写入本地sdcard文件,再从sdcard的文件中读取数据在ListView中展示,具体代码如下:
- public class MainActivity extends Activity {
- private static final String TAG = "MainActivity";
- private ListView mListView;// 展示新闻的ListView列表
- private List<NewsBean> mList = new ArrayList<NewsBean>();// 新闻实体
- private NewsBaseAdapter mAdapter = null;// 新闻适配器
- /**
- * 调用FileManager工具类保存数到sdcard
- */
- private FileManager manager = new FileManager(MainActivity.this);
- private static final int REFRESH_LISTVIEW = 0x110;
- @Override
- protected void onCreate(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" };
- new LoadJsonTask().execute(url);
- }
- @Override
- protected void onResume() {
- super.onResume();
- /**
- * 从sdcard文件读取新闻数据
- */
- try {
- String json = manager.getDataFromSDCard("json.der");
- JSONObject obj = new JSONObject(json);
- String data = obj.getString("data");
- mList = resolveJson(data);
- Log.d(TAG, "取出json= " + data);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- int id = item.getItemId();
- if (id == R.id.action_settings) {
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
- private class LoadJsonTask extends AsyncTask<String, Void, String> {
- @Override
- protected String doInBackground(String... params) {
- String json = HttpURLConn.newIntance(params[0]).get();
- if (json == null) {
- return "获取不到数据";
- }
- return json;
- }
- @Override
- protected void onPostExecute(String result) {
- super.onPostExecute(result);
- Log.d(TAG, "result= " + result);
- /**
- * 保存新闻数据到sdcard
- */
- try {
- manager.saveToSdcard("json.der", result);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- private List<NewsBean> resolveJson(String json) {
- List<NewsBean> mList = new ArrayList<NewsBean>();
- try {
- JSONArray mArray = new JSONArray(json);
- int i = 0;
- int length = mArray.length();
- while (i < length) {
- JSONObject obj = mArray.getJSONObject(i);
- String title = obj.getString("title");
- String description = obj.getString("description");
- String images = obj.getString("imgUrl");
- NewsBean bean = new NewsBean(title, description, images);
- mList.add(bean);
- i++;
- }
- mHandler.sendEmptyMessage(REFRESH_LISTVIEW);
- return mList;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
- Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- super.handleMessage(msg);
- switch (msg.what) {
- case REFRESH_LISTVIEW:
- /**
- * 刷新新闻列表
- */
- mAdapter = new NewsBaseAdapter(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,转换代码如下:
- JSONObject obj = new JSONObject(json);
- String data = obj.getString("data");//取出data嵌套的json数组
- mList = resolveJson(data);
四.内存读写数据
内存读写数据的位置在:/data/data/
- /**
- * 存数据到内存
- *
- * @param filename
- * :文件名
- * @param body
- * :文件内容
- */
- public void saveToPhone(String filename, String body) throws Exception {
- /**
- * 开辟一个输出流 filename: 文件名 ,有则打开,无则创建 Mode:文件的操作模式 : private: 私有的覆盖模式 (默认)
- * 、append : 私有的追加模式 return: FileOutputStream
- * 文件的路径:/data/data/<pachagename>/file
- *
- */
- FileOutputStream openFileOutput = context.openFileOutput(filename,
- Context.MODE_APPEND);
- openFileOutput.write(body.getBytes());// 写字符串到文件输出流
- openFileOutput.close();// 关闭流
- }
- /**
- * 从内存读数据
- *
- * @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;
- }
可以将上面新闻中的数据同时保存到内存/sdcard,当启动APP时,首先从内存读取,如果内存的数据不存在,再从sdcard中读取,最后
从网络加载,这是三级缓存的开发思路,结合上面的Demo,完成新闻列表的展示。
五.下一篇文章将介绍《如何读写sqlite数据库中的新闻数据》
你可能感兴趣的文章
转载请注明出处: https://www.teachcourse.cn/1620.html ,谢谢支持!