前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android数据库存储模块封装,让操作记录更好用可复用

Android数据库存储模块封装,让操作记录更好用可复用

作者头像
杨永贞
发布2020-08-04 21:25:22
8360
发布2020-08-04 21:25:22
举报

按照模块化搭积木的思想。

要实现一款POS机,需要存储模块、配置文件操作模块,通信模块,卡操作模块,界面显示模块。

如果这些都具备了,实现一完整的pos机岂不是很简单?就像搭积木一样。

再在android的框架下按照一种MVVM架构去实现,轻而易举且能做到结构清晰。

先来看结果,操作记录有多简单,

Record01 rec = new Record01();

rec.recType = 1;//赋值

RecordApi api = RecordApi.getInstance();

api.saveRec01(rec);//存储

long unrec = api.getRecUnNum();//获取未上传记录数

Log.d("un send rec num:",String.valueOf(unrec));

//依此上传未上传记录

Record01 rdrec;

for(int i =0 ;i < unrec;i++){

rdrec = api.readRecNotServer(1+i);

Log.d("recDebug:",rdrec.toString());

}

//删除已上传记录:

RecordApi api = RecordApi.getInstance();

api.decRec01(unrec);

目前有这个打算从零开始构建一个Android上运行的POS机应用。

先从构建各个模块开始。银联8583解析的模块有了,操作配置文件的已经有了,前文介绍过了。通信的简单,Android上有各种开源的库,其中的retrofit配合Rxjava就很好用。卡操作模块打算用JNI做封装,因此卡操作模块也简单。重要的就是数据存储模块了,

以及后续对MVVM架构模式做个探究,再加上界面就完整了。

这里主要针对数据存储做个封装,让接口更好用。

Android上对本地数据库的操作很简单,且有很多开源的第三方ORM库可以用,如litepal ,greendao,realm,OrmLite等。

其中的greendao据说是性能很好,它能够支持数千条记录的CRUD每秒,和OrmLite相比,GreenDAO要快几乎4.5倍。

Realm 则是一个移动数据库,可运行于手机、平板和可穿戴设备之上。可以让你的应用更快速,带来难以想象的体验。其目标是为了代替 CoreData 和 SQLite 数据库。 Realm非常易用,不是在SQLite基础上的ORM,它有自己的数据查询引擎。是完全重新开发的数据库,速度非常快,并且支持跨平台,数据加密,数据迁移,支持json,流式api等 。

Android官方也有操作sqllite的ROOM框架。

这里我采用了国人做的一个ORM框架litepal,我觉得它操作简单好用,且升级维护表结构也简单。

只所以要封装,是因为本身的数据的 增,删,改,查,无论用哪种框架或者直接用原生的sqlhelper都不难。

但是这还不能满足我们的要求。

我们的需求是,数据存储不能一直顺序存,存满指定数目要从头覆盖存储。一条一条的覆盖已经上传过的记录。

且从不能删除记录,记录只能打过上传标记被覆盖,不能删除记录表里的记录。

还要能查询未上传记录数目,按顺序依次上传未上传的记录数量。

因此,本次封装实现原理,新建一个数据目录表。一个记录表对应一个记录目录表。

记录目录表里就一条记录,用来跟踪记录表里当前记录写到的ID位置,记录上传ID的位置。

且要考虑同一条记录可能需要上传至多个后台(平台)。没上传之前不能被覆盖。

可能有人说直接在一个记录表里就可以搞定了,或者写几个sql语句可以实现。但是数据量很大时效率是个问题。

以下为实现:

代码语言:javascript
复制
package com.example.yang.testmvvm.database.table;

import org.litepal.annotation.Column;
import org.litepal.crud.LitePalSupport;

/**
 * Created by yangyongzhen on 2018/08/07
 *
 * RecordDir,记录表对应的目录表,用来对记录表进行管理。
 * RecordDir表,记录了当前记录的写的位置及记录读的位置。
 * 可据此实现,查询未上传记录数量,依次上传未上传记录,
 * 记录顺序存储,存满指定容量后从头覆盖存储的方式。
 * 删除记录操作只更新记录目录表的读的位置,从不真正的从记录表删除数据,保证数据的安全性。
 */
public class RecordDir extends LitePalSupport {
    @Column(nullable = false)
    //Litepal数据库自动生成的自增的ID
    private long recNO;
    private long writeID;
    private long readID1;
    private long readID2;
    private long readID3;
    private int  mode;

    private int  upDateFlag;
    private long curWriteID;
    private long curReadID;
    @Column(ignore = true)
    public static final long MaxRecNO = 10;

    public long getRecNO() {
        return recNO;
    }

    public long getWriteID() {
        return writeID;
    }

    public long getReadID1() {
        return readID1;
    }

    public long getReadID2() {
        return readID2;
    }

    public long getReadID3() {
        return readID3;
    }

