Update UI on insertions to Database using RxJAVA and DAO - java

I have a ChatThreadsActivity() which displays messages between two people.
Whenever the user opens the activity, I make an API to fetch the most recent 100 messages. So in the onCreate() method I call makeApiRequestToGetChatThread().
I know I am doing alot of things wrong here.
Inside onNext() I add rows to the chatThreadAdapter. I know this is wrong since I update adapter after ever insert.
chatDaoObject.queryChatThreadsFromDB(someId).observeOn(AndroidSchedulers.mainThread()).subscribe()
Should this be anonymously called? When will this be unsubscribed if ever?
How do I unsubscribe from it?
I've read about BackPressure and realize this is it, I've run this on Android Monitor and here's how I can identify it. Am I doing this right?
private void makeApiRequestToGetChatThreads() {
public void onResponse(Call call, final Response response) {
final String responseString = response.body().string();
runOnUiThread (() -> {
final JSONArray array = new JSONArray(responseString);
JSONObject obj;
for (int i=0 ; i < array.length ; i++) {
obj = new JSONObject(array.get(i));
insertAChatIntoDB (obj);
}
}
});
}
private void insertAChatIntoDB(JSONObject o) {
if(insertSubscriber != null) {
insertSubscriber.unsubscribe();
}
insertSubscriber = new Subscriber<Long>() {
public void onCompleted() {
}
public void onError() {
}
public void onNext() {
chatDaoObject.queryChatThreadsFromDB(someId)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<ChatObject>>() {
#Override
public void call(List<ChatObject> chatList) {
if (chatList != null) {
//Royal Screw up
//After every insertion in the DB I update the UI
chatThreadAdapter.addAllChatThreadsIntoList(chatList);
//Notify is called inside the above below
//notifyItemRangeChanged(initialPosition,chatList.size())
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
}
});
}
};
try {
//Inserts happening on Schedulers.computation()
chatDaoObject.insertAChatInDB(o).observeOn(Schedulers.computation()).subscribe(insertSub);
} catch (JSONException e) {
e.printStackTrace();
Crashlytics.logException(e);
}
}
I use RxJAVA and SQLBrite Dao and here's what the query looks like:
//In `ChatDao` class
ChatDataDao extends Dao {
...
...
public Observable<long> insertAChatInDB(JSONObject o) {
ChatThreadsTable.ContentValuesBuilder valuesBuilder = ChatThreadsTable.contentValues();
...
//populating columns values
...
return insert(ChatThreadsTable.TABLE_NAME, valuesBuilder.build(), SQLiteDatabase.CONFLICT_IGNORE);
}
public Observable<List> queryChatThreadsFromDB () {
return rawQuery("SELECT * FROM " + ChatThreadsTable.TABLE_NAME).run().mapToList(ChatObjectMapper.MAPPER);
}
...
...
}
Edit:
Is this the right way to query db without worrying about subscription/unsubscription?
rawQuery("SELECT * FROM " + SomeObject.TABLE_NAME + " ORDER BY " + SomeObject.COL1 + " , " +SomeObject.COL2 + " DESC").run().mapToList(SomeObjectMapper.MAPPER)
.flatMap(new Func1<List<SomeObject>, Observable<SomeObject>>() {
#Override
public Observable<SomeObject> call(List<SomeObject> SomeObjects) {
return Observable.from(SomeObjects);
}
}).doOnNext(new Action1<SomeObject>() {
#Override
public void call(SomeObject chatThreadObject) {
}
}).subscribe();

what is going on with all these tutorials telling people to create their own Subscribers? Here's a cleaned up version:
private void makeApiRequestToGetChatThreads() {
// ... some call that calls back onResponse
}
public void onResponse(Call call, final Response response) {
// are you sure this is how you parse a JSON String?
Observable
.from(response.body().string())
.observeOn(Schedulers.computation())
.flatMapIterable(JsonArray::new)
.map(JSONObject::new)
.flatMap(chatDaoObject::insertAChatInDB)
.flatMap(chatDaoObject::queryChatThreadsFromDB)
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(chatThreadAdapter::addAllChatThreadsIntoList)
.subscribe(dummy -> {}, throwable -> {
e.printStackTrace();
Crashlytics.logException(e);
});
}
It's quite likely that there's a better way to directly make the network call return an Observable. Check your documentation / peers.
When the Observable completes all processing and UI updates, it will unsubscribe on it's own. However, if the network call takes a while to complete, the user might have already switched screens/apps, and the UI manipulation will break your app. Consider saving the subscription in your view and unsubscribing. Hint: creating the subscription in the onResponse is... not optimal.
You are getting 100s of threads, and updating the UI for each and every one of them. Are you sure you want that?
I don't think you need to care about backpressure.

Related

Android RxJava 2: What is the difference between fromCallable and Just?

