欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Room

程序员文章站 2022-05-14 13:12:41
...

官方文档:https://developer.android.com/topic/libraries/architecture/room.html

一、Room数据库框架结构

1、数据实体对象:作为业务逻辑的数据结构组成部分,并且提供标准的get/set方法,如果有必要也需重写equals和hashCode方法。与DAO层的交互是通过注解的方式来实现的。
(1)Entity注解:是在类层次上的注解,主要的作用是指定tableName,默认情况下是表名就是类名.很好奇两个表名相同的数据实体对象在执行数据读写操作时会是什么情况?编译的时候就报类似如下的错误:

Error:(15, 8) 错误: Table name "contact" is used by multiple entities: com.example.dson.app.model.entry.Contact, com.example.dson.app.model.entry.BadContact

(2)实体属性注解:作用就是为数据库表中字段赋予特殊性,最常见的就是PrimaryKey和Ignore,PrimaryKey注解标注的属性将作为主键,而Ignore注解标注的字段不会被保存。

(3)字段分为两种:一是基本类型,是不需要进行转换;而对象类型:需要转换,否则会报如下错误:

Error:(29, 24) 错误: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

(4)具体如下联系人的示例代码:

首先引入顶层build.gradle配置和引入模块build.gradle配置

allprojects {
    repositories {
        jcenter()
        maven { url 'https://maven.google.com' }
    }
}
    compile "android.arch.persistence.room:runtime:1.0.0-alpha3"
    annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha3"
    testCompile "android.arch.persistence.room:testing:1.0.0-alpha3"

@Entity
public class Contact {

    @PrimaryKey(autoGenerate = true)
    private long id;
    private String name;
    @Ignore
    private boolean isChecked;
    private String phone;
    private char initial;
    private long time;
    @Ignore
    private BadContact object;
    private long groupId;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public boolean isChecked() {
        return isChecked;
    }

    public void setChecked(boolean checked) {
        isChecked = checked;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public char getInitial() {
        return initial;
    }

    public void setInitial(char initial) {
        this.initial = initial;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public BadContact getObject() {
        return object;
    }

    public void setObject(BadContact object) {
        this.object = object;
    }

    @Override
    public String toString() {
        return "name:" + name + "initial:" + initial + "phone:" + phone + "isChecked:" +
            isChecked;
    }

    public long getGroupId() {
        return groupId;
    }

    public void setGroupId(long groupId) {
        this.groupId = groupId;
    }

    public static LetterComparator getComparator() {
        return new LetterComparator();
    }

    private static class LetterComparator implements Comparator<Contact> {

        @Override
        public int compare(Contact lhs, Contact rhs) {
            char a = lhs.getInitial();
            char b = rhs.getInitial();
            if (a == b) {
                return 0;
            } else if (a < b) {
                return -1;
            } else {
                return 1;
            }
        }
    }
}
2、DAO

(1)Dao标注:该标注作用于接口或者抽象类,注解处理器将会自动生成该Dao的实现类,位于app/build/generated/source/apt/debug/对应包名下。如下图所示:

Android Room

(2)方法注解:目前提供了Insert、Update、Delete和Query四种注解,对于前三种标注作用的方法,操作的参数的是实体对象,而Query需要提供相应的查询语句,注意数据库表名或字段不能使用数据库的标识符,否则编译报错,如:

@Query("select * from Group where name in(:groupName)")
    Group load(String groupName);
报错:
Error:(29, 11) 错误: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (near "Group": syntax error)

(3)具体代码如下:

@Dao
public abstract class ContactDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract void save(Contact contact);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract void saveAll(List<Contact> contacts);

    @Query("SELECT * FROM Contact WHERE name IN(:contactName)")
    public abstract Contact load(String contactName);

    @Delete
    public abstract void remove(Contact contact);
}
3、数据库对象RoomDatabase

(1)继承RoomDatabase,并提供相关Dao获取的抽象方法方法

(2)Database注解:提供数据库版本号,数据实体对象,具体代码如下:

首先在Application中创建与配置Room数据库

private static AppDatabase sAppDatabase;

    @Override
    public void onCreate() {
        super.onCreate();
        sAppDatabase = Room.databaseBuilder(this, AppDatabase.class, "app").allowMainThreadQueries().build();
    }

    public static AppDatabase getAppDatabase() {
        return sAppDatabase;
    }
}

@Database(version = 12, entities = {Contact.class, ActivityEntry.class, BadContact.class, Group.class})
public abstract class AppDatabase extends RoomDatabase {

    public abstract ContactDao contactDao();

    public abstract BadContactDao badContactDao();

    public abstract ActivityEntryDao activityEntryDao();

    public abstract GroupDao groupDao()
}

(3)注意:每次修改数据库的schema之后必须升级数据库的版本号,常见的修改schema的场景是增删表、增删表中字段等

