Android数据持久化篇(五)—— 自己动手封装一个属于自己的数据存储框架

在前面的几篇文章里面,我们讲了关于文件的使用https://www.jianshu.com/p/36fc304f1ab8,SharedPreference的使用https://www.jianshu.com/p/784450ae7df9,SQLite数据库的使用https://www.jianshu.com/p/95901ccfc54c等等,这些都是属于数据持久化的系列,但是这些都是孤立的、零散的知识节点,很多时候我们需要学会将零散的知识点进行整合,然后通过各种设计模式对外暴露接口供应用层调用,那么,今天我们就来看看如何将之前的数据化内容进行整合。

在开始之前,我们需要先了解一下Java中23种设计模式之一的工厂模式,因为本篇的重点并不在这里,所以我就稍微带一下就好。

工厂模式的实现分为3种:

(1)简单工厂

(2)工厂方法

(3)抽象工厂

简单工厂
简单工厂.png

这是简单工厂的结构图,其实不难看出,sample为抽象接口,定义每一个具体实现类的规范,而Factory是用来生成sample的具体实例供外部去使用,举一个例子:

public abstract class Computer {

    public abstract String getRAM();
    public abstract String getHDD();
    public abstract String getCPU();

    @Override
    public String toString(){
        return "RAM= "+this.getRAM()+", HDD="+this.getHDD()+", CPU="+this.getCPU();
    }}

Computer 为一个抽象的电脑类,里面定义了CPU、RAM、HDD这几个参数,然后它的实现类则为:

Dell电脑类
public class DellComputer extends Computer {

    private String ram;
    private String hdd;
    private String cpu;

    public DellComputer(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public String getRAM() {
        return this.ram;
    }

    @Override
    public String getHDD() {
        return this.hdd;
    }

    @Override
    public String getCPU() {
        return this.cpu;
    }
}
Apple电脑类
public class AppleComputer extends Computer {

    private String ram;
    private String hdd;
    private String cpu;

    public AppleComputer(String ram, String hdd, String cpu){
        this.ram=ram;
        this.hdd=hdd;
        this.cpu=cpu;
    }
    @Override
    public String getRAM() {
        return this.ram;
    }

    @Override
    public String getHDD() {
        return this.hdd;
    }

    @Override
    public String getCPU() {
        return this.cpu;
    }
}
编写工厂类供外部调用
public class ComputerFactory {

    public static Computer getComputer(String type, String ram, String hdd, String cpu) {
        if ("DELL".equalsIgnoreCase(type)) {
            return new DellComputer(ram, hdd, cpu);
        } else if ("Apple".equalsIgnoreCase(type)) {
            return new AppleComputer(ram, hdd, cpu);
        }
        return null;
    }}
最后简单测试一下
public class TestFactory {

    public static void main(String[] args) {
        Computer dell= ComputerFactory.getComputer("DELL", "2 GB", "500 GB", "2.4 GHz");
        Computer apple= ComputerFactory.getComputer("Apple", "16 GB", "1 TB", "2.9 GHz");
        System.out.println("Factory DELL Config::" + dell);
        System.out.println("Factory APPLE Config::" + apple);
    }
}

这样一个简单的简单工厂就完成了。工厂方法和抽象工厂这里就不展开讲解了,有兴趣的同学可以自己去学习学习。

开始进入正题

按照简单工厂的结构,首先我们需要定义一个抽象类去约束具体实现规则,抽象类或者接口都可以,这里我定义的是一个接口,如下所示:

/**
 * Created by zhoufan on 2018/2/24.
 * 实现对数据的存储
 */

public interface IOHandler {
    // 保存String类型 
    IOHandler saveString(String key, String value);
    // 保存Float类型
    IOHandler saveFloat(String key, float value);
    // 保存Int类型
    IOHandler saveInt(String key, int value);
    // 保存Boolean类型
    IOHandler saveBoolean(String key, boolean value);
    // 保存Long类型
    IOHandler saveLong(String key, long value);
    // 获取String类型的值
    String getString(String key);
    // 获取Float类型的值
    float getFloat(String key, float defaultValue);
    // 获取Int类型的值
    int getInt(String key, int defaultValue);
    // 获取Boolean类型的值
    boolean getBoolean(String key, boolean defaultValue);
    // 获取Long类型的值
    long getLong(String key, long defaultValue);
    // 执行删除操作
    void delete(String key);
    // 更新文件的名称
    void updateFileName(String fileName);
}

然后去实现具体的子类。

实现File文件类的存储
/**
 * author: zhoufan
 * data: 2021/8/9 10:21
 * content: 对文件的操作进行进一步的封装
 */
public class FileUtil implements IOHandler {

    // 使用单例生成唯一实例
    private static volatile FileUtil mInstance;
    private String mFileName;

