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/对应包名下。如下图所示:
(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;
}
}
推荐阅读
-
Java Android 开发数字不足位数前面补0
-
微信表情开放平台发布 微信6.2.4 for Android正式版下载
-
谷歌android apk开发工具Android Studio安装使用图文教程
-
Android Studio 官方最新版下载地址(支持国内下载)
-
Linux下Android开发环境搭建详细步骤
-
Android自定义View实现微信支付密码输入框
-
Android自定义相机Camera实现手动对焦的方法示例
-
Android EditText每4位自动添加空格效果
-
Android EditText追加空格、限制字符等方法示例
-
Android通过AlarmManager类实现简单闹钟功能