一些思路与看法
在开发Android应用时,必不可少的就是去网络请求,我们的一般做法是这样的,进入一个页面,页面需要向后台获取数据,我们就用一个异步请求去获取数据,每次进入每次请求,可能我们不会去关心后台的网络请求量,也很少关心这样频繁访问会有多少的流量,在wifi环境下还好,这个是不花钱的,但是在移动流量是需要money的,用户说你这个应用跑流量太多了,你赶紧给我优化,没办法,客户是上帝!
然后我们就来想办法,怎么做呢,做数据缓存呗,减少网络访问。缓存怎么做呢,我们可以这样做,一个是文件存储,将你的json数据都存为文件,一个是数据库存储,将每次的访问的url作为数据库的ID,这样,你可以在需要数据的时候依据ID去数据库查询。好了,假如现在我们的缓存已经做好了,但是用户又说你这个保证不了数据的实时度啊,好吧!于是我们找到了解决办法,给页面增加一个功能按钮,点击刷新时去重新获取数据,这个对于列表的话一般用一个下拉刷新这样的控件。现在我们感觉可以了,但用户说,我不想去点击刷新.哎,我们继续想办法,怎么办呢。那我们就给他设置个文件的有效时间吧,比如说一个小时重新请求数据,或者更短,或者更长,这就要看你数据的更新度了。好了,现在我们的缓存思路基本有了。下面来实现
实现之前,我们先来比较一下数据库缓存和文件缓存,个人认为,数据库缓存比较麻烦,你还需要去依据建表什么的,数据库不要忘了必须有一个时间的字段,而文件存储就好办了,每个文件都有一个最后修改时间,我们只需要依据这个时间来决定是否重新请求。
下面来看具体实现
文件缓存
1、检测文件是否存在
/**
* 检测缓存文件是否存在
* */
private boolean checkExist() {
File files = new File(cacheFile + "/" + fileName);
if (files.exists()) {
return true;
} else {
return false;
}
}
2、获取文件的最后修改时间,并且和当前时间作比较
//文件存在,读取文件的最后修改时间
File files = new File(cacheFile + "/" + fileName);
long lastModify = files.lastModified();
Date date = new Date(files.lastModified());
Log.d("zhanglinshu", "date -- " + date);
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.d("zhanglinshu", "lastModify --" + df.format(date));
Date currentDate = new Date();
Log.d("zhanglinshu", "currentDate --" + df.format(currentDate));
long time = currentDate.getTime() - date.getTime();
//一分钟的time
long min = time / (1000 * 60);
Log.d("zhanglinshu", "diffMin" + min);
3、如果大于60分钟,也就是一个小时,则重新获取数据,否则直接读取缓存文件中的数据,当然这个时间视具体时间而定
if (min > 60) {
request();
} else {
readFile();
}
request()
/**
* 从网络获取数据
*/
private void request() {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
"http://gc.ditu.aliyun.com/geocoding?a=深圳市",
null,
new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d("zhanglinshu", "reponse---->" + response.toString());
textGet.setText("写入前:" + response.toString());
writeFile(response.toString());
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
mQueue.add(jsonObjectRequest);
}
我这里采用的是Volley网络请求框架,只需要在你的build文件中添加下面代码即可
compile 'com.mcxiaoke.volley:library:1.0.19'
writeFile()
/**
* 写文件
*/
private void writeFile(String string) {
checkExist();
FileOutputStream fos = null;
try {
fos = openFileOutput(fileName, MODE_PRIVATE);
byte[] bytes = string.getBytes();
fos.write(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
readFile()
/**
* 读文件
*/
private String readFile() {
Log.d("zhanglinshu", "readFile " + readFile());
FileInputStream fis = null;
ByteArrayOutputStream stream = null;
try {
//获得输入流
fis = openFileInput(fileName);
stream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length = -1;
while ((length = fis.read(buffer)) != -1) {
//获取内存缓存区的数据
stream.write(buffer, 0, length);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fis.close();
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return stream.toString();
}
上述就是一个简单的文件缓存策略,大家可以根据自己的需求做相应的调整和优化
下面给出我的布局文件
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/id_request_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/btn_bg"
android:text="请求数据"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/id_text_get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="16sp" />
<Button
android:id="@+id/id_read_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:background="@drawable/btn_bg"
android:text="读取数据"
android:textColor="@android:color/white" />
<TextView
android:id="@+id/id_text_show"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="16sp" />
</LinearLayout>
btn_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="48dp" />
<solid android:color="#FF6A4C" />
<size
android:width="48dp"
android:height="48dp" />
</shape>
具体的效果界面看的不是太理想,需要大家自行测试,源码会在文章末尾出现。
数据库缓存
数据库缓存和文件缓存的思路一致,只不过讲文件读写替换为了数据库的增删查改
这里不再赘述过多。
数据库我这里采用的是Afinal
Afinal的用法很简单,关键是那个实体类,记得不要忘记setter getter方法,他是Afinal数据库的关键
具体使用代码中有体现,很简单很好用对于数据结构不太复杂的建议你去用Afinal
下面给出代码
public class DataBaseCacheActivity extends AppCompatActivity implements View.OnClickListener {
RequestQueue mQueue;
private TextView textGet;
private Button buttonGet;
private Button buttonShow;
FinalDb db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_base_cache);
mQueue = Volley.newRequestQueue(this);
db = FinalDb.create(this, "test.db", true);
initViews();
initData();
}
private void initData() {
List<ResponseBean> lists = db.findAll(ResponseBean.class);
ResponseBean responseBean = null;
if (lists != null && lists.size() >= 0) {
responseBean = lists.get(0);
}
Date date = new Date(responseBean.getDate());
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Log.d("zhanglinshu", "lastModify --" + df.format(date));
Date currentDate = new Date();
Log.d("zhanglinshu", "currentDate --" + df.format(currentDate));
long time = currentDate.getTime() - date.getTime();
//一分钟的time
long min = time / (1000 * 60);
Log.d("zhanglinshu", "diffMin" + min);
if (min > 60) {
request();
} else {
//直接用数据库中的数据
textGet.setText("数据库中的数据 " + responseBean.toString());
}
}
/**
* 控件初始化
*/
private void initViews() {
buttonShow = (Button) findViewById(R.id.id_request_data_1);
buttonShow.setOnClickListener(this);
buttonGet = (Button) findViewById(R.id.id_read_data_1);
buttonGet.setOnClickListener(this);
textGet = (TextView) findViewById(R.id.id_text_show_1);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.id_request_data_1:
request();
break;
}
}
private void request() {
StringRequest stringRequest = new StringRequest(
"http://gc.ditu.aliyun.com/geocoding?a=深圳市",
new Response.Listener() {
@Override
public void onResponse(Object response) {
Log.d("zhanglinshu", "response --" + response.toString());
textGet.setText("网络请求的数据 " + response.toString());
Gson gson = new Gson();
ResponseBean responseBean = null;
Type type = new TypeToken<ResponseBean>() {
}.getType();
responseBean = gson.fromJson(response.toString(), type);
responseBean.setDate((new Date()).toString());
db.deleteAll(ResponseBean.class);
db.save(responseBean);
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
}
);
mQueue.add(stringRequest);
}
}
最关键的实体类
responseBean.java
package com.example.lenovo.filecachetest;
import net.tsz.afinal.annotation.sqlite.Table;
import java.io.Serializable;
/**
* Created by lenovo on 2016/6/30.
*/
@Table(name = "response")
public class ResponseBean implements Serializable {
private int id;
private String lon;
private String level;
private String alevel;
private String address;
private String lat;
private String cityName;
private String date;
public ResponseBean() {
}
public String getLon() {
return lon;
}
public void setLon(String lon) {
this.lon = lon;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getAlevel() {
return alevel;
}
public void setAlevel(String alevel) {
this.alevel = alevel;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
至此两种数据缓存方案都介绍完了,
我个人还是比较中意文件缓存,他没有那么多的表去建立,省了好多麻烦事,哈哈,具体哪个方便,看你喽
源码下载