日期:2014-05-16  浏览次数:20469 次

Android客户端SQLite数据库升级方案

一,前言

没有采用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批量插入,增加写入效率。代码如下:

@Overri