设为首页 收藏本站
查看: 1575|回复: 0

[经验分享] Android客户端SQLite数据库升级方案

[复制链接]

尚未签到

发表于 2016-12-1 09:11:11 | 显示全部楼层 |阅读模式
  一,前言
  没有采用Android自身提供的那一套数据库操作方式。而是想对SQLite数据库文件有更全面的控制,包括随时导出数据库文件修改表结构,增删数据等等。这样一来虽然在开放中得到不少便利,但是也带来了数据库升级的一些问题。
  后来不得已采用了一种方案,可以解决问题,现将方案的全部实现细节记录下来。最后也会提出一些我认为有问题的地方。
  二,数据库文件拷贝
  程序不负责数据库的创建,SQLite数据库文件是在外部创建好的。程序启动阶段拷贝进SD卡。以达到对数据库结构的全面控制。
  数据库文件存放位置见附件图片。

public void copyDBFile() {
// 数据库路径
if (!FileOperator.checkFile(SysConst.DB_PATH)) {
boolean result = FileOperator.write2Sdcard(activity,
R.raw.scpip_collection, SysConst.DB_PATH);
Debug.log("无数据库文件,首次拷贝" + result);
if (!result) {
throw new IllegalAccessError(activity
.getString(R.string.copy_db_exception));
} else {
// 拷贝成功,更新数据库版本
try {
PackageInfo info = activity.getPackageManager()
.getPackageInfo(activity.getPackageName(), 0);
// 当前程序版本号,在AndroidManifest.xml中定义
int versionCode = info.versionCode;
Config.saveDbVer(versionCode);
Debug.log("拷贝成功" + result);
} catch (NameNotFoundException e) {
Debug.e(e);
}
}
} else {
// 数据库已存在的情况
if (dbUpdate.needUpdate() ) {
activity.showProgress("数据库升级中,请稍后", false);
new Thread() {
@Override
public void run() {
try {
Debug.log("update db");
dbUpdate.updateDb();
handler.sendEmptyMessage(0);
} catch (Exception e) {
Debug.e(e);
handler.sendEmptyMessage(-1);
}
};
}.start();
}
}
}
  其中有几个要点:
  1、检测数据库文件是否已经存在。不存在则从raw文件夹复制数据库文件拷贝至SD卡中指定目录。
  2、数据库版本是根据应用的versionCode相同。拷贝数据库后,会把当前versionCode写入数据库的表中。

<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx"
android:versionCode="2"
android:versionName="1.01.001">
<uses-sdk android:minSdkVersion="8" />
  versionCode在AndroidManifest.xml文件中。
  在这种方案下,实际上是由versionCode控制数据库版本,versionName控制程序版本。
  3、SD卡指定目录已经存在数据库文件的情况,则读取其中保存的数据库版本号,与versionCode对比,从而确定是否需要升级数据库。代码如下:

public boolean needUpdate() {
int currVer = Config.getDbVer();
return currVer < getAppVersion();
}
public int getAppVersion(){
try {
PackageInfo info = context.getPackageManager().getPackageInfo(
context.getPackageName(), 0);
// 当前程序版本号,在AndroidManifest.xml中定义
return info.versionCode;
} catch (NameNotFoundException e) {
Debug.e(e);
return 1;
}
}
  三,升级数据库
  包括三个步骤:
  1、从程序中拷贝新数据库文件至SD卡指定目录,命名为temp.db。

String temp = SysConst.DB_FOLDER + "temp.db";
boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);
  2、分别获取两个数据源。

//原数据库文件
BaseDao sd = new BaseDao();
//新数据库文件
BaseDao nd = new BaseDao(temp);
  对于SQLite数据库来讲,数据源就是数据库文件。BaseDao是自己封装的,关键在于要可以配置不同的数据源
  具体实现的相关代码如下:

