美文网首页
数据列表离线缓存的另一种方法

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

作者: pkxutao | 来源:发表于2017-12-25 10:30 被阅读23次

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

    相关文章

      网友评论

          本文标题:数据列表离线缓存的另一种方法

          本文链接:https://www.haomeiwen.com/subject/zdjpbxtx.html