Android 应用数据库升级

Android应用在数据库结构发生变化时,需要处理数据转换以保证兼容性。SQLiteOpenHelper提供了数据库升级和降级支持,通过onCreate、onUpgrade和onDowngrade方法进行操作。应用需定义DATABASE_VERSION并在每次结构变化时更新,确保所有旧版本能正确升级到新版本。同时,对降级处理需谨慎,部分应用可能不完全支持。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用的更新有两种情况,一是系统应用随着系统OTA更新,二是用户从市场上单独更新app。无论哪种情况,若是前后版本的应用的数据库结构发生了变化,应用需要自行处理前后结构的转换工作。否则,更新后的应用访问数据库时,访问的是更新前的代码建立的数据库,会出现NoSuchColumnException,导致应用FC。


需要说明的是,不只是数据库,所有持久性数据的结构的修改,都需要考虑前后版本的兼容性。否则,轻则丢失用户数据,重则大量的FC,应用无法正常使用。


Android 的SQLiteOpenHelper.java 己经内建了数据库升级或是降级的支持:

 public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
  public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) 
 
 

应用一般由继承了 SQLiteOpenHelper.java  的工具类提供对数据库的封装和访问接口,并重写onCreate,onUpgrade,onDowngrade等。 如ContactsDatabaseHelper.java#996中有:

 
996    protected ContactsDatabaseHelper(
997            Context context, String databaseName, boolean optimizationEnabled) {
998        super(context, databaseName, null, DATABASE_VERSION);      //注意这个
999        mDatabaseOptimizationEnabled = optimizationEnabled;
1000        Resources resources = context.getResources();
1001
1002        mContext = context;
1003        mSyncState = new SyncStateContentProviderHelper();
1004        mCountryMonitor = new CountryMonitor(context);
1005        mUseStrictPhoneNumberComparison = resources.getBoolean(
1006                com.android.internal.R.bool.config_use_strict_phone_number_comparation);
1007    }



可以看到,在构造函数里,传递了一个DATABASE_VERSION给super构造,做为当前数库的版本号。  这个DATABASE_VERSION是应用自己定义的一个int值。一般来讲,每当数据库结构变化时,需要把DATABASE_VERSION变大。在ContactsDatabase中,由于结构复杂,变化次数多,还特别注明了不同Android间大版本的历史:


 
104
105    /**
106     * Contacts DB version ranges:
107     * <pre>
108     *   0-98    Cupcake/Donut
109     *   100-199 Eclair
110     *   200-299 Eclair-MR1
111     *   300-349 Froyo
112     *   350-399 Gingerbread
113     *   400-499 Honeycomb
114     *   500-549 Honeycomb-MR1
115     *   550-599 Honeycomb-MR2
116     *   600-699 Ice Cream Sandwich
117     *   700-799 Jelly Bean
118     *   800-899 Kitkat
119     *   900-999 L
120     * </pre>
121     */
122    static final int DATABASE_VERSION = 910;
 




另外,还有onCreate.  onCreate用来调用"create table" 建立数据库结构的。 如 ContactsDatabaseHelper.java#1150

1149    @Override
1150    public void onCreate(SQLiteDatabase db) {
1151        Log.i(TAG, "Bootstrapping database version: " + DATABASE_VERSION);
1152
1153        mSyncState.createDatabase(db);
1154
1155        // Create the properties table first so the create time is available as soon as possible.
1156        // The create time is needed by BOOT_COMPLETE to send broadcasts.
1157        db.execSQL("CREATE TABLE " + Tables.PROPERTIES + " (" +
1158                PropertiesColumns.PROPERTY_KEY + " TEXT PRIMARY KEY, " +
1159                PropertiesColumns.PROPERTY_VALUE + " TEXT " +
1160                ");");
1161        setProperty(db, DbProperties.DATABASE_TIME_CREATED, String.valueOf(
1162                System.currentTimeMillis()));
1163
1164        db.execSQL("CREATE TABLE " + Tables.ACCOUNTS + " (" +
1165                AccountsColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
1166                AccountsColumns.ACCOUNT_NAME + " TEXT, " +
1167                AccountsColumns.ACCOUNT_TYPE + " TEXT, " +
1168                AccountsColumns.DATA_SET + " TEXT" +
1169        ");");
......


需要注意的是,onCreate只会在应用第一次安装时调用,在应用更新时不会被调用的。 当应用更新时,onUpgrade就需要出场了。 
2197    @Override
2198    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
2199        if (oldVersion < 99) {  //这里还是比较暴力的,如果数据库版本太旧了,就drop掉所有的表,然后调用onCreate再全部重新创建
2200            Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion
2201                    + ", data will be lost!");
2202
2203            db.execSQL("DROP TABLE IF EXISTS " + Tables.CONTACTS + ";");
                .....
2222            onCreate(db);
2223            return;
2224        }
2225