I've been experimenting with RxJava in Android, but I'm trying to figure out the difference between fromCallable and Just. Both receive data just once, versus Create which can receive data multiple times. Here is the code that I'm using to experiment:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listenCallable();
listenJust();
}
private void listenCallable() {
CompositeDisposable compositeDisposable = new CompositeDisposable();
Disposable disposable = Shooter.getCallable().subscribe(i -> {
Log.d("Tag", "Listen Callable: " + i);
});
compositeDisposable.add(disposable);
compositeDisposable.dispose();
}
private void listenJust() {
CompositeDisposable compositeDisposable = new CompositeDisposable();
Disposable disposable = Shooter.getJust("Jay").subscribe(i -> {
Log.d("Tag", "Listen Just " + i);
});
compositeDisposable.add(disposable);
compositeDisposable.dispose();
}
}
and the class which is emitting data:
public class Shooter {
public static Observable<String> getCallable() {
return Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
try {
Thread.sleep(500);
} catch (Exception e) {}
return "Callable Results";
}
});
}
public static Observable<String> getJust(String input) {
try {
Thread.sleep(500);
} catch (Exception e) {}
return Observable.just("Just Results " + input);
}
}
Observable.fromCallable( is analogous to
Observable.defer {
try {
Observable.just(...)
} catch(e: Throwable) {
Observable.error(e)
}
}
Therefore, just runs synchronously, while fromCallable can be deferred to another Scheduler with subscribeOn (and executed "later").
As an addition to #EpicPandaForce:
Observable#just is eager, Observable#fromCallable is lazy.
Example eager:
Observable.just(methodCall()) -> first methodCall will be evaluted during assembly-time. methodCall()-Value will be captured and used as input-parameter for Observable#just. This value will be stored in the observable. Everything to this point has already happend without any subscription. When subscribing to created Observable the captured value will be replayed on each subscription.
Example lazy:
Observable.fromCallable(() -> methodCall()) -> during assembly-time the observable will be created with a reference to the lambda. methodCall() will not be invoked during assembly-time. The lambda execution will happen on every subscription.
If you want to defer expensive work until the first subscription happens you would use Observable#defer / Observable#fromCallable. When dealing with sync. values, which are already present or are very expensive to created, you would use Observable#just.
Also please have a look at https://github.com/ReactiveX/RxJava#assembly-time (Assembly time/ Subscription time/ Runtime)

RxJava + Retrofit + Realm is doing unlimited get request

I am completely new to rxJava and it's really confusing, I want to make my app offline first and I've decided to use Realm and Retrofit, First I want to get the data from retrofit and then get the data from my remote webservice then, use realm's insertOrUpdate to merge the remote objects with the local one. I'm able to get on this process so far but when I looked into my Network requests on stetho, this method is complete requesting infinite times. Where did I go wrong? Here's the function
public Observable<RealmResults<Event>> all() {
Realm realm = Realm.getDefaultInstance();
return realm.where(Event.class).findAllAsync()
.asObservable()
.filter(new Func1<RealmResults<Event>, Boolean>() {
#Override
public Boolean call(RealmResults<Event> events) {
return events.isLoaded();
}
})
.doOnNext(new Action1<RealmResults<Event>>() {
#Override
public void call(RealmResults<Event> events) {
service.getEvents()
.subscribeOn(Schedulers.io())
.subscribe(new Action1<List<Event>>() {
#Override
public void call(final List<Event> events) {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.insertOrUpdate(events);
}
});
} // auto-close
}
});
}
});
}
and here's the function on my activity, where I use it
private void getEvents() {
Log.i("EVENTSELECTION", "STARTING");
repository.all()
.subscribe(new Subscriber<List<Event>>() {
#Override
public void onCompleted() {
Log.i("EVENTSELECTION", "Task Completed");
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onError(Throwable e) {
Log.e("EVENTSELECTION", e.getMessage());
swipeRefreshLayout.setRefreshing(false);
e.printStackTrace();
}
#Override
public void onNext(List<Event> events) {
Log.i("EVENTSELECTION", String.valueOf(events.size()));
}
});
}
Thank you so much.
Where did I go wrong?
Let's go through it:
1.
public Observable<RealmResults<Event>> all() {
Realm realm = Realm.getDefaultInstance();
This opens a Realm instance that will never be closed. So your Realm lifecycle management is wrong, refer to the documentation for best practices.
2.
return realm.where(Event.class).findAllAsync()
.asObservable() // <-- listens for changes in the Realm
// ...
.doOnNext(new Action1<RealmResults<Event>>() {
#Override
public void call(RealmResults<Event> events) {
service.getEvents() // <-- downloads data
.subscribeOn(Schedulers.io())
.subscribe(new Action1<List<Event>>() {
You basically say that "in case there are any changes made to data in Realm, then download data from the service and write it into the Realm"
Which will trigger the RealmChangeListener which will trigger a download and so on.
This is a conceptual error, you're using Realm notifications incorrectly.
RealmResults<T> is not just a list of objects, it is also a subscription for changes. So you need to keep it as a field reference, and "stay subscribed to changes in the database".
RealmResults<Sth> results;
RealmChangeListener<RealmResults<Sth>> changeListener = (element) -> {
if(element.isLoaded()) {
adapter.updateData(element);
}
};
void sth() {
results = realm.where(Sth.class).findAllSortedAsync("id");
results.addChangeListener(changeListener);
}
void unsth() {
if(results != null && results.isValid()) {
results.removeChangeListener(changeListener);
results = null;
}
}
In your case, RealmResults<T> which symbolizes a subscription and also provides access to the current/new data is wrapped as an Observable<T> which you can create subscribers to.
Observable<List<<Sth>> results;
Subscription subscription;
Action1<List<Sth>> changeListener = (element) -> {
if(element.isLoaded()) {
adapter.updateData(element);
}
};
void sth() {
results = realm.where(Sth.class).findAllSortedAsync("id").asObservable();
subscription = results.subscribe(changeListener);
}
void unsth() {
if(subscription != null && !subscription.isUnsubscribed()) {
subscription.unsubscribe();
subscription = null;
results = null;
}
}
As you can see, you have a subscription at the start of the component, and an unsubscription at the end of the component.
Calling Observable.first() is incorrect, it does not make sense to do that. If you saw it in any tutorial (I've seen it before...), then that tutorial was wrong.
So it's really a by design on realm and it won't call the onCompleted, I added a .first() at the end of my getEvents function to get only the first result.

Observable is not asynchronous

I am learning RxJava and am testing a scenario where I read data from a DB and then post it to a Queue. I just made a sample mock of the whole process but I don't seem to find the Observable working as I wanted it to ie. asynchronously.
This is my code:
package rxJava;
import java.util.ArrayList;
import java.util.List;
import rx.Observable;
import rx.Observer;
import rx.functions.Action1;
public class TestClass {
public static void main(String[] args) {
TestClass test = new TestClass();
System.out.println("---START---");
test.getFromDB().subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.out.println("Publish complete.");
}
#Override
public void onError(Throwable t) {
System.out.println(t.getMessage());
}
#Override
public void onNext(String s) {
test.publishToQueue(s).subscribe(new Observer<Boolean>() {
#Override
public void onNext(Boolean b) {
if (b) {
System.out.println("Successfully published.");
}
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable arg0) {
}
});
};
});
System.out.println("---END---");
}
public Observable<String> getFromDB() {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 30; i++) {
list.add(Integer.toString(i));
}
return Observable.from(list).doOnNext(new Action1<String>() {
#Override
public void call(String temp) {
if (temp.contains("2")) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
public Observable<Boolean> publishToQueue(String s) {
return Observable.defer(() -> {
try {
if (s.contains("7")) {
Thread.sleep(700);
}
System.out.println("Published:: " + s);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Observable.just(true);
});
}
}
Suppose I get a list from the DB asynchronously and want to post it to the queue,. I have used an Observable returned from getFromDB and have subscribed to it which mimics the data I get from DB. Every time I get the data from DB , I want to push it to a queue using publishToQueue which also returns an Observable. I wanted to make the queue call also asynchronous. Now on positive acknowledgement from the queue such as the Boolean which I am returning (Observable<Boolean>), I want to print something.
So basically I just want both the processes to be asynchronous. For every data from DB, I push it to the Queue asynchronously.
I have added Thread.sleep() in both the methods, db call and queue so as to mimic a delay and to test the asynchronous operations. I think this is what causing the problem. But I also tried Obseravable.delay() but that doesn't even produce any output.
Please help me understand how this works and how I can make it work as I want it to.
You have to specified subscribeOn value.
Observable.just("one", "two", "three", "four", "five")
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(/* an Observer */);
http://reactivex.io/documentation/operators/subscribeon.html
By default, RxJava is synchronous. It means that everything will be perform in the same thread (and the current thread), by default. You can perform tasks in another thread thanks to observeOn / subscribeOn methods, or using some operators that perform tasks in another job (because it use another scheduler, like delay, interval, ...)
In your example, you have to explitly set in which scheduler the subscription will pe performed. (here, in which thread Observable.from will emit your list)
test.getFromDb()
.subscribeOn(Schedulers.io())
.subscribe();
Then you can use the flatMap operator and calling your publishToQueue method. This method will be executed in the previous scheduler, but you can force it to use another scheduler, thanks to observeOn method. Everything after the observeOn method will be executed in another thread.
test.fromDb()
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.flatMap(l -> test.publishToqueue(l))
.subscribe();

RxJava - check condition and repeat once only if condition is true

I use RxJava + Retrofit to make API calls in my Android app. There may be cases when user makes a request and his token is expired. In this cases I receive a normal response in my onNext, but the response contains not the result but an error element with some code. If such thing happens I need to re-login the user and only after getting a new token repeat the original request.
So I want to organize this using RxJava.
To make things easier I will bring a simple example. Let's say I have the following method:
public void test(int someInt){
Observable.just(someInt)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
log("onCompleted");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
log("onError");
}
#Override
public void onNext(Integer integer) {
log("onNext - " + integer);
}
});
I want to check if (someInt == 0) before onNext() is called. If I get false I want to continue and get onNext() called, but if I get true I want to perform some action and repeat the original observable only once, if the condition returns false second time I don't want to repeat again.
Can someone help me to figure out what options do I have for this?
P.S. I am new in RX world.
Here you go. Since you want to retry the whole chain .retryWhen is great for it so you have to "play" a bit with the errors.
Below if you detect a invalid token, you pass an error (only on the first time) which the retryWhen will catch and resubscribe to the whole rx chain (starting from Observable.just(someInt)).
haveRetriedOnce = false;
Observable.just(someInt)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(integer ->{
if(integer == 0){
if(haveRetriedOnce){
return Observable.error(new UserOperationException());
}
// problem, throw an error and the .retryWhen will catch it
return Observable.error(new InvalidTokenException());
}else{
return Observable.just(integer);
}
})
.retryWhen(observable -> observable.flatMap(throwable->{
if(throwable instanceOf InvalidTokenException){
haveRetriedOnce = true;
return just(0); // retry, the int here is irrelevant
}else{
// other error, pass it further
return Observable.error(throwable);
}
}))
.subscribe(new Subscriber<Integer>() {
#Override
public void onCompleted() {
log("onCompleted");
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
log("onError");
}
#Override
public void onNext(Integer integer) {
log("onNext - " + integer);
}
}

Avoid using CountDownLatch to wait for many threads before sending the callback

So I have a list of Track Ids that for each track Id I need to execute a network request to get the track details, I am using a for loop to launch all the requests and a latch to wait for all the requests to be completed. After they are completed then the callback is sent with the List of Tracks that have already populated.
I would like to know if there is any better way to do this, maybe with RxJava ?
I am using Retrofit 2.0 in Android.
public IBaseRequest batchTracksById(final TrackIdList trackIdListPayload, final IRequestListener<TracksList> listener) {
final TracksList tracks = new TracksList();
final Track[] trackArray = newrack[trackIdListPayload.getTrackIds().length];
tracks.setTrack(trackArray);
final CountDownLatch latch = new CountDownLatch(trackArray.length);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
latch.await();
handler.post(new Runnable() {
#Override
public void run() {
listener.onRequestUpdate(null, tracks, null, true);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
for (String id : trackIdListPayload.getTrackIds()) {
getTrackById(id, new IRequestListener<Track>() {
#Override
public void onRequestFailure(IBaseRequest request, Exception exception) {
latch.countDown();
}
#Override
public void onRequestUpdate(IBaseRequest request, Track track, RequestState state, boolean requestComplete) {
//iterate through the tracks and update the ones in the thing
int i = 0;
for (String s : trackIdListPayload.getTrackIds()) {
if (s.equals(track.getTrackId())) {
trackArray[i] = track;
// don't break here, as we may have a case where we have multiple instances of the same trackId (although
// at the moment a request will be made for each anyway...
}
i++;
}
latch.countDown();
}
});
}
return null;
}
If you want to make all the requests asynchronously and wait for them to return you can do this (lambdas for brevity and readability):
tracks.flatMap(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io()))
.toList()
.doOnNext(list -> processTrackList())
...
If you require that the results are returned in the order of tracks but are still requested asynchronously then in soon to be released rxjava 1.0.15 you will be able to do this
tracks.concatMapEager(track -> getTrackDetails(track.id)
.subscribeOn(Schedulers.io())
.toList()
.doOnNext(list -> processTrackList())
...
If I understand correctly, you have a list of tracks as input and you want a list of webservice results. Here's a simple way to do that with RxJava if you can make your network call synchronous (rxjava will handle the background processing for you).
Observable.from(trackList)
.map(new Func1<Track, Response>() {
#Override
public Response call(Track track) {
return makeRequestSynchronously(track.id());
}
})
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Response>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Response> responses) {
}
});
Edit: You can change Retrofit to return observables from webservice, if you do that you will need to change map to the following
.flatMap(new Func1<Track, Observable<Response>>() {
#Override
public Observable<Response> call(Track track) {
return makeRequestThatReturnsObservable(track.id());
}
})

Categories