Android数据存储 | 青训营笔记

86 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第五天

本文用于记录我在字节跳动青训营学习到的有关Android数据存储方面的知识

存储方式

常见的数据持久化存储大致分为一下四种。

image.png

SharedPreferences 常用于保存APP的各种配置,例如登录模块的是否记住密码、是否自动登录等。

文件存储 常用于保存文件。

ContentProvider 跨APP进行数据共享,常用于读取系统应用的数据,例如图库。

SQLite 数据库常用于存储结构化数据,也是较为重要的一种数据存储方式。

数据库存储

Android提供了可以直接操作SQLite的一套接口,但是其往往比较复杂,所以在实际应用中,多数选择使用相关框架,加快开发速度。

常用的SQLite框架有三种,下面是他们之间的一些对比

image.png

在这里我记录一下在这次大作业中,团队使用到的Room框架的使用方法。

Room

在这里我们使用的Java + Room + RxJava

第一步:引依赖

    def room_version = "2.4.3"
    implementation "androidx.room:room-runtime:$room_version"
    annotationProcessor "androidx.room:room-compiler:$room_version"
    // RxJava依赖
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    implementation "androidx.room:room-rxjava3:$room_version"

第二步:编写实体类

@Entity(tableName = "listData_table", primaryKeys = {"id", "version"})
public class ListData {

    public static final int MOVIE_TYPE = 1;
    public static final int TELEPLAY_TYPE = 2;
    public static final int VARIETY_TYPE = 3;

    @NonNull
    public String id = "";

    /**
     * 榜单期数,一周一期
     */
    @NonNull
    public Integer version = 0;
    /**
     * 演员
     */
    public List<String> actors;
}

在实体类上需要使用@Entity 注解标明其是一个数据库实体,并设置好表名、约束等。

第三步:编写DAO接口

/**
 * 对表listData_table进行数据操作
 */
@Dao
public interface IListDataDao {

    /**
     * 获取所有榜单数据
     * @param type 数据类型 {@link ListData#type ListData.type}
     * @param version 榜单版本
     * @return 榜单数据
     */
    @Query("select * from listData_table where type = :type and version = :version")
    Single<List<ListData>> getListData(Integer type, Integer version);
    /**
     * 插入榜单数据集合
     * @param listData 需要插入的榜单数据集合
     * @return 异步操作对象
     */
    @Insert
    Completable insertList(List<ListData> listData);
}

注意:这里如果在Sql中需要引入方法参数,使用的是 : ,和后端JDBC使用的 # $ 不同。Sql语句和后端Mysql等的差不多。

在这里可以直接标明一个@Insert表示插入数据,可以是一条,也可以是集合

第四步:定义数据库对象

@Database(entities = {ListData.class,UserOpenInfo.class},version = 2,exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {

    private static volatile AppDatabase instance;

    private static String dbPath = "database-name";

    public static AppDatabase getInstance() {
        /*
          获取database单例
         */
        if (instance == null) {
            synchronized (AppDatabase.class) {
                if (instance == null) {
                    instance = Room.databaseBuilder(MyApplication.getAppContext(), AppDatabase.class, dbPath)
                            .fallbackToDestructiveMigration()
                            .build();
                }
            }
        }
        return instance;
    }

    /**
     * 获取榜单dao的操作对象
     * @return
     */
    public abstract IListDataDao getListDataDao();

第五步:调用接口


/**
* 更新数据库中的数据
*/
private void updateDataBase(List<ListData> list, Integer type, Integer version) {
    IListDataDao dao = AppDatabase.getInstance().getListDataDao();
    for (ListData data :
            list) {
        data.version = version;
    }

    Single<List<ListData>> single = dao.getListData(type, version);
    single.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
            .subscribe(new SingleObserver<List<ListData>>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {

                }

                @Override
                public void onSuccess(@NonNull List<ListData> listData) {
                    if (listData == null || listData.isEmpty()) {
                        //数据库中没有数据,直接插入数据库
                        dao.insertList(list).subscribeOn(Schedulers.io()).subscribe();
                        return;
                    }
                    dao.updateListData(list).subscribeOn(Schedulers.io()).subscribe();
                }

                @Override
                public void onError(@NonNull Throwable e) {
                    Log.d(TAG, "onError: 查询数据库失败!!!" + e.getMessage());
                }
            });
}

总体就是按照这五步进行,在操作数据库的时候,由于Android的特性,我们不能直接在主线程操作数据库,而需要在子线程,可以引入线程池,也可以使用异步框架,我们这里使用的就是RxJava这个框架。