Android ContentProvider结合Room保存数据库

ContentProvider结合Room保存数据库

1. ContentProvider

使用理解

ContentProvider主要作用为共享使用本地存储数据,要想让多个app使用包名、类名全都相同的ContentProvider,那么每个app都要持有自己相应的授权(authorities),可以向下面一样对这个ContentProvider进行注册

<!-- authorities的值为使用此Provider的app的包名,在app的build.gradle中需配置applicationId -->
<!-- 最好在后面在拼接一些特殊标识字符串,普遍为你的Provider的类名 -->
<provider
    android:name="{你的Provider的包名}.{你的Provider的类名}"
    android:authorities="${applicationId}.{你的特殊标识}"
    android:exported="true"
    android:multiprocess="true" />

执行顺序

  1. 注册了的ContentProvider会先于Application创建,所以要考虑一些情况的处理,例如

Timber打印。

如果在Application才添加Timber.plant(new Timber.DebugTree());,那么ContentProvider中Timber打印在刚启动时肯定不会打印,可以在ContentProVider添加它。也要注意不要在项目中多次添加这句话,否则你的打印会成倍的输出。

读写权限。

这里的ContentProvider要对本地的数据库.db文件进行处理,所以要考虑还没有读写权限时创建文件和打开数据库的异常处理。

  1. ContentProvider的构造方法先于onCreate方法执行

构造方法一般无参,里面可以处理不需要Context参与的内容,例如文件的创建,前提是有读写权限才能成功。onCreate方法就有了Context参数,可以用其打开数据库,读取PackageManager里的内容等等。还有UriMatcher的添加要使用到授权(authorities),建立数据库表的Uri也需要授权(authorities)进行拼接,具体拼接为"content://" + authority + "/表名"

重写Provider的增删改查方法

这里只贴出ContentProvider源码中相应的方法

// 单条增
public abstract @Nullable Uri insert(@NonNull Uri uri, @Nullable ContentValues values);

// 批量增,虽然源码已经实现,但是也是公开方法,可以覆盖重写
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
    int numValues = values.length;
    for (int i = 0; i < numValues; i++) {
        insert(uri, values[i]);
    }
    return numValues;
}

// 按条件删除
public abstract int delete(@NonNull Uri uri, @Nullable String selection,
            @Nullable String[] selectionArgs);

// 按条件更新
public abstract int update(@NonNull Uri uri, @Nullable ContentValues values,
            @Nullable String selection, @Nullable String[] selectionArgs);

// 按条件查询
public abstract @Nullable Cursor query(@NonNull Uri uri, @Nullable String[] projection,
            @Nullable String selection, @Nullable String[] selectionArgs,
            @Nullable String sortOrder);

之后就是在重写里面添加数据库的操作,可以使用之前的SQLiteDatabase,但感觉对数据库有些改动需要升级的时候非常难管理,经常导致多个应用数据库版本不同而使某些功能崩溃,所以这里在第二部分会介绍如何使用Room来管理数据库。

使用Provider的增删改查方法

方法重写好之后就是调用,调用很简单,只要获取到ContentResolver对象调用和重写方法名相同的方法的就行。

ContentResolver contentResolver = context.getContentResolver());
contentResolver.insert();
contentResolver.bulkInsert();
contentResolver.delete();
contentResolver.update();
contentResolver.query();

2. Room

Room中的@Database、@Entity、@Dao结合使用不再多说。

单独使用@Database

因为项目之前的Bean对象嵌套层级多,而且实际建表用的SQL语句拼接,好多表中的字段和Bean中定义的字段不同,所以放弃给之前的Bean加@Entity注解,这样用@Dao注解来管理增删改查也就不能使用。相当于单单使用@Database注解,只是图它好升级的作用。

// 这里entities对应的大括号里不能为空,所以还新增加了一张简单的表,那么原来的表怎么创建?
@Database(entities = {PackageInfoBean.class}, version = 1, exportSchema = false)

不使用@Entity如何创建表(SupportSQLiteDatabase)

创建原来的表在实例化RoomDatabase对象的监听中实现,只要能拿到Room框架中的SupportSQLiteDatabase对象,就可以像使用原来的SQLiteDatabase对象一样来调用一切方法。

