Iam trying to figure out what RunInTransaction eventually does. Room documentation doesnt say much other than "Executes the specified Runnable in a database transaction".
From what i understand:
If we have an asynchronous operation like a query and then some insertion without the runInTransaction
roomDB.runInTransaction(new Runnable() {
#Override
public void run() {
query
}
});
insertions
insertions
The runInTransaction locks the database until the specified operation is completed. So at the first insertion the Thread pauses (please correct me) until the runInTransaction completes.
How can i control which procedure executes first?
But i think that database either way locks the tables and without using runInTransaction method. Please correct me if iam wrong.
Updated
#Dao
public interface RepoDao {
#Query("SELECT * FROM Table")
LiveData<List<Table>> getAll();
#Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(List<Table> table);
}
Main Activity
repo = ((BasicApp)getApplication()).getRepository();
repo.insertDataFromNetwork();
((BasicApp)getApplication()).getDatabase().repoMethods()
.getAll().observe(this, new Observer<List<Table>>() {
#Override
public void onChanged(#Nullable List<Table> message) {
Log.d("hello");
}
});;
insertDataFromNetwork
mDatabase.runInTransaction(new Runnable() {
#Override
public void run() {
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
mDatabase.repoMethods().insert(....);
}
}
});
A transaction symbolizes a unit of work performed within a database management system against a database. (Wikipedia)
It means that, if you, for example, will insert 10 users and update 10 other users not using runInTransaction method, Room will execute each insertion and update as a single action (transaction), and will update observers, who are observing changes on the Users table each time. While doing the same thing in runInTransaction method will perform all this changes as one action (transaction) and will notify listeners only once.
How can i control which procedure executes first?
Just run them sequentially in a single thread.
And don't run database transactions in the main thread.
Related
I've a Worker in which i first want to apply FFMPEG command before uploading it to server. As Worker is already running in background so to keep the result on hold until file uploads I've used RxJava .blockingGet() method. But I'm unable to understand that how to execute FFmpeg command synchronously by anyway i.e. RxJava etc. One tip that I found is to use ListenableWorker but it's documentation says that it stops working after 10 minutes. So, i don't want to go with that solution. Following is the method of FFmpeg just like any other async method. How can i make it synchronous or integrate it with RxJava? Any ideas would be appreciable.
ffmpeg.execute(command, new ExecuteBinaryResponseHandler() {
#Override
public void onFailure(String s) {
}
#Override
public void onSuccess(String s) {
uploadMediaItem(mediaUpload);
}
#Override
public void onProgress(String s) {
}
#Override
public void onStart() {
}
#Override
public void onFinish() {
// countDownLatch.countDown();
}
});
This is the flow of my Worker:
check pending post count in DB.
Pick first post and check if it has pending media list to upload.
Pick media recursively and check if editing is required on it or not.
Apply FFmpeg editing and upload and delete from DB.
Repeat the cycle until last entry in the DB.
Thanks
If you wanna create a syncronous job you need to use the CountDownLatch class (there is a comment in your code).
CountDownLatch is a syncronization object that can be used in cases like this.
As for now there isn't a valid method to have sync workers.
Listenable workers is useful when you want to monitor the worker itself from your app using a Livedata that return useful information (e.g. the status).
If I remember correctly the standard Worker class also descend from Listenable worker so you can use that.
In your case is useful to have two workers: the first apply a FFMPEG command, and the second worker that take the output of this command to do the network upload. Separating this two operations allows you to have more time for complete the two works (10 + 10).
In your case you can do something like this for the first worker:
private final CountDownLatch syncLatch = new CountDownLatch(1);
...ctor
doWork(){
//your asyncronous call
...
#Override
public void onFinish() {
//you need to save error status into a onSuccess and onFailure
syncLatch.countDown();
}
...
//end
syncLatch.await();
...
//evaluate if there are errors
...
//create output to pass to the next worker
Data outputData = ...
//pass the result to second worker, remember that onfailure will stop all subsequent workers
if(error==true)
{
return Result.failure(outputData);
}else{
return Result.success(outputData);
}
}
For the second worker you can do the same according to your upload function behavihour to syncronize the call.
Hope this help.
Cheers.
I have a method that loads data from Firebase into ArrayList. After this,I use that ArrayList to construct RecyclerView. I've decided to load data on another thread. Below is my code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_just);
citiesRecyclerView =
(RecyclerView)findViewById(R.id.recyclerView);
handler = new Handler()
{
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what==1)
{
cityAdapter = new
CityAdapter(MainActivity.this,cities) ;
citiesRecyclerView.setAdapter(cityAdapter);
}
}
};
t = new Thread(new Runnable() {
#Override
public void run() {
//method that loads data into List.If this method was
//successfully done,then I send message 1 to Handler
loadDataFromFirebase();
}
});
t.start();
//other operations below
}
Hope,that everything understandable. Code works fine. And my problem is that I need to use loadDataFromFirebase method in thread again. I wanted to call t.start() again in order to call loadDataFromFirebase method,but there was error that thread already started. I checked that by writing this code:
if(t.getState()== Thread.State.NEW)
t.start();
else
someMethod();
else statement worked above.
And my questions are:
1) Does loadDataFromFirebase method work really on another thread by this way?
2) How to call loadDataFromFirebase method again in another thread, if something happened? Do I need to create another variable for Thread again?
It's not a good idea to handle all low-level thread work by your own.
Accroding to Android you could:
Use AsyncTask (but notice that they have many drawbacks such as context leak in some cases etc),
I could suggest you to get into RxJava - it's a painless way to use async work in your app.
To 'download' data from Firebase you could probably use FCM (push notifications) to load data on demand.
And what about your question:
"It is never legal to start a thread more than once. In particular, a thread may not be restarted once it has completed execution."(c) http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html#start()
If you are using firebase SDK you can use realtime database feature, so do not need to query it each time.
You should just subscribe one time and get updates. For example:
firebaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
YourDataObject value = dataSnapshot.getValue(YourDataObject.class);
Log.d(TAG, "Value is: " + value);
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
You can read docs here.
I'm migrating the following pattern of accessing the Android app's SQLite database into the RxJava world:
public List<Stuff> doStuff(){
synchronized (lock) {
open(); // this effectively checks for isOpen() then calls getWritableDatabase()
// query the database for stuff
close(); // SQLiteOpenHelper close method
return stuffList;
}
}
Something I'm struggling is when should I close the database connection? I know there are patterns for not closing the connection at all as well as closing the connection as part of the Activity method. However, those patterns would require me applying the logic to the whole database manager class which I'd like to avoid if possible. Was hoping maybe there's a suggested way to handle this with RxJava and specifically SqlBright wrapper? My migrated code looks something like this:
public Observable<List<Stuff>> doStuff(){
synchronized (lock) {
open();
String sql = <..>;
return db.createQuery(tableName, sql, args).mapToList(mStuffMapper);
// where do I close()?
}
}
The solution I'm after, ideally, should allow me to change this one method, keeping the rest with the current open/close pattern.
You can use Subscription to close the connection.
db.createQuery(tableName, sql, args)
.mapToList(mStuffMapper);
.doOnSubscribe(new Action0() {
#Override public void call() {
close();
}
});
Subscription subscribe = doStuff().subscribe();
subscribe.unsubscribe();
I'm learning RxJava so please be gentle. I've watched the tutorials, done the reading, searched SO, however, I'm still having some problems transforming my AsyncTaskLoader. For some reason, I can't find a pattern of operators to achieve my task (although I think it's a common one). What I'm trying to do is the following: return an Observable my fragment could subscribe to. The observable should do the following on subscribe:
1) Fetch data from the local database by doing 2 queries, running some logic and returning results;
2) Fetching data from API;
3) Synchronising the new API data with the database;
4) Repeating step one and returning results;
So far I've transformed my db calls and my API calls to return observables. I'm trying to understand how I can emit the cold results and continue with the chain. I could probably keep the two operations separately, and use the same subscriber to subscribe to both? But I'm not sure how that would work if my new loader-replacement class returns an observable... Also I don't really need to process the results from the second observable - I just need for the first one to replay when the second one finished.
So far I have the following:
public Observable<StuffFetchResult> getColdStuff() {
return Observable.zip(mDataSource.listStuff(), mDataSource.listOtherStuff(),
(stuff, moreStuff) -> {
List<Stuff> mergedList = new ArrayList<>();
// do some merging stuff
return new StuffFetchResult(mergedList);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Assume I also have getHotStuff() that will do the API call and the synchronisation with the database, if that's the right approach, and return the same Observable. However, I'm stuck on the next step - how can I restart the first observable to replay once hotStuff has completed, without adding another subscriber?
EDIT:
I've made some progress and I think all I need now is to join it all up. I have my two methods:
1) getColdStuff() is pretty much as described above
2) getHotStuff() will do call to the API, synchronise with the database, and return an Observable. The idea was to call getColdStuff() again after getHotStuff() has finished in order to refresh the UI, so actual result returned from getHotStuff() can be ignored. All it needs to do is to trigger getColdStuff() once done.
I've tried the suggestion in the answer to and created the following:
BehaviorRelay<Observable<StuffFetchResult>> callSequence = BehaviorRelay.create();
Observable<StuffFetchResult> valueSequence = Observable.switchOnNextDelayError(callSequence.toSerialized());
valueSequence.subscribe(new Subscriber<StuffFetchResult>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(StuffFetchResult result) {
// UI stuff
}
});
callSequence.call(loader.getColdStuff());
I can subscribe to valueSequence here and use callSequence.call(loader.getColdStuff());, which will run the first method and produce results in onNext() of my subscription, which I can use for my UI. However, I'm not sure how to run getHotStuff() in parallel and also do a different action on it when it returns. Also getHotStuff() returns a different type of Observable so I can't really use the same callSequence?
EDIT 2
Using two subscribers, I can achieve the required behaviour I think. Not really sure if that's the right way to go about it though.
loader.getHotStuff()
.subscribeOn(Schedulers.io())
.subscribe( new Subscriber<Object>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(Object stuffWeDontCareAbout) {
callSequence.call(loader.getColdStuff());
}
});
if i understand your scenario correctly, you may want something like that -
BehaviorSubject<Observable<T> callSequence = BehaviorSubject.create();
Observable<T> valueSequence = Observable.swithOnNextDelayError(callSequence.toSerialized());
your subscriber will be listening to the valueSequence, and whenever you need to "restart", you will call this -
callSequence.onNext(call.cache()); // *call* is Observable<T>
(i leave the .subscribeOn/.observeOn configuration to you)
I'm designing out a module in Android that does some processing and then writes to the database using ORMLite transactions. In particular, my background code will be something like:
public class BackgroundOperation implements Runnable {
#Override
public void run() {
//Do some stuff
//Write to the database in a transaction
try {
ORMHelper h = ORMHelper.getDefaultOrmHelper();
final MyModel modelObj = h.myModelDao.queryForId(someId);
TransactionManager.callInTransaction(
h.getConnectionSource(),
new Callable<Void>() {
public Void call() throws Exception {
modelObj.col1 = 10;
modelObj.col2 = "hello";
h.myModel2Dao.update(modelObj);
h.myModel2Dao.create(new MyModel2("a", "b"));
return null;
}
}
);
}
catch (Exception e) {
return null;
}
}
}
This runnable will then be executed by being submitted to a ThreadPoolExecutor. I want to be able to cancel the background thread if needed and am trying to make sure that if the operation is cancelled, then the transaction will simply fail and do nothing. For example, if I do this:
Future f = myThreadPoolExecutor.submit(new BackgroundOperation());
//Some time later
f.cancel(true);
Can I be sure that it will be an all or nothing deal with the transaction in ORMLite. That is, there is no cleanup needed and my modelObj will have either both col1 and col2 set or neither set? Do I have to do anything special when catching the InterruptedException in the Runnable to handle the case when a task is cancelled in this way, or can I simply exit?
If you call f.cancel(true), all that does is interrupt the Thread which causes wait(), sleep(), and some other methods to throw InterruptedException. It will not cancel the database transaction underway.
If you want, you can check for the interrupted bit in the middle of your IO operations:
h.myModel2Dao.update(modelObj);
if (Thread.currentThread().isInterrupted()) {
throw new RuntimeException("Thread was interrupted");
}
h.myModel2Dao.create(new MyModel2("a", "b"));
For more information about what happens when a thread is interrupted see here:
What does java.lang.Thread.interrupt() do?
Transactions are for when you are updating multiple objects as a single unit or writing to multiple tables. See the documentation about transactions which has an example of updating an Account and an Order inside of a transaction.
Also, you do not need to use a transaction if you are updating multiple fields in the same row. The update statement is considered to be a single unit and the database should ensure that the row gets updated atomically. Only if you are updating multiple different rows either in the same table or in separate tables do you need a transaction.
ORMLite will utilize the sqlite transactions under the covers. This is most likely a double phase commit which only allows you to commit a transaction as an entire unit.
In short, you can be assured that col1 and col2 will only be modified as a single atomic unit. Also, it if is interrupted the commit will fail and the changes to col1 and col2 will be rolled back.