    public static FileUtil getInstance() {
        if (mInstance == null) {
            synchronized (IOFactoryUtil.class) {
                if (mInstance == null) {
                    mInstance = new FileUtil();
                }
            }
        }
        return mInstance;
    }

    private FileUtil() {
        mFileName = "fileCache";
    }


    @Override
    public IOHandler saveString(String key, String value) {
        BufferedWriter bufferedWriter = null;
        try {
            boolean isResult = FileTool.createFolderAndFileItemDir(mFileName, key);
            if (isResult) {
                FileWriter fileWriter = new FileWriter(key);
                bufferedWriter = new BufferedWriter(fileWriter);
                bufferedWriter.write(value);
                bufferedWriter.flush();
            } else {
                MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.create_error));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedWriter != null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return this;
    }

    @Override
    public IOHandler saveFloat(String key, float value) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return this;
    }

    @Override
    public IOHandler saveInt(String key, int value) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return this;
    }

    @Override
    public IOHandler saveBoolean(String key, boolean value) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return this;
    }

    @Override
    public IOHandler saveLong(String key, long value) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return this;
    }

    @Override
    public String getString(String key) {
        BufferedReader bufferedReader = null;
        StringBuilder builder = new StringBuilder();
        try {
            String path = FileTool.getItemDirPath() + key;
            File file = new File(path);
            if (file.exists()) {
                FileReader fileReader = new FileReader(file);
                bufferedReader = new BufferedReader(fileReader, 512);
                String readBuff = null;
                while ((readBuff = bufferedReader.readLine()) != null) {
                    builder.append(readBuff);
                }
            } else {
                MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_no_exists));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return builder.toString();
    }

    @Override
    public float getFloat(String key, float defaultValue) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return 0;
    }

    @Override
    public int getInt(String key, int defaultValue) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return 0;
    }

    @Override
    public boolean getBoolean(String key, boolean defaultValue) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return false;
    }

    @Override
    public long getLong(String key, long defaultValue) {
        MyToast.showCenterSortToast(IApplication.getContext(), IApplication.getContext().getString(R.string.file_type_error));
        return 0;
    }

    @Override
    public void delete(String key) {
        FileTool.deleteFile(key);
    }

    @Override
    public void updateFileName(String fileName) {
        this.mFileName = fileName;
    }
}
实现MMKV的存储
/**
 * author: zhoufan
 * data: 2021/8/8 9:29
 * content: 数据持久化过程中使用MMKV进行存储
 */
public class MMKVUtil implements IOHandler {

    // 使用单例生成唯一实例
    private static volatile MMKVUtil mInstance;
    private MMKV mMMKV;

    public static MMKVUtil getInstance() {
        if (mInstance == null) {
            synchronized (IOFactoryUtil.class) {
                if (mInstance == null) {
                    mInstance = new MMKVUtil();
                }
            }
        }
        return mInstance;
    }

    private MMKVUtil() {
        mMMKV = MMKV.defaultMMKV();
    }


    @Override
    public IOHandler saveString(String key, String value) {
        if (key == null || value == null) {
            return this;
        }
        mMMKV.encode(key, value);
        return this;
    }

    @Override
    public IOHandler saveFloat(String key, float value) {
        if (key == null) {
            return this;
        }
        mMMKV.encode(key, value);
        return this;
    }

    @Override
    public IOHandler saveInt(String key, int value) {
        if (key == null) {
            return this;
        }
        mMMKV.encode(key, value);
        return this;
    }

    @Override
    public IOHandler saveBoolean(String key, boolean value) {
        if (key == null) {
            return this;
        }
        mMMKV.encode(key, value);
        return this;
    }

    @Override
    public IOHandler saveLong(String key, long value) {
        if (key == null) {
            return this;
        }
        mMMKV.encode(key, value);
        return this;
    }

    @Override
    public String getString(String key) {
        if (key == null) {
            return null;
        }
        return mMMKV.decodeString(key);
    }

    @Override
    public float getFloat(String key, float defaultValue) {
        if (key == null) {
            return defaultValue;
        }
        return mMMKV.decodeFloat(key);
    }

    @Override
    public int getInt(String key, int defaultValue) {
        if (key == null) {
            return defaultValue;
        }
        return mMMKV.decodeInt(key);
    }

    @Override
    public boolean getBoolean(String key, boolean defaultValue) {
        if (key == null) {
            return defaultValue;
        }
        return mMMKV.decodeBool(key);
    }

    @Override
    public long getLong(String key, long defaultValue) {
        if (key == null) {
            return defaultValue;
        }
        return mMMKV.decodeLong(key);
    }

    @Override
    public void delete(String key) {
        mMMKV.removeValueForKey(key);
    }

    @Override
    public void updateFileName(String fileName) {
        mMMKV = MMKV.mmkvWithID(fileName);
    }
}
实现SharedPreference的存储
public class SharePreferencesUtil implements IOHandler {