yourDatabase = Room.databaseBuilder(context.getApplicationContext(), YourRoomDatabase.class, getDBFilePath())
                    .allowMainThreadQueries()
                    .addMigrations(MIGRATION_1_2) // 数据库升级使用,其实这里的升级就不针对原来的表了
                    .addCallback(callback)
                    .build();
/**
 * 监听数据库,创建和打开的操作
 */
RoomDatabase.Callback callback = new RoomDatabase.Callback() {
    @Override
    public void onCreate(@NonNull SupportSQLiteDatabase db) {
        super.onCreate(db);
        Timber.d("数据库创建成功,当前版本为: %d", db.getVersion());
        // 数据库被创建执行建表语句
        db.execSQL("创建表1的sql语句");
        db.execSQL("创建表2的sql语句");
        db.execSQL("创建表3的sql语句");
    }

    @Override
    public void onOpen(@NonNull SupportSQLiteDatabase db) {
        super.onOpen(db);
        Timber.d("数据库打开成功,当前版本为: %d", db.getVersion());

        for (int i = 0; i < db.getVersion(); i++) {
            if (i == 1) { // 版本1: 相应的升级内容
                db.execSQL("升级的sql语句");
                db.execSQL("升级的sql语句");
            }
            if (i == 2) { // 版本2: 预留,暂无更改内容

            }
        }
    }
};

RoomDatabase.Callback只有两个方法,一个onCreate、一个onOpen。

onCreate只有在db不存在的时候会执行一次,并且里面打印SupportSQLiteDatabase的版本号始终是0,onCreate执行后就会执行onOpen,而且打印的版本号已经变成申明@Database注解中的指定的version。

如果db已经存在,再创建RoomDatabase回调就只会执行onOpen。

不使用@Dao如何管理表(SupportSQLiteDatabase)

上面的回调中可以获取到SupportSQLiteDatabase对象,便可以利用execSQL方法执行SQL语句,那不在回调中要怎么拿到SupportSQLiteDatabase对象。如下为对表的增和查,删和改都类似于它们。

/**
 * 向某张表中插入数据,其中yourDatabase是上面步骤中创建的RoomDatabase对象
 */
 public long insert(Context context, String table, String nullColumnHack, ContentValues values) {
    SupportSQLiteDatabase supportDB = yourDatabase.getOpenHelper().getWritableDatabase();
    supportDB.beginTransaction();
    long raw;
    try {
        raw = supportDB.insert(table, SQLiteDatabase.CONFLICT_NONE, values);
        supportDB.setTransactionSuccessful();
    } finally {
        supportDB.endTransaction();
    }
//        db.close();
    return raw;
}
/**
 * 查询表中的数据,其中yourDatabase是上面步骤中创建的RoomDatabase对象
 */
public Cursor query(Context context, SupportSQLiteQuery query) {
    Cursor cursor = yourDatabase.getOpenHelper().getWritableDatabase().query(query);
//        db.close();
    return cursor;
}

查询要注意接口SupportSQLiteQuery,它类似于之前的SQLiteQueryBuilder,但使用完全不同,这里给出官方的示例及对应的地址SimpleSQLiteQuery实现了SupportSQLiteQuery,实现它的还有RoomSQLiteQuery,但并没有在官网找到示例。

@Dao
interface RawDao {
@RawQuery
Song getSongViaQuery(SupportSQLiteQuery query);
}
// Usage of RawDao
SimpleSQLiteQuery query = new SimpleSQLiteQuery(
“SELECT * FROM Song WHERE id = ? LIMIT 1”,
new Object[]{ songId});
Song song = rawDao.getSongViaQuery(query);

public class SongAndArtist {
@Embedded
public Song song;
@Embedded
public Artist artist;
}
@Dao
interface RawDao {
@RawQuery
SongAndArtist getSongAndArtist(SupportSQLiteQuery query);
}
// Usage of RawDao
SongAndArtist result = rawDao.getSongAndArtist(
new SimpleSQLiteQuery(“SELECT * FROM Song, Artist WHERE Song.artistId = Artist.id LIMIT 1”))

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值