android - Room:替换 Dao 中的 SQLite 游标?
问题描述
我有一个 JobIntentService 从 SQLite 数据库重新设置挂起的警报通知。它使用查询和游标从数据库中的 4 个不同列中获取通知日期。我正在转换为 Room 数据库,但不确定如何将光标转换为 Dao 方法。我是否需要使用@Transaction,因为我从数据库中的多个列获取通知?将不胜感激有关如何在 Room 中构建的任何见解或想法。
Service
public class Service extends JobIntentService {
static final int JOB_ID = 9;
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, RebootService.class, JOB_ID, work);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
AlarmManager alarmManager1;
Intent brIntent1;
PendingIntent pendingIntent1;
SQLiteDB sqLiteDB = SQLiteDB.getInstance(this);
Calendar cal1 = Calendar.getInstance();
Cursor cursor = sqLiteDB.resetNotifications();
try {
if (cursor.getCount() > 0) {
cursor.moveToFirst();
int dueDatentimeColumnIndex = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_DUEDATENTIME);
int notifColumnIndex1 = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_NOTIFTIME);
int notif2ColumnIndex2 = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_NOTIFTIME2);
int randColumnIndex1 = cursor.getColumnIndex(ItemContract.ItemEntry.COLUMN_RANDINT);
while (!cursor.isAfterLast()) {
do {
long notifTime1 = cursor.getLong(notifColumnIndex1);
int randInt1 = cursor.getInt(randColumnIndex1);
cal1.setTime(new Date(notifTime1));
// Set up a system AlarmManager to fire a future alarm that sends a Notification
// even if the app is in the background or closed.
alarmManager1 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
if (cal1.getTimeInMillis() > System.currentTimeMillis()) {
brIntent1 = new Intent(this, AlarmReceiver.class);
brIntent1.setAction("24Hour");
pendingIntent1 = PendingIntent.getBroadcast(this, randInt1, brIntent1,
PendingIntent.FLAG_ONE_SHOT);
if (alarmManager1 != null && notifTime1 != -1) {
alarmManager1.set(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(), pendingIntent1);
}
...
}
SQLiteDB.java
...
public Cursor resetNotifications() {
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[]{
ItemContract.ItemEntry.COLUMN_NOTIFTIME,
ItemContract.ItemEntry.COLUMN_NOTIFTIME2,
ItemContract.ItemEntry.COLUMN_DUEDATENTIME,
ItemContract.ItemEntry.COLUMN_RANDINT};
return db.query(
TABLE_NAME,
columns, // The columns to return
null,
null,
null,
null,
null
);
}
This is the code I came up as a replacement:
public class RebootService extends JobIntentService {
// Unique job ID for this service
static final int JOB_ID = 10000;
// Convenience method for enqueueing work to this service.
public static void enqueueWork(Context context, Intent work) {
enqueueWork(context, RebootService.class, JOB_ID, work);
}
private QuickcardRepository reposit1;
@Override
protected void onHandleWork(@NonNull Intent intent) {
reposit1 = new QuickcardRepository(getApplication());
Bundle extras = intent.getExtras(); // Returns the Intent *that started this Service.*
if (extras != null) {
String classname = extras.getString("TAG");
if (classname != null && classname.equals("bootCompleted")) {
AlarmManager alarmManager1;
Intent brIntent1, brIntent2, brIntent3;
PendingIntent pendingIntent1, pendingIntent2, pendingIntent3;
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = Calendar.getInstance();
Calendar cal3 = Calendar.getInstance();
List<Quickcard> resetNotificationsList = reposit1.getNotifications();
// Cycle through the Room database rows to get the Notifications data
for (Quickcard quickcard: resetNotificationsList) {
// Quickcards without a Due date get a Due date in the database of -1.
// Therefore, only cycle through and get data for those quickcards that have
// a Due data and therefore have Notifications (reminders) where the actual
// "Due date" is != -1.
if(quickcard.getDuedatentime() != -1) {
// Set up the 24-Hour calendar object.
long notifTime1 = quickcard.getNotiftime();
int randInt1 = quickcard.getRandint();
cal1.setTime(new Date(notifTime1));
// Set up a system AlarmManager to fire a future alarm that sends a Notification
// even if the app is in the background or closed. Have to add "context" here.
alarmManager1 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// If the stated alarm trigger time is in the past, the alarm will be triggered immediately.
// So only set Alarms to fire Notifications for Duedates in the future
// (meaning > than the current System time). Ignore those in the past.
if (cal1.getTimeInMillis() > System.currentTimeMillis()) {
// For the 24Hour Notifications.
// Set up a PendingIntent that will perform broadcast to the BroadcastReceiver.
brIntent1 = new Intent(this, AlarmReceiver.class);
brIntent1.setAction("24Hour");
// Need to use FLAG_ONE_SHOT on the PendingIntent, not FLAG_UPDATE_CURRENT.
// A random int is used to be able to set multiple alarms and then to be able to
// delete them later (if the user for ex., deletes the quickCards Duedate) using
// the same random int.
pendingIntent1 = PendingIntent.getBroadcast(this, randInt1, brIntent1,
PendingIntent.FLAG_ONE_SHOT);
// Alarms have 3 properties below after "set(...)":
// 1) Alarm type: RTC_WAKEUP type is chosen here to wake the device from sleep.
// 2) Trigger time: in this case, 24 hours before the Duedate/Duetime is reached.
// 3) Pending Intent: A future Intent that is sent when the trigger time is reached.
int SDK_INT1 = Build.VERSION.SDK_INT;
if (SDK_INT1 >= Build.VERSION_CODES.M) {
// Wakes up the device in Doze Mode for API Level 23 and higher.
// The "... != -1" test only sets up pendingIntents for quickCards that have
// a Notification. quickCards with no Duedate & Duetime are bypassed.
if (alarmManager1 != null && notifTime1 != -1) {
alarmManager1.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(),
pendingIntent1);
}
} else if (SDK_INT1 >= Build.VERSION_CODES.KITKAT) {
// Wakes up the device in Idle Mode for API Level 19 to 22.
// The "... != -1" test only sets up pendingIntents for quickCards that have
// a Notification. quickCards with no Duedate & Duetime are bypassed.
if (alarmManager1 != null && notifTime1 != -1) {
alarmManager1.setExact(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(), pendingIntent1);
}
} else {
// Old APIs Level 18 and below.
// The "... != -1" test only sets up pendingIntents for quickCards that have
// a Notification. quickCards with no Duedate & Duetime are bypassed.
if (alarmManager1 != null && notifTime1 != -1) {
alarmManager1.set(AlarmManager.RTC_WAKEUP, cal1.getTimeInMillis(), pendingIntent1);
}
}
}
解决方案
我相信@Transaction将代码包装在事务中。除了@Query 之外,这已经完成(如果 @Query 不是更新/删除查询(如果它是更新或删除查询,它被包装在事务中))。
我认为关于 SELECT 查询是否应该包含在事务(@Transaction @Query......
)中的问题在于是否使用了 @Relation。如果是,则相关/关联项目/对象的列表将作为单独的查询运行,因此在事务中运行它们将确保一致的数据。否则,基础数据可能会被其他事务更改,因此结果数据可能会不一致。
说在不需要的地方使用@Transaction几乎不会产生影响,如果它被编码在可能无意中没有被编码的地方,它甚至可能产生积极的影响。
当然,您始终可以使用 Room 来返回 Cursor。您可能希望查看在 Android Studio 中使用 Room 数据库关联表,其中有一些示例。
根据您的代码,主要是您有一个带有 ItemEntry 子类的 ItemContract,然后 ItemEntry 的实体可以在ItemEntry.java中,根据:-
@Entity
public class ItemEntry {
@PrimaryKey(autoGenerate = true)
private long id;
@ColumnInfo(name = COLUMN_NOTIFTIME)
private long notiftime;
@ColumnInfo(name = COLUMN_NOTIFTIME2)
private long notiftime2;
@ColumnInfo(name = COLUMN_DUEDATENTIME)
private long duedatentime;
@ColumnInfo(name = COLUMN_RANDINT)
public int randint;
public ItemEntry(){
}
@Ignore
public ItemEntry(long notiftime, long notiftime2, long duedatentime, int randint) {
this.notiftime = notiftime;
this.notiftime2 = notiftime2;
this.duedatentime = duedatentime;
this.randint = randint;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public long getNotiftime() {
return notiftime;
}
public void setNotiftime(long notiftime) {
this.notiftime = notiftime;
}
public long getNotiftime2() {
return notiftime2;
}
public void setNotiftime2(long notiftime2) {
this.notiftime2 = notiftime2;
}
public long getDuedatentime() {
return duedatentime;
}
public void setDuedatentime(long duedatentime) {
this.duedatentime = duedatentime;
}
public int getRandint() {
return randint;
}
public void setRandint(int randint) {
this.randint = randint;
}
}
连同一个接口ItemEntryDao.java为:-
@Dao
interface ItemEntryDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
long[] insertItemEntries(ItemEntry... itemEntries);
@Insert(onConflict = OnConflictStrategy.IGNORE)
long insertItemEntry(ItemEntry itemEntry);
@Update(onConflict = OnConflictStrategy.IGNORE)
int updateItemEnrties(ItemEntry... itemEntries);
@Update(onConflict = OnConflictStrategy.IGNORE)
int updateItemEntry(ItemEntry itemEntry);
@Delete
int deleteItemEntries(ItemEntry... itemEntries);
@Delete
int deleteItemEntry(ItemEntry itemEntry);
@Query("SELECT * FROM ItemEntry")
List<ItemEntry> resetNotifications();
}
- @Query 等效于 Cursor 但返回 ItemEntry 对象的列表。
例如,上面可以使用(基本上复制您的代码,但将提取的数据输出到日志),例如:-
public void onHandleWork() {
ItemEntry ie = new ItemEntry();
ie.setNotiftime(100);
ie.setNotiftime2(200);
ie.setDuedatentime(500000);
ie.setRandint(567);
mDB.getItemEntryDao().insertItemEntry(ie);
List<ItemEntry> mylist = mDB.getItemEntryDao().resetNotifications();
for (ItemEntry itementry: mylist) {
Log.d("ITEMENTRY",
"\n\tnotiftime= " + String.valueOf(itementry.getNotiftime()) +
"\n\tnotiftime2= " + String.valueOf(itementry.getNotiftime2()) +
"\n\tduedatetime= " + String.valueOf(itementry.getDuedatentime()) +
"\n\trandint= " + String.valueOf(itementry.getRandint())
);
}
}
- mDB 是构建的对象(即@Database 类的一个实例)
这将导致(第一次运行):-
05-28 14:31:14.587 7211-7211/aso.so56326640 D/ITEMENTRY: notiftime= 100 notiftime2= 200 duedatetime= 500000 randint= 567
推荐阅读
- discord.py - discord.py 形成 DM 反馈
- node.js - 在nodejs循环内重复发送的请求
- installation - 我想在没有 CD-Rom 的笔记本上安装打印机 (Xerox WorkCentre 3025BI)
- java - 从 sd 卡恢复 Room Sqlite 数据库不起作用
- sql - 如何在每年年初将剩余年假列值更改为默认值?
- c# - 返回的 JSON 中未完全填充的响应 JObject 值
- python - 保存前的 Django 表单模型
- python - 所以我试图为我的机器人做出反应触发。失败是每当你的消息中有“sus”时,机器人就会对 sus 表情做出反应
- python-3.x - /ap1/checkbooks/ 'type' 对象的 TypeError 不可迭代;return [auth() for auth in self.authentication_classes] TypeError: 'type' object is
- css - Bootstrap 导航栏切换器图标颜色保持不变