    // 使用单例生成唯一实例
    private static volatile SharePreferencesUtil mInstance;
    private String mFileName;

    public static SharePreferencesUtil getInstance() {
        if (mInstance == null) {
            synchronized (IOFactoryUtil.class) {
                if (mInstance == null) {
                    mInstance = new SharePreferencesUtil();
                }
            }
        }
        return mInstance;
    }

    private SharePreferencesUtil() {
        mFileName = "sharedPreferenceCache";
    }

    @Override
    public SharePreferencesUtil saveString(String key, String value) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString(key, value);
        editor.apply();
        return this;
    }

    @Override
    public SharePreferencesUtil saveFloat(String key, float value) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putFloat(key, value);
        editor.apply();
        return this;
    }

    @Override
    public SharePreferencesUtil saveInt(String key, int value) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putInt(key, value);
        editor.apply();
        return this;
    }

    @Override
    public SharePreferencesUtil saveBoolean(String key, boolean value) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putBoolean(key, value);
        editor.apply();
        return this;
    }

    @Override
    public SharePreferencesUtil saveLong(String key, long value) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putLong(key, value);
        editor.apply();
        return this;
    }


    @Override
    public String getString(String key) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        return sharedPreferences.getString(key, null);
    }

    @Override
    public float getFloat(String key, float defaultValue) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        return sharedPreferences.getFloat(key, defaultValue);
    }

    @Override
    public int getInt(String key, int defaultValue) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        return sharedPreferences.getInt(key, defaultValue);
    }

    @Override
    public boolean getBoolean(String key, boolean defaultValue) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        return sharedPreferences.getBoolean(key, defaultValue);
    }

    @Override
    public long getLong(String key, long defaultValue) {
        SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
        return sharedPreferences.getLong(key, defaultValue);
    }

    // 执行删除操作
    @Override
    public void delete(String key) {
        try {
            SharedPreferences sharedPreferences = IApplication.getContext().getSharedPreferences(mFileName, 0);
            SharedPreferences.Editor editor = sharedPreferences.edit();
            if (sharedPreferences.contains(key)) {
                editor.remove(key);
                editor.apply();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void updateFileName(String fileName) {
        this.mFileName = fileName;
    }
}

也就是说,FileUtil(文件存储)、MMKVUtil(MMKV存储)、SharedPreferenceUtil(SharedPreference存储)就是IOHandler的具体实现类,类似于上面图中的sampleA、sampleB等等。接下来要操作的就是我们的Factory了。

首先定义工厂的抽象类

public interface IOFactory {

    IOHandler create(String type);

}
然后去实现我们的工厂,在这里提供对外的实现类
/**
 * Created by zhoufan on 2018/2/24.
 * 使用抽象工厂方法实现对缓存的处理
 */

public class IOFactoryUtil implements IOFactory {

    // 使用单例生成唯一实例
    private static volatile IOFactoryUtil ioFactoryUtil;
    public static final String MMKV_HANDLER = "1";
    public static final String SHAREDPREFERENCE_HANDLER = "2";
    public static final String FILE_HANDLER = "3";
    private IOHandler mIOHandler;

    public static IOFactoryUtil getIOFactoryUtil() {
        if (ioFactoryUtil == null) {
            synchronized (IOFactoryUtil.class) {
                if (ioFactoryUtil == null) {
                    ioFactoryUtil = new IOFactoryUtil();
                }
            }
        }
        return ioFactoryUtil;
    }

    @Override
    public IOHandler create(String type) {
        switch (type) {
            case SHAREDPREFERENCE_HANDLER:
                mIOHandler = getSharePreferencesHandler();
            case FILE_HANDLER:
                mIOHandler = getFileHandler();
            default:
                mIOHandler = getMMKVHandler();
        }
        return mIOHandler;
    }

    // SharePreferences存储
    private IOHandler getSharePreferencesHandler() {
        return SharePreferencesUtil.getInstance();
    }

    // MMKV存储
    private IOHandler getMMKVHandler() {
        return MMKVUtil.getInstance();
    }

    // 文件存储
    private IOHandler getFileHandler() {
        return FileUtil.getInstance();
    }

    // 默认的存储方式
    public IOHandler getUserHandler() {
        return create(MMKV_HANDLER);
    }
}

代码写的很详细,就不做过多讲解了,到这里,通过一个简单工厂就实现了对数据持久化实现的简单封装,最后来看下实现

// 使用默认的数据存储方式(默认为MMKV)
IOFactoryUtil.getIOFactoryUtil().userHandler.saveString("key","value")
// 切换引擎进行数据存储
IOFactoryUtil.getIOFactoryUtil().create(IOFactoryUtil.SHAREDPREFERENCE_HANDLER).saveString("key","value")

这样,一个简单又好用的数据持久化就完成啦

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容