private String dbPath;
public BaseDao() {}
public BaseDao(String dbPath) {
this.dbPath = dbPath;
}
public SQLiteDatabase getDb() {
return SQLiteDatabase.openDatabase(SysConst.DB_PATH, null,
SQLiteDatabase.OPEN_READWRITE);
}
public SQLiteDatabase getDb(String dbPath) {
return SQLiteDatabase.openDatabase(dbPath, null,
SQLiteDatabase.OPEN_READWRITE);
}
  这样就可以根据文件,获取不同的SQLiteDatabase 对象。
  3、传输数据
  把原数据库中的数据查询出来,插入到新数据库中。

public <E>void transfer(BaseDao sd,BaseDao nd,Class<E> cls) throws Exception{
List<E> list = sd.find(cls, null);
nd.batchInsert(list);
}
  这里有两个要点,
  第一,使用了自行封装的ORM。
  第二,使用了SQLite批量插入,增加写入效率。代码如下:

@Override
public <T> void batchInsert(List<T> datas) throws Exception {
SQLiteDatabase dba = null;
if(dbPath == null){
dba = getDb();
} else {
dba = getDb(dbPath);
}
int size = datas.size();
try {
dba.beginTransaction();
for (int i = 0; i < size; i++) {
DaoHelper helper = new DaoHelper(datas.get(i));
String tableName = helper.getTableName();
String sql = "select 1 from " + tableName + " where "
+ helper.getPkCol() + "=";
Object id = helper.getPkValue();
if (id instanceof String) {
sql = sql + "'" + id + "'";
} else {
sql = sql + id;
}
c = dba.rawQuery(sql, null);
if (c != null ? (c.getCount() == 1) : false) {
c.close();
continue;
}
if(c != null){
c.close();
}
//SqlArgs sa = helper.prepareInsert();
//dba.execSQL(sa.getSql(), sa.getArgs());
dba.insert(helper.getTableName(), "", helper.getInsertContent());
}
dba.setTransactionSuccessful();
dba.endTransaction();
} finally {
dba.close();
}
}
  4、写入当前数据库版本,即从程序获得的versionCode。
  5、删除原数据库文件,重命名temp.db。
  完整过程如下:

public void updateDb() throws Exception {
//1,写入新的数据库文件,命名为temp
String temp = SysConst.DB_FOLDER + "temp.db";
boolean s1 = FileOperator.write2Sdcard(context, R.raw.scpip_collection,temp);
Debug.log("s1:" + s1);
if(s1) {
//原数据库文件
BaseDao sd = new BaseDao();
//新数据库文件
BaseDao nd = new BaseDao(temp);
//转移数据
//此处代码略
//删除原数据库文件,重命名临时数据库文件
if(FileOperator.delSdcardFile(SysConst.DB_PATH)){
File file = new File(temp);
file.renameTo(new File(SysConst.DB_PATH));
}
//此时更新数据库版本
Config.saveDbVer(getAppVersion());
}
}
  至此,整个数据库升级完成。在保留原数据的基础上,获取了新的数据库结构。
  四,问题
  1,需要保留的原数据与新表结构不符。这个可以在程序中控制,创建新的OR映射。
  2,效率问题,如果需要保留的数据量非常大的情况下,是否会出现问题。这个是亟待解决的,目前我还没有想到解决办法。
  五,总结
  这个方案针对的情况是外部创建数据库文件,程序启动时从apk包将数据库文件拷贝进SD卡,从而达到对数据库文件的完全控制。
  方案步骤:
  1,检测是否需要升级数据库文件。数据库文件版本是由versionCode控制。程序升级时,如果需要升级数据库,则要将versionCode+1。
  2,将新数据库文件(存在于apk中),写入SD卡。
  3,转移数据。
  4,删除原数据库文件,重命名新数据库文件。
  5,向新数据库写入当前数据库版本。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-307990-1-1.html 上篇帖子: Android 数据库操作 以及命令行上操作sqlite 下篇帖子: 给sqlite数据库加密的两种方法
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表