Realm two way to insert element - java

As far as I know, there is two way to insert element to database.
In first one, we can insert like this
realm.beginTransaction();
UsersTable usersTable = realm.createObject(UsersTable.class);
usersTable.setName(name);
usersTable.setSurname(surname);
usersTable.setAge(age);
usersTable.setSalary(salary);
realm.commitTransaction();
In second one, we have different kind of method to insert
realm.executeTransactionAsync(new Realm.Transaction () {
public void execute(Realm realm) {
//......
}
}, new Realm.Transaction.onSuccess() {
public void onSuccess(Realm realm) { }
}, new Realm.Transaction.onError() {
public void onSuccess(Realm realm) { }
}
What is the difference between them? It seems like the bottom one gives option to indicate something on both success and error.

realm.beginTransaction();
...
realm.commitTransaction();
This is synchronous transaction with no error handling (catch(Throwable e) { realm.cancelTransaction();). It is executed on the thread you are currently on.
realm.executeTransactionAsync(new Realm.Transaction () {
public void execute(Realm realm) {
//......
}
}, new Realm.Transaction.onSuccess() {
public void onSuccess(Realm realm) { }
}, new Realm.Transaction.onError() {
public void onSuccess(Realm realm) { }
}
This is asynchronous transaction. It can only be initiated from a Looper thread. It will run the transaction on Realm's background thread-pool.
The callbacks will be called only after Realm has updated the UI thread Realm.

Related

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.

Realm Asynchronous Transactions Order Structure in Android

I want to execute below methods in order. That means, if A methods ends, B starts. If B ends C starts and so forth.
public void InitializeAPI {
public static void init_A(Context mContext) {
Realm.init(mContext);
Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Some working is here.
}
}, new Realm.Transaction.OnSuccess() {
// Some proceesing lines are here.
}, new Realm.Transaction.OnError() {
Log.d("AsyncTransaction", "ERROR");
});
// And there are lots of other TransactionAsync() line in this methods.
}
public static void init_B(Context mContext) {...}
public static void init_C(Context mContext) {...}
// and So many initialize methods.
}
As you can see, when I execute init_A(Context), Async Transaction will be in background.
But the problem is init_B needs to be executed after init_A ending. init_C also. How can I design this matter??
Of course, I know that there is Realm.Transaction.OnSuccess() But, If I write init_B in Realm.Transaction.OnSuccess() at init_A, I think that codes won't be neat or good to Maintenance.
Why don't you use something like this:
realm.beginTransaction();
//... add or update objects here ...
realm.commitTransaction();
instead of a async call.
It will ensure that code will work synchronously.
Hope it helps..!
you could send a message though handler in the OnSuccess to trigger the init_B. But that doesn't look much better. So if you want to do the initialization without blocking the UI, creating your own thread might a good choice.
public void InitializeAPI {
public static void init_A(Context mContext) {
Realm.init(mContext);
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
// Write data
realm.commitTransaction();
realm.close();
}
public static void init_B(Context mContext) {...}
public static void init_C(Context mContext) {...}
// and So many initialize methods.
public static void init() {
init_A();
init_B();
init_C();
//...
}
}
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
InitializeAPI.init();
// Notify the main thread that initialization is done.
handler.post(initialziedRunnable);
}
});
thread.start();

CountDownLatch in Android locking thread