    public long getCurReadID() {
        return curReadID;
    }

    public int getMode() {
        return mode;
    }
    public long getCurWriteID() {
        return curWriteID;
    }

    public void setRecNO(long recNO) {
        this.recNO = recNO;
    }

    public void setWriteID(long writeID) {
        this.writeID = writeID;
    }

    public void setReadID1(long readID1) {
        this.readID1 = readID1;
    }

    public void setReadID2(long readID2) {
        this.readID2 = readID2;
    }

    public void setReadID3(long readID3) {
        this.readID3 = readID3;
    }

    public void setMode(int mode) {
        this.mode = mode;
    }
    public void setCurWriteID(long curWriteID) {
        this.curWriteID = curWriteID;
    }
    public void setCurReadID(long curReadID) {
        this.curReadID = curReadID;
    }

    public int getUpDateFlag() {
        return upDateFlag;
    }

    public void setUpDateFlag(int upDateFlag) {
        this.upDateFlag = upDateFlag;
    }

    @Override
    public String toString() {
        return "RecordDir{" +
                "recNO=" + recNO +
                ", writeID=" + writeID +
                ", readID1=" + readID1 +
                ", readID2=" + readID2 +
                ", readID3=" + readID3 +
                ", mode=" + mode +
                ", upDateFlag=" + upDateFlag +
                ", curWriteID=" + curWriteID +
                ", curReadID=" + curReadID +
                '}';
    }
}

记录操作的接口:

代码语言:javascript
复制
package com.example.yang.testmvvm.database;

import android.content.Context;
import android.util.Log;

import com.example.yang.testmvvm.app.App;
import com.example.yang.testmvvm.database.table.Record01;
import com.example.yang.testmvvm.database.table.RecordDir;

import org.litepal.LitePal;

/**
 * Created by yangyongzhen on 2018/08/07
 * 实现记录的常用操作接口:记录存储,查询未上传记录数,读取未上传记录,删除记录
 */
public class RecordApi {

    public RecordDir recDir01;

    private static Context context;
    private static RecordApi instance = null;

    private RecordApi(Context contxt){
        context = contxt;
        recDir01 = LitePal.find(RecordDir.class, 1);
        if(recDir01 == null){
            recDir01 = new RecordDir();
            recDir01.setWriteID(0);
            recDir01.setReadID1(0);
            recDir01.setRecNO(0);
            recDir01.setUpDateFlag(0);
            recDir01.save();
        }

    }

    /**
     * 保存记录
     * @param rec
     * @return
     */
    public int saveRec01( Record01 rec){
        if(rec == null){
            return 1;
        }
        if(recDir01.getWriteID()+1 > RecordDir.MaxRecNO){
            if((recDir01.getWriteID() + 1 - RecordDir.MaxRecNO) == recDir01.getReadID1()){
                return 2;//记录满
            }
            recDir01.setWriteID(1);
            recDir01.setUpDateFlag(1);
            rec.update(1);
        }
        else {
            if(recDir01.getUpDateFlag() == 1){
                if((recDir01.getWriteID() + 1) == recDir01.getReadID1()){
                    return 3;//记录满
                }
                rec.update(recDir01.getWriteID()+1);
                recDir01.setCurWriteID(recDir01.getWriteID()+1);
                recDir01.setRecNO(recDir01.getRecNO() + 1);
                recDir01.setWriteID(recDir01.getWriteID() + 1);
                recDir01.update(1);
            }
            else {
                rec.save();
                recDir01.setRecNO(recDir01.getRecNO() + 1);
                recDir01.setWriteID(recDir01.getWriteID() + 1);
                recDir01.setCurWriteID(recDir01.getWriteID()+1);
                recDir01.update(1);
            }
        }
        Log.d("WriteRec:",recDir01.toString());
        return 0;
    }

    /**
     * 删除记录,实际上只更改记录目录表的读指针,并不删除记录表的数据
     * 记录表的数据采取循环存储,循环覆盖的模式,保证安全性
     * @param recnum
     * @return
     */
    public int decRec01(long recnum){

        long id = recDir01.getReadID1();
        if(recDir01.getWriteID() == recDir01.getReadID1()){
            return 0;
        }
        if((id + recnum) > RecordDir.MaxRecNO){
            if((id + recnum - RecordDir.MaxRecNO) > recDir01.getWriteID() ){
                recDir01.setReadID1(recDir01.getWriteID());
                recDir01.setCurReadID(recDir01.getWriteID());
                recDir01.update(1);
                return 0;
            }
            recDir01.setReadID1(id + recnum - RecordDir.MaxRecNO);
            recDir01.setCurReadID(id + recnum - RecordDir.MaxRecNO);
            recDir01.update(1);
        }else {
            if(recDir01.getWriteID() > recDir01.getReadID1()){
                if(id + recnum > recDir01.getWriteID()){
                    recDir01.setReadID1(recDir01.getWriteID());
                    recDir01.setCurReadID(recDir01.getWriteID());
                    recDir01.update(1);
                    return 0;
                }
            }
            recDir01.setReadID1(id + recnum);
            recDir01.setCurReadID(id + recnum);
            recDir01.update(1);
        }
        return 0;
    }

