Fixed
Status Update
Comments
yb...@google.com <yb...@google.com> #2
For one of observables (single, maybe) we don't specify a subscvription thread so that developer can control where the action happens.
For flowables / observables; we always use the Room IO thread fo rthe actual query since we may repeat the work.
For LiveData, ti si always the Room IO Thread.
Until we can extend Room Query methods to receive non-query parameter (e.g. a Scheduler) we cannot do this.
At least room catches this instantly since it will crash w/ db access on main thread exception.
For flowables / observables; we always use the Room IO thread fo rthe actual query since we may repeat the work.
For LiveData, ti si always the Room IO Thread.
Until we can extend Room Query methods to receive non-query parameter (e.g. a Scheduler) we cannot do this.
At least room catches this instantly since it will crash w/ db access on main thread exception.
[Deleted User] <[Deleted User]> #3
Hello Yigit, nice to meet you here! :)
Probably I should have described the problem more clearly.
I'm using Flowable, and hopefully it executes queries in background thread (at least, app does not crash with IllegalStateException, as with Single).
But nevertheless subscribing and disposing(!) does cause StrictMode violations. Stack trace examples:
D/StrictMode: StrictMode policy violation; ~duration=17 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1111)
at android.database.sqlite.SQLiteConnection.applyBlockGuardPolicy(SQLiteConnection.java:1043)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:552)
at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323)
at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:426)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:465)
at androidx.room.InvalidationTracker.addObserver(InvalidationTracker.java:249)
at androidx.room.RxRoom$1.subscribe(RxRoom.java:79)
at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:71)
at io.reactivex.Flowable.subscribe(Flowable.java:14479)
at io.reactivex.internal.operators.flowable.FlowableObserveOn.subscribeActual(FlowableObserveOn.java:56)
at io.reactivex.Flowable.subscribe(Flowable.java:14479)
at io.reactivex.internal.operators.flowable.FlowableFlatMapMaybe.subscribeActual(FlowableFlatMapMaybe.java:54)
at io.reactivex.Flowable.subscribe(Flowable.java:14479)
at io.reactivex.Flowable.subscribe(Flowable.java:14416)
at io.reactivex.Flowable.subscribe(Flowable.java:14306)
at gmk57.strictroom.MainActivity.onCreate(MainActivity.java:30)
StrictMode policy violation; ~duration=108 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1111)
at android.database.sqlite.SQLiteConnection.applyBlockGuardPolicy(SQLiteConnection.java:1043)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:552)
at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323)
at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:426)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:465)
at androidx.room.InvalidationTracker.removeObserver(InvalidationTracker.java:281)
at androidx.room.RxRoom$1$2.run(RxRoom.java:83)
at io.reactivex.disposables.ActionDisposable.onDisposed(ActionDisposable.java:30)
at io.reactivex.disposables.ActionDisposable.onDisposed(ActionDisposable.java:19)
at io.reactivex.disposables.ReferenceDisposable.dispose(ReferenceDisposable.java:43)
at io.reactivex.internal.disposables.DisposableHelper.dispose(DisposableHelper.java:124)
at io.reactivex.internal.disposables.SequentialDisposable.dispose(SequentialDisposable.java:72)
at io.reactivex.internal.operators.flowable.FlowableCreate$BaseEmitter.cancel(FlowableCreate.java:301)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.cancel(FlowableObserveOn.java:154)
at io.reactivex.internal.operators.flowable.FlowableFlatMapMaybe$FlatMapMaybeSubscriber.cancel(FlowableFlatMapMaybe.java:158)
at io.reactivex.internal.subscriptions.SubscriptionHelper.cancel(SubscriptionHelper.java:189)
at io.reactivex.internal.subscribers.LambdaSubscriber.cancel(LambdaSubscriber.java:119)
at io.reactivex.internal.subscribers.LambdaSubscriber.dispose(LambdaSubscriber.java:104)
at gmk57.strictroom.MainActivity.onDestroy(MainActivity.java:37)
Probably I should have described the problem more clearly.
I'm using Flowable, and hopefully it executes queries in background thread (at least, app does not crash with IllegalStateException, as with Single).
But nevertheless subscribing and disposing(!) does cause StrictMode violations. Stack trace examples:
D/StrictMode: StrictMode policy violation; ~duration=17 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1111)
at android.database.sqlite.SQLiteConnection.applyBlockGuardPolicy(SQLiteConnection.java:1043)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:552)
at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323)
at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:426)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:465)
at androidx.room.InvalidationTracker.addObserver(InvalidationTracker.java:249)
at androidx.room.RxRoom$1.subscribe(RxRoom.java:79)
at io.reactivex.internal.operators.flowable.FlowableCreate.subscribeActual(FlowableCreate.java:71)
at io.reactivex.Flowable.subscribe(Flowable.java:14479)
at io.reactivex.internal.operators.flowable.FlowableObserveOn.subscribeActual(FlowableObserveOn.java:56)
at io.reactivex.Flowable.subscribe(Flowable.java:14479)
at io.reactivex.internal.operators.flowable.FlowableFlatMapMaybe.subscribeActual(FlowableFlatMapMaybe.java:54)
at io.reactivex.Flowable.subscribe(Flowable.java:14479)
at io.reactivex.Flowable.subscribe(Flowable.java:14416)
at io.reactivex.Flowable.subscribe(Flowable.java:14306)
at gmk57.strictroom.MainActivity.onCreate(MainActivity.java:30)
StrictMode policy violation; ~duration=108 ms: android.os.StrictMode$StrictModeDiskWriteViolation: policy=31 violation=1
at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk(StrictMode.java:1111)
at android.database.sqlite.SQLiteConnection.applyBlockGuardPolicy(SQLiteConnection.java:1043)
at android.database.sqlite.SQLiteConnection.execute(SQLiteConnection.java:552)
at android.database.sqlite.SQLiteSession.beginTransactionUnchecked(SQLiteSession.java:323)
at android.database.sqlite.SQLiteSession.beginTransaction(SQLiteSession.java:298)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:505)
at android.database.sqlite.SQLiteDatabase.beginTransaction(SQLiteDatabase.java:416)
at androidx.sqlite.db.framework.FrameworkSQLiteDatabase.beginTransaction(FrameworkSQLiteDatabase.java:69)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:426)
at androidx.room.InvalidationTracker.syncTriggers(InvalidationTracker.java:465)
at androidx.room.InvalidationTracker.removeObserver(InvalidationTracker.java:281)
at androidx.room.RxRoom$1$2.run(RxRoom.java:83)
at io.reactivex.disposables.ActionDisposable.onDisposed(ActionDisposable.java:30)
at io.reactivex.disposables.ActionDisposable.onDisposed(ActionDisposable.java:19)
at io.reactivex.disposables.ReferenceDisposable.dispose(ReferenceDisposable.java:43)
at io.reactivex.internal.disposables.DisposableHelper.dispose(DisposableHelper.java:124)
at io.reactivex.internal.disposables.SequentialDisposable.dispose(SequentialDisposable.java:72)
at io.reactivex.internal.operators.flowable.FlowableCreate$BaseEmitter.cancel(FlowableCreate.java:301)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.cancel(FlowableObserveOn.java:154)
at io.reactivex.internal.operators.flowable.FlowableFlatMapMaybe$FlatMapMaybeSubscriber.cancel(FlowableFlatMapMaybe.java:158)
at io.reactivex.internal.subscriptions.SubscriptionHelper.cancel(SubscriptionHelper.java:189)
at io.reactivex.internal.subscribers.LambdaSubscriber.cancel(LambdaSubscriber.java:119)
at io.reactivex.internal.subscribers.LambdaSubscriber.dispose(LambdaSubscriber.java:104)
at gmk57.strictroom.MainActivity.onDestroy(MainActivity.java:37)
yb...@google.com <yb...@google.com> #4
oh this is bad :/ we should fix it, thanks!
[Deleted User] <[Deleted User]> #5
Looks like it is broken for both add observer and remove observer since both are sync.
We need to investigate this better because these methods were not @WorkerThread before and we had to change them to fix some invalidation problems (see internal bug b/73592149 ).
Luckily this is not an issue for LiveData since it uses ComputableLiveData and always calls observe on a background thread.
We need to investigate this better because these methods were not @WorkerThread before and we had to change them to fix some invalidation problems (see internal bug
Luckily this is not an issue for LiveData since it uses ComputableLiveData and always calls observe on a background thread.
yb...@google.com <yb...@google.com> #6
Project: platform/frameworks/support
Branch: androidx-master-dev
commit 79deed62f2188a6ce2d21244dd28c3dc8ab6d0de
Author: Daniel Santiago Rivera <danysantiago@google.com>
Date: Wed Oct 17 11:07:39 2018
Specify subscribeOn and unsubscribeOn scheduler in RxRoom.
Setting subscriber and unsubscriber schedulers prevents triggers being
synced in the main thread when subscribing or disposing a RxRoom
flowable / observable.
Moved beginTransaction() calls in InvalidationTracker outside the try,
finally with endTransaction() so that exceptions thrown by
beginTransaction() aren't aborted nor forgotten.
Bug: 117201279
Test: Manual test with sample app.
Change-Id: Iefb33dc63cf78e125a8805bd66a26ada365d8453
A room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/InvalidationTrackerTest.java
M room/runtime/src/main/java/androidx/room/InvalidationTracker.java
M room/rxjava2/src/main/java/androidx/room/RxRoom.java
M room/rxjava2/src/test/java/androidx/room/RxRoomTest.java
https://android-review.googlesource.com/792524
https://goto.google.com/android-sha1/79deed62f2188a6ce2d21244dd28c3dc8ab6d0de
Branch: androidx-master-dev
commit 79deed62f2188a6ce2d21244dd28c3dc8ab6d0de
Author: Daniel Santiago Rivera <danysantiago@google.com>
Date: Wed Oct 17 11:07:39 2018
Specify subscribeOn and unsubscribeOn scheduler in RxRoom.
Setting subscriber and unsubscriber schedulers prevents triggers being
synced in the main thread when subscribing or disposing a RxRoom
flowable / observable.
Moved beginTransaction() calls in InvalidationTracker outside the try,
finally with endTransaction() so that exceptions thrown by
beginTransaction() aren't aborted nor forgotten.
Bug: 117201279
Test: Manual test with sample app.
Change-Id: Iefb33dc63cf78e125a8805bd66a26ada365d8453
A room/integration-tests/testapp/src/androidTest/java/androidx/room/integration/testapp/test/InvalidationTrackerTest.java
M room/runtime/src/main/java/androidx/room/InvalidationTracker.java
M room/rxjava2/src/main/java/androidx/room/RxRoom.java
M room/rxjava2/src/test/java/androidx/room/RxRoomTest.java
Description
Version used: 2.0.0
Devices/Android versions reproduced on: N/A
In current version of room if I mark method in DAO with @Transaction, and if it uses any disposable e.g. Single, the generated code will throw main thread access exception at runtime.
I would expect one of two behaviours instead:
1. It would be ideal if room could somehow defer start/end transaction to match thread that is used on Disposable, but I can imagine that this can be not possible.
2. To get compile time warning or error that this may be not the correct way of using @Transaction with mix of asynchronous code.
Example of code:
Method in abstract DAO:
@Transaction
open fun upsert(obj: T): Single<Boolean> {
return insert(obj)
.map { true }
.onErrorResumeNext {
if (it is SQLiteConstraintException)
{ update(obj)
.map { true }
} else {
Single.error(it) }
}
.subscribeOn(Schedulers.io()) }
Generated method:
@Override
public Single<Boolean> upsert(Supplier obj) {
__db.beginTransaction();
try {
Single<Boolean> _result = super.upsert(obj);
__db.setTransactionSuccessful();
return _result;
} finally {
__db.endTransaction();
}
}