2235
2236        if (oldVersion == 99) {
                ....                     //做些升级到100的处理
2238            oldVersion++;            // 变成100,这样下面继续走100升101的逻辑
2239        }
2240
2241        if (oldVersion == 100) {
                ....                   //100升101
2250            oldVersion++;
2251        }
...
 
虽然大多数情况下用户都是更新到应用的新版本,DB_VERSION通常时增加的,但是不排除降版本的情况。此时onDowngrade就要出场了:


2187    @Override
2188    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
2189        Log.i(TAG, "ContactsProvider cannot proceed because downgrading your database is not " +
2190                "supported. To continue, please either re-upgrade to your previous Android " +
2191                "version, or clear all application data in Contacts Storage (this will result " +
2192                "in the loss of all local contacts that are not synced). To avoid data loss, " +
2193                "your contacts database will not be wiped automatically.");
2194        super.onDowngrade(db, oldVersion, newVersion);
2195    }
2196

对于Contacts DB来讲,处理方法是十分暴力的,直接打了个Log说不支持,然后就啥事不干了。事实上,很多android app对DB downgrade的处理都不完全支持。这也是为什么OTA降级系统版本后,常常会出现更多的应用错误。

下面着重讲一下onUpgrade.

onUpgrade传入的三个参数中,oldVersion表示旧的版本号,也就是更新前的app的数据库版本号,newVersion表示更新后的数据库版本号,应当就是当前代码中的构造函数的DATABASE_VERSION。由于用户的升级不一定是连续的,

因此,要求对于任一可能的oldVersion取值(也就是数据库曾用过的版本号),都能通过onUpgrade正确的升级到当前版本(newVersion)。 

onUpgrade/onDowngrade并不对内部写法做要求,但有一些常见的写法可以参考。
比如,曾用版本是1,2,3,当前版本是4。一般的写法是用一大堆if或是switch:
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 
    switch(oldVersion){
        case 1:
            doUpgradeTo2(db)
        case 2:
           doUpgradeTo3(db)
        case 3:
           doUpgradeTo4(db)
  }
 
}

千万注意,这里没有break,无论是oldVersion是1,2,3 都能一级级的最终到4。 当下次要升级到版本5时,只要处理4->5的就行了,如:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 
    switch(oldVersion){
        case 1:
            doUpgradeTo2(db)
        case 2:
           doUpgradeTo3(db)
        case 3:
           doUpgradeTo4(db)
        case 4:
          doUpgradeTo5(db)            //只要写4->5的就行了,若是1,2,3跑到这里时,己经升级到4了
  }
 
}



综上,app对数据库的升级的处理方法是:

  1.  继承并实现SQLiteOpenHelper.java 工具类 MySqlLiteHelper 
  2. 定义一个DATABASE_VERSION, 并且在MySqlLiteHelper 的构造函数中,调用  super(context, databaseName, null, DATABASE_VERSION);
  3. 在onCreate中实现数据库的构造
  4. 每次修改数据库结构时,增加DATABASE_VERSION,并在onUpgrade中实现旧版本到新版本的数据库升级代码。确保对DATABASE_VERSION的任一历史值,都能升级到最新版本。
  5. 如有需要,在onDowngrade中实现版本降级的代码。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值