    /**
     * 获取未上传的记录条数
     * @return
     */
    public long getRecUnNum()
    {
        long num = 0;
        if(recDir01.getWriteID() == recDir01.getReadID1()){
            num = 0;
            return num;
        }
        if(recDir01.getUpDateFlag() == 0){
            num = (recDir01.getWriteID() - recDir01.getReadID1());
        }else{
            if(recDir01.getWriteID() > recDir01.getReadID1()){
                num = (recDir01.getWriteID() - recDir01.getReadID1());
            }else{
                num = RecordDir.MaxRecNO - recDir01.getReadID1() + recDir01.getWriteID();
            }
        }
        return num;
    }

    /**
     * 读取未上传的记录数据,顺序读取
     * sn取值 1-到-->未上传记录数目
     * @param sn
     * @return
     */
    public Record01 readRecNotServer(long sn){

        Record01 rec;
        long id = recDir01.getReadID1();
        if((id + sn) > RecordDir.MaxRecNO){
            if(id + sn - RecordDir.MaxRecNO > recDir01.getWriteID()){
                return null;
            }
            rec = LitePal.find(Record01.class, id + sn - RecordDir.MaxRecNO );
        }else {
            if(recDir01.getReadID1() < recDir01.getWriteID()){
                if((id + sn) > recDir01.getWriteID()){
                    return null;
                }
            }
            rec = LitePal.find(Record01.class, recDir01.getReadID1() + sn);

        }
        return rec;
    }

    public static RecordApi getInstance(){
        if(instance == null){
            instance = new RecordApi(App.getContext());
        }
        return instance;
    }
}

一个测试 demo:

代码语言:javascript
复制
btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //String anotherName = "John Doe";
                //myViewModel.getName().setValue(anotherName);
                //myViewModel.loginmodel.login("qq8864","123456", LoginType.TYPE_LOGIN);
                Record01 rec = new Record01();
                rec.recType = 1;
                RecordApi api = RecordApi.getInstance();
                api.saveRec01(rec);
                long unrec = api.getRecUnNum();
                Record01 rdrec;
                Log.d("un send rec num:",String.valueOf(unrec));
                for(int i =0 ;i < unrec;i++){
                    rdrec = api.readRecNotServer(1+i);
                    Log.d("recDebug:",rdrec.toString());
                }

            }
        });

        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //mThread = new LongTimeWork();
                //mThread.start();
                /*
                sysCfg.ver.value = "1234";
                sysCfg.ip.value = "218.28.133.181";
                sysCfg.saveConfig();
                sysCfg.printConfig();
                sysCfg.ver.value = "5678";
                sysCfg.saveConfig();
                sysCfg.printConfig();
                */
                RecordApi api = RecordApi.getInstance();
                api.decRec01(1);
                Log.d("del rec:",String.format("curwriteid:%d,curreadid:%d",api.recDir01.getCurWriteID(),api.recDir01.getCurReadID()));

            }
        });

测试日志:

D/WriteRec:: RecordDir{recNO=81, writeID=9, readID1=2, readID2=0, readID3=0, mode=0, upDateFlag=1, curWriteID=9, curReadID=2} D/un send rec num:: 7 D/recDebug:: Record01{id=3, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0} D/recDebug:: Record01{id=4, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0} D/recDebug:: Record01{id=5, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0} D/recDebug:: Record01{id=6, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0} D/recDebug:: Record01{id=7, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0} D/recDebug:: Record01{id=8, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0} D/recDebug:: Record01{id=9, physn=0, recType=1, psamTid='null', lineID=0, dealTime='null', cardAsn='null', dealMoney=0, oldBalance=0}

至此,操作记录存储以及上传未上传记录变得很简单了。

首先一开始先定义好Recod01表结构。

存记录时:

代码语言:javascript
复制
Record01 rec = new Record01();
rec.recType = 1;//赋值
RecordApi api = RecordApi.getInstance();
api.saveRec01(rec);//存储
long unrec = api.getRecUnNum();//获取未上传记录数
Record01 rdrec;
Log.d("un send rec num:",String.valueOf(unrec));
//依此上传未上传记录
for(int i =0 ;i < unrec;i++){
    rdrec = api.readRecNotServer(1+i);
    Log.d("recDebug:",rdrec.toString());
}

//删除已上传记录:

代码语言:javascript
复制
RecordApi api = RecordApi.getInstance();
api.decRec01(unrec);
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-08-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档