Process: com.example.dson.myapplication, PID: 19493
                                                                                java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.dson.myapplication/com.example.dson.app.ui.activity.MainActivity}: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
                                                                                    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2423)
                                                                                    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2483)
                                                                                    at android.app.ActivityThread.access$900(ActivityThread.java:153)
                                                                                    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1349)
                                                                                    at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                    at android.os.Looper.loop(Looper.java:148)
                                                                                    at android.app.ActivityThread.main(ActivityThread.java:5438)
                                                                                    at java.lang.reflect.Method.invoke(Native Method)
                                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
                                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)
                                                                                 Caused by: java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
                                                                                    at android.arch.persistence.room.RoomOpenHelper.checkIdentity(RoomOpenHelper.java:111)
                                                                                    at android.arch.persistence.room.RoomOpenHelper.onOpen(RoomOpenHelper.java:92)
                                                                                    at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$1.onOpen(FrameworkSQLiteOpenHelper.java:64)
                                                                                    at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:266)
                                                                                    at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
                                                                                    at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:106)
                                                                                    at android.arch.persistence.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:82)
                                                                                    at android.arch.persistence.room.RoomDatabase.inTransaction(RoomDatabase.java:235)
                                                                                    at android.arch.persistence.room.InvalidationTracker$1.run(InvalidationTracker.java:277)
                                                                                    at android.arch.persistence.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:408)
                                                                                    at android.arch.persistence.room.RoomDatabase.beginTransaction(RoomDatabase.java:186)
                                                                                    at com.example.dson.app.db.dao.ActivityEntryDao_Impl.saveAll(ActivityEntryDao_Impl.java:48)
                                                                                    at com.example.dson.app.ui.activity.MainActivity.onCreate(MainActivity.java:65)
                                                                                    at android.app.Activity.performCreate(Activity.java:6303)
                                                                                    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1108)
                                                                                    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2376)
                                                                                    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2483)?
                                                                                    at android.app.ActivityThread.access$900(ActivityThread.java:153)?
                                                                                    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1349)?
                                                                                    at android.os.Handler.dispatchMessage(Handler.java:102)?
                                                                                    at android.os.Looper.loop(Looper.java:148)?
                                                                                    at android.app.ActivityThread.main(ActivityThread.java:5438)?
                                                                                    at java.lang.reflect.Method.invoke(Native Method)?
                                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)?
                                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)?
(4)Room与Dagger结合使用:例如提供Dao实例
@Module
public class DaoModule {

    @Provides
    ContactDao contactDao() {
        return new ContactDao_Impl(MainApplication.getAppDatabase());
    }

    @Provides
    GroupDao groupDao() {
        return new GroupDao_Impl(MainApplication.getAppDatabase());
    }
}
public class ContactActivity extends BaseActivity implements LoaderManager
        .LoaderCallbacks<List<Contact>>, LetterPickView.OnLetterChangedListener,
        ContactAdapter.ContactCheckedListener {

    @BindView(R.id.contact_list)
    ListView mListView;

    @BindView(R.id.contact_pick)
    LetterPickView mLetterPickView;

    private ContactAdapter mAdapter;
    private List<Contact> mEntries;

    @Inject
    ContactDao contactDao;

    public static void start(Activity activity) {
        Intent intent = new Intent(activity, ContactActivity.class);
        activity.startActivity(intent);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);

        mLetterPickView.setOnLetterChangedListener(this);
        mEntries = new ArrayList<>();
        mAdapter = new ContactAdapter(this, mEntries);
        mAdapter.setCheckedListener(this);
        mListView.setAdapter(mAdapter);

        getLoaderManager().initLoader(0, null, this);
    }

    @Override
    public String getActivityTag() {
        return ContactActivity.class.getSimpleName();
    }

    @Override
    public Loader<List<Contact>> onCreateLoader(final int id, Bundle args) {
        return new ContactLoader(this);
    }

    @Override
    public void onLoadFinished(Loader<List<Contact>> loader,
                               final List<Contact> data) {
        mEntries.clear();
        mEntries.addAll(data);
        mAdapter.notifyDataSetChanged();
        new Thread(new Runnable() {
            @Override
            public void run() {
                contactDao.saveAll(data);
            }
        }).start();
    }

    @Override
    public void onLoaderReset(Loader<List<Contact>> loader) {

    }

    @Override
    public void onLetterChanged(char letter) {
        int pos = getLetterPosition(letter);
        LogDebug("onLetterChanged:" + letter + ",pos:" + pos);
        mListView.setSelection(pos);
    }

    private int getLetterPosition(char initial) {
        int count = mEntries.size();
        for (int i = 0; i < count; i++) {
            char a = mEntries.get(i).getInitial();
            LogDebug("initial:" + initial + ",index all:" + a);
            if (a == initial) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public void onChecked(List<Contact> checkedList) {
        LogDebug("onChecked:" + checkedList);
    }
}
public class ContactLoader extends AsyncTaskLoader<List<Contact>> {

    @Inject
    GroupDao groupDao;

    @Inject
    public ContactLoader(Context context) {
        super(context);
    }

    @Override
    public List<Contact> loadInBackground() {
        List<Contact> entries = queryContactList();
        Collections.sort(entries, Contact.getComparator());
        return entries;
    }

    @Override
    protected void onStartLoading() {
        forceLoad();
    }

    private List<Contact> queryContactList() {
        List<Contact> contacts = new ArrayList<>();
        Cursor contactCursor = null;
        try {
            contactCursor = getContext().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                new String[]{ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER},
                ContactsContract.Contacts.HAS_PHONE_NUMBER + "=1", null, null);
            if (contactCursor == null) {
                return contacts;
            }
            final int displayNameIndex = contactCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
            final int phoneNumberIndex = contactCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);

            int index = 0;
            while (contactCursor.moveToNext()) {
                String displayName = contactCursor.getString(displayNameIndex);
                String phoneNumber = contactCursor.getString(phoneNumberIndex);

                if (TextUtils.isEmpty(phoneNumber)) {
                    continue;
                }

                Contact invitationEntry = new Contact();
                invitationEntry.setName(displayName);
                invitationEntry.setInitial(PinyinUtil.getInitial(displayName));
                invitationEntry.setChecked(false);
                invitationEntry.setPhone(phoneNumber);
                contacts.add(invitationEntry);
            }
        } catch (Exception e) {
            Log.d("TAG", "Exception:" + e.toString());
        } finally {
            if (contactCursor != null) {
                contactCursor.close();
            }
        }
        return contacts;
    }
}