I've just started playing around with CountDownLatch in my Android app. Currently I am trying to make two Volley requests to my api, and wait until the data has been retrieved and stored before continuing with thread execution.
This is a sample of my code:
// new CountDownLatch for 2 requests
final CountDownLatch allDoneSignal = new CountDownLatch(2);
transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
#Override
public void onSuccess(ArrayList<Contact> resources, String resourceId) {
transactions.createRealmObject(resources, Contact.class);
allDoneSignal.countDown();
}
#Override
public void onFail(ArrayList<Contact> resources) {
}
});
transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
#Override
public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
transactions.createRealmObject(resources, Meeting.class);
allDoneSignal.countDown();
}
#Override
public void onFail(ArrayList<Meeting> resources) {
}
});
try {
allDoneSignal.await();
// continue executing code
// ...
} catch (InterruptedException e) {
e.printStackTrace();
}
The issue is that it doesn't seem to "complete" the countdown and therefore freezes because the latch is never released. I have confirmed that the API requests are working and the onSuccess callback is hit successfully, but the thread hangs.
UPDATE
I've just noticed that with the CountDownLatch set to 0, it hits onSuccess, but when I set it to anything greater than 0, it freezes and onSuccess is never called. Seems like something's funky with the threading.
Your code is too error prone, you need to call countDown() in a finally block and call it also in onFail otherwise in case of failure your application will freeze for ever. So your code should rather be something like:
transactions.getResourcesForRealm(Contact.class, "", new ICallBack<Contact>() {
#Override
public void onSuccess(ArrayList<Contact> resources, String resourceId) {
try {
transactions.createRealmObject(resources, Contact.class);
} finally {
allDoneSignal.countDown();
}
}
#Override
public void onFail(ArrayList<Contact> resources) {
allDoneSignal.countDown();
}
});
transactions.getResourcesForRealm(Meeting.class, "", new ICallBack<Meeting>() {
#Override
public void onSuccess(ArrayList<Meeting> resources, String resourceId) {
try {
transactions.createRealmObject(resources, Meeting.class);
} finally {
allDoneSignal.countDown();
}
}
#Override
public void onFail(ArrayList<Meeting> resources) {
allDoneSignal.countDown();
}
});
Sorry for the late answer, but if it's still any help to anyone:
You need to do the ".await" in a separate thread because it blocks the current thread.
Example:
final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
new Thread(new Runnable() {
#Override
public void run() {
allDoneSignal.await();
mainThreadHandler.post(new Runnable() {
doSomethingWhenAllDone();
});
}
}).start()

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());
}
})

How can I reuse a Subscriber between two Observables (RxJava)

In order to not repeat myself, I want to re-use a Subscriber variable between two observables. How do you do accomplish this? My current code below does not work, because after the subscriber is used once, it is unsubscribed and no longer works again. If I new a Subscriber instead of reusing a variable, my subscription works. I don't want to write the same code twice, if possible.
public class HomePresenter extends BasePresenter<HomeView> {
ArticleRepo articleRepo;
#Inject
public HomePresenter(ArticleRepo articleRepo) {
this.articleRepo = articleRepo;
}
#Override
public void onCreate(#Nullable PresenterBundle bundle) {
super.onCreate(bundle);
}
public void onEvent(ArticleCategoryClickedEvent event) {
Timber.v("Adapter position clicked at position: '%d'", event.getAdapterPosition());
view.launchArticleActivity(event.getArticleCategory());
}
public void onEvent(SeabeeOnlineExternalLinkClickedEvent event) {
view.launchExternalLink(event.getSeabeeOnlineExternalLink());
}
public void loadArticleImages() {
articleRepo.getArticleBuckets()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
public void refreshData() {
articleRepo.refreshAndSaveArticles()
.flatMap(new Func1<List<ArticleEntity>, Observable<List<ImageArticleCategoryEntity>>>() {
#Override
public Observable<List<ImageArticleCategoryEntity>> call(List<ArticleEntity> articleEntityList) {
return articleRepo.getArticleBuckets();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
final Subscriber<List<ImageArticleCategoryEntity>> subscriber = new Subscriber<List<ImageArticleCategoryEntity>>() {
#Override
public void onCompleted() {
Timber.v("Loading article images complete!");
view.hideLoadingAnimation();
}
#Override
public void onError(Throwable e) {
Timber.e("Error loading article images", e);
Log.e("tag", "Error loading article images", e);
}
#Override
public void onNext(List<ImageArticleCategoryEntity> integerImageArticleCategoryEntityHashMap) {
view.loadArticleImages(integerImageArticleCategoryEntityHashMap);
}
};
}
A Subscriber should not be reused. It will not work because it is a Subscription and once unsubscribed it is done.
Use an Observer instead if you want to reuse it.
source
You can reuse your subscriber, you just need to create an actual class out of it.
private static class MySubscriber extends Subscriber<List<ImageArticleCategoryEntity>> {...}
Subscriber<> subscriber1 = new MySubscriber();
Subscriber<> subscriber2 = new MySubscriber();
And there you go.

Categories