Android中很大一部分APP,都是非常“轻”的,这里的“轻”指的是APP的“体量”和业务逻辑。受手机性能的限制和安全的考虑,绝大部分运算以及业务逻辑都放在后台执行,移动端只是一个展示和操作(交互)的平台,所以在开发APP的过程中,推荐尽量使用比较轻度的方法或插件等。
工作这几年了,开发的APP大大小小应该超过20个了,极少使用到数据库,当然这和开发的APP业务相关。今天要说的是使用文件存储方式存储离线缓存列表数据。
先来介绍Android中几种存储方式。
Android提供以下四种存储方式:
SharePreference
SharedPreference是一种轻型的数据存储方式,实际上是基于XML文件存储的“key-value”键值对数据。通常用来存储程序的一些配置信息,例如是否夜间模式,是否接收推送等。其存储在“data/data/程序包名/shared_prefs目录下。
SQLite
SQLite是一个轻量级关系型数据库,主要存储一些缓存数据,例如用户信息,离线展示数据等。
File
文件储存方式,主要存储文件,例如下载下来的apk、图片等
ContentProvider
能够实现跨应用之间的数据操作,例如联系人(各个应用都能申请获取)
另一种思路
现在有个列表数据需要缓存在本地,根据官方推荐肯定是使用数据库存储,但是考虑到这是一个非常简单的需求,存储的数据不需要做修改、联查等(整存整取),可以试试使用文件存储。
下面是封装的文件存储类:
public class CacheList<T>{
private static final String LOG_TAG = CacheList.class.getSimpleName();
private static Map<String, Object> _lockMap = new HashMap<>();
private static final String
LIST_FILE_DIR = "solid_list";
public static final int UNLIMITED_SIZE = -1;
private Context _context;
private String _name;
private int _maxSize;
private String _path;
private LinkedList<T> _linkedList;
private Object _lock;
private DeadListCallback<T> _callback;
public CacheList(Context context, String name, int maxSize, DeadListCallback<T> callback) {
_context = context;
_name = name;
_maxSize = maxSize;
_callback = callback;
_path = _context.getDir(LIST_FILE_DIR, Context.MODE_PRIVATE) + "/" + _name;
createLock();
createList();
}
private void createLock() {
if( ! _lockMap.containsKey(_name)){
_lockMap.put(_name, new Object());
}
_lock = _lockMap.get(_name);
}
@SuppressWarnings("unchecked")
private void createList() {
synchronized (_lock) {
File listFile = new File(_path);
if( ! listFile.exists()) {
_linkedList = new LinkedList<T>();
Log.d(LOG_TAG, "list file not exist, file=" + _path + ", create an empty list");
return;
}
try{
FileInputStream fis = new FileInputStream(_path);
ObjectInputStream ois = new ObjectInputStream(fis);
_linkedList = (LinkedList<T>)(ois.readObject());
ois.close();
} catch(Exception e) {
Log.e("Exception" ,e.toString());
_linkedList = new LinkedList<T>();
}
}
}
public boolean remove(int index) {
synchronized (_lock) {
if(index < 0 || index >= _linkedList.size()) {
return false;
}
T element = _linkedList.remove(index);
if(-1 == _linkedList.lastIndexOf(element)){
// no same element in list any more
if(null != _callback) {
_callback.onRemove(element);
}
}
solidify();
return true;
}
}
public boolean remove(T element) {
synchronized (_lock) {
if(_linkedList.remove(element)) {
if(-1 == _linkedList.lastIndexOf(element)){
// no same element in list any more
if(null != _callback) {
_callback.onRemove(element);
}
}
solidify();
return true;
}else{
Log.d(LOG_TAG, "remove element fail");
}
return false;
}
}
public boolean addAll(List<T> elementList) {
synchronized (_lock) {
int i = 0;
for( ; i < elementList.size(); ++i) {
if(UNLIMITED_SIZE == _maxSize || _linkedList.size() < _maxSize) {
T element = elementList.get(i);
_linkedList.addLast(element);
if(null != _callback) {
_callback.onAdd(element);
}
} else {
break;
}
}
solidify();
return i >= elementList.size();
}
}
public boolean add(T element) {
synchronized (_lock) {
if(UNLIMITED_SIZE == _maxSize || _linkedList.size() < _maxSize) {
Log.e("cache", "add element,name: " + _name);
_linkedList.addLast(element);
if(null != _callback) {
_callback.onAdd(element);
}
solidify();
return true;
}
return false;
}
}
public boolean addFrist(T element) {
synchronized (_lock) {
if(UNLIMITED_SIZE == _maxSize || _linkedList.size() < _maxSize) {
Log.e("cache", "add element,name: " + _name);
_linkedList.addFirst(element);
if(null != _callback) {
_callback.onAdd(element);
}
solidify();
return true;
}
return false;
}
}
/**
* 固化链表(固化到本地)
*/
public void solidify() {
synchronized (_lock) {
try {
FileOutputStream fos = new FileOutputStream(_path);
ObjectOutputStream oos = new ObjectOutputStream(fos);
while(_maxSize != UNLIMITED_SIZE && _linkedList.size() > _maxSize) {
T element = _linkedList.removeLast();
if(-1 == _linkedList.lastIndexOf(element)){
// no same element in list any more
if(null != _callback) {
_callback.onRemove(element);
}
}
}
oos.writeObject(_linkedList);
oos.close();
} catch(Exception e) {
Log.e("Exception" ,e.toString());
}
}
}
public void clear() {
synchronized (_lock) {
int oldMaxSize = _maxSize;
setMaxSize(0);
solidify();
setMaxSize(oldMaxSize);
}
}
public boolean destroy(){
synchronized (_lock) {
try {
clear();
File listFile = new File(_path);
if(listFile.exists()) {
boolean succ = listFile.delete();
if(succ) {
Log.d(LOG_TAG, "list file delete succ, path=" + _path);
} else {
Log.e(LOG_TAG, "list file delete fail, path=" + _path);
}
return succ;
} else {
Log.d(LOG_TAG, "list file not exist, delete fail, path=" + _path);
return false;
}
} catch(Exception e) {
Log.e("Exception", e.toString());
return false;
}
}
}
public void setMaxSize(int maxSize) {
synchronized (_lock) {
_maxSize = maxSize;
}
}
public int size() {
synchronized (_lock) {
return _linkedList.size();
}
}
public LinkedList<T> getList() {
synchronized (_lock) {
return _linkedList;
}
}
public interface DeadListCallback<E> {
void onAdd(E element);
void onRemove(E element);
}
}
代码非常简单,不做解释了,使用时:
1、新建列表:(如果已经存在缓存则会读取缓存)
userCacheList = new CacheList<>(this, "test", 50, new CacheList.DeadListCallback<User>() {
@Override
public void onAdd(User element) {
Log.e("tag", "add element id: " + element.getId());
}
@Override
public void onRemove(User element) {
Log.e("tag", "remove element id: " + element.getId());
}
});
2、添加数据并固化到本地:
List<User> userList = new ArrayList<>();
for (int i=0; i<50; i++){
User user = new User();
user.setId(i);
user.setName("name"+i);
user.setAddress("中华人民共和国北京天安门"+i);
user.setImgUrl("lskdjflsajflsajflsafjlasdjflasdfasfasdfasfsaf"+i);
user.setPwd("pwd"+i);
user.setSex(1);
userList.add(user);
}
userCacheList.addAll(userList);
userCacheList.solidify();
3、删除数据:
3种删除方法
userCacheList.clear();//全清
userCacheList.remove(1);//按Position删除数据
userCacheList.remove(new User());//按对象删除数据
删除完别忘了固化到本地:
userCacheList.solidify();
实测50条数据的读取
12-22 15:59:14.533 23506-23506/com.twsm.testdemo E/tag: get data before: 1513929554533
12-22 15:59:14.570 23506-23506/com.twsm.testdemo E/tag: get data after: 1513929554570
不到40毫秒,完全可以在主线程读取(测试的数据量比较小,当然不推荐在主线程做操作)。
这样看起来是不是很方便,挺适合做列表展示数据的离线缓存。当然,还是推荐使用SqlLite,这只是提供另一种思路。
网友评论