数据列表离线缓存的另一种方法

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,这只是提供另一种思路。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容