Your Realm is opened from a thread without a Looper - android

I thought I was following the recommended Realm approach for running Async data inserts like this:
public void addCustomer(final Customer customer) {
Realm insertRealm = Realm.getDefaultInstance();
insertRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm backgroundRealm) {
long id = customerPrimaryKey.incrementAndGet();
customer.setId(id);
backgroundRealm.copyToRealm(customer);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d(LOG_TAG, "Customer Added");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.d(LOG_TAG, error.getMessage());
}
});
insertRealm.close();
}
However, when I run the above code I get "Your Realm is opened from a thread without a Looper and you provided a callback, we need a Handler to invoke your callback"
I am running this code in a non-Activity class, what I am doing wrong here and how can I fix it. Thanks.
Update - Fixed
It turns out that there is nothing wrong with the query, problem is that I was calling it from IntentService. I was trying to seed the database on app first run, so I fixed this like this:
protected void onHandleIntent(Intent intent) {
Realm realm = Realm.getDefaultInstance();
//Add sample Customers to database
List<Customer> customers = SampleCustomerData.getCustomers();
realm.beginTransaction();
for (Customer customer: customers){
customer.setId(customerPrimaryKey.getAndIncrement());
realm.copyToRealm(customer);
}
realm.commitTransaction();
realm.close();
}
That fixed, outside of the IntentService, the query works fine when called from a UI Thread.

In order for
insertRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm backgroundRealm) {
//...
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// !!!
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// !!!
}
});
asynchronous transaction callbacks to be called on a background thread, the thread needs to be associated with a Looper (and therefore have Handlers that can communicate with the thread with that Looper).
Solution, use synchronous transaction on background thread.
But you've already figured that out.
protected void onHandleIntent(Intent intent) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
final List<Customer> customers = SampleCustomerData.getCustomers();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
for (Customer customer: customers){
customer.setId(customerPrimaryKey.getAndIncrement());
realm.insert(customer);
}
}
});
} finally {
if(realm != null) {
realm.close();
}
}
}

Realm relies on Android' thread communication by Hanfler and Looper classes.
It looks like you query async operation from another background Thread (why?, it is already in background, use synchronous version).
To fix that you need thread with Active Looper. Use Handler thread as you background thread - it will have Looper initialized

You need to call addCustomer from UI thread.

Try this line
insertRealm.close();
to add within onSuccess and onError method. Remove it from last line as it is currently.
So your code will look like
public void addCustomer(final Customer customer) {
final Realm insertRealm = Realm.getDefaultInstance();
insertRealm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm backgroundRealm) {
long id = customerPrimaryKey.incrementAndGet();
customer.setId(id);
backgroundRealm.copyToRealm(customer);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
Log.d(LOG_TAG, "Customer Added");
insertRealm.close();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
Log.d(LOG_TAG, error.getMessage());
insertRealm.close();
}
});
}

Related

Android Realm and on a null object reference in fragment

I'm looking the answer of my problem. It's my first contact with Realm database and I have a problem with null object reference in fragment during saving data. Does anyone know what I'm doing wrong?
Helper method:
public void saveTask(final String text, final String date, final String time ) {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Task task = bgRealm.createObject(Task.class);
task.setText(text);
task.setDate(date);
task.setTime(time);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// Transaction was a success.
Log.d(TAG, "ON_SUCCESS: Data Written Successfully");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.d(TAG, "ON_ERROR: Error");
}
});
}
OnClick method in fragment:
#OnClick(R.id.save_button)
public void onClickSave() {
realmHelper.saveTask(title_text.getText().toString().trim(), date_text.getText().toString().trim(), time_text.getText().toString().trim());
}
Code inside onCreateView:
ButterKnife.bind(this, view);
((MainActivity) getActivity()).hideFloatingActionButton();
Realm.init(getActivity());
RealmConfiguration configuration = new RealmConfiguration
.Builder()
.deleteRealmIfMigrationNeeded()
.build();
realm = Realm.getInstance(configuration);
I tried solution with textview fields inside onClick method, but still same.
From your OnClick method in fragment, I observe that you have a realm helper class which you created an instance of called realmHelper. Since you are using realm asynchronously I suggest you pass the instantiated realm object to the helper method or instantiate realm afresh in the helper method or class.
Passing the realm instance to the helper method
public void saveTask(Realm realm, final String text, final String date, final String time ) {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Task task = bgRealm.createObject(Task.class);
task.setText(text);
task.setDate(date);
task.setTime(time);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// Transaction was a success.
Log.d(TAG, "ON_SUCCESS: Data Written Successfully");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.d(TAG, "ON_ERROR: Error");
}
});
}
Instantiating realm in the helper method
public void saveTask(Context context, final String text, final String date, final String time ) {
Realm.init(context);
RealmConfiguration configuration = new RealmConfiguration
.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm realm = Realm.getInstance(configuration);
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Task task = bgRealm.createObject(Task.class);
task.setText(text);
task.setDate(date);
task.setTime(time);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
// Transaction was a success.
Log.d(TAG, "ON_SUCCESS: Data Written Successfully");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.d(TAG, "ON_ERROR: Error");
}
});
}

Does Realm executeTransactionAsync close the instance

The title says it all. I've done some searching but haven't found anything concrete.
Do I need to call realm.close after doing realm.executeTransactionAsync or does the async transaction handle that?
Thank you
EDIT: Per EpidPandaForce, executeTransactionAsync closes the background realm instance when complete.
But what is the proper way to close the realm instance if executeTransactionAsync is called from the UI thread? In the transactions onSuccess/onFailure?
You seem like you're looking for the following scenario.
public void doWrite(MyObject obj) {
Realm realm = Realm.getDefaultInstance();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
bgRealm.insert(obj); // assuming obj is unmanaged
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
realm.close();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
realm.close();
}
});
}
In Kotlin
fun doWrite(obj: RealmObject) {
val realm = Realm.getDefaultInstance()
realm.executeTransactionAsync({ bgRealm ->
bgRealm.insert(obj) // assuming obj is unmanaged
}, { realm.close() }, { realm.close() })
}

UI blocked when writing 36 MB JSON file to realm Dataabase, so unable to show progress

I am trying to parse a local JSON file i.e. 36 mb large into realm database. I am also trying to show progress bar while doing it. But it is not showing. I then used timer to start realm task after a second, this time progress dialog starts but hangs after a second. I even tried asynch realm task but issue persists.
RealmAsyncTask transaction = realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
}
}, null);
// configuration change ...
public void onStop () {
if (transaction != null && !transaction.isCancelled()) {
transaction.cancel();
}
}
Guys, find any solution regarding this.
Use an async transaction instead
showProgressbar();
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Import data
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
hideProgressbar();
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
hideProgressbar();
}
});

onSuccess of Realm.Transaction.Callback is not getting called from IntentService

I am trying to update records in Realm using executeTransaction function but when i call this piece of code from IntentService , onSuccess of Ream.Transaction.Callback is not getting called automatically after calling copyToRealmOrUpdate.
final Realm realm = getRealm() ;
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
//some db operations and eventually calling
realm.
realm.copyToRealmOrUpdate(employeeList);
}
}, new Realm.Transaction.Callback() {
#Override
public void onSuccess() {
// this function never gets called if
//the whole function is called from IntentService
realm.close();
}
#Override
public void onError(Exception e) {
realm.close();
}
});
private RealmConfiguration initalizeRealmConfig(){
if(realmConfiguration == null)
return realmConfiguration = new RealmConfiguration.Builder(mContext)
.name(DB_NAME)
.schemaVersion(DB_VERSION)
.build();
else return realmConfiguration;
}
public Realm getRealm(){
return Realm.getInstance(initalizeRealmConfig());
}
i am using IntentService for background db operation and webservice calls when i call above code from Activity onSuccess gets called but i really dont want to call it from Activity as per requirement.
I think reason is that you are doing a asynchronous transaction in the IntentService, and perhaps the IntentService is closed before the result is ready. An async transaction shouldn't necessary in any case since the IntentService is running on a background thread.
Just doing the below should be the same:
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// work
}
});
realm.close();

Realm access from incorrect thread when Observable subscribed on Schedulers.io()

I am subscribed on IO scheduler.
getObservableItems(itModel).subscribeOn(Schedulers.io()).
onBackpressureBuffer().
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<List<ItemModel>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<ItemModel> resultItemModel) {
}
});
This is my observable where I am doing Realm transactions
private Observable<List<ItemModel>> getObservableItems(ItModel itModel) {
return Observable.create(subscriber -> {
realm = Realm.getInstance(mContext);
if (itModel != null) {
ArrayList<String> ids = ProjectUtil.getId(itModel.getRequestUrl());
DatabaseHelper.saveItemCategory(realm, itModel, ids.get(0), ids.get(1));
}
RealmQuery<ItemModel> itemModelRealmQuery = realm.where(ItemModel.class);
/* Error on below line */
resultItemModel = itemModelRealmQuery.equalTo("res_id", subCategoryModel.getId()).
equalTo("menu_grp_id", subCategoryModel.getMenu_grp_id()).findAll();
subscriber.onNext(resultItemModel);
subscriber.onCompleted();
});
}
It is because you are combining .subscribeOn(Schedulers.IO() with observeOn(AndroidSchedulers.mainThread()).
This means that you execute the work on the io() thread but try to use the result on the main thread. This currently goes against Realms threading policy as we use Thread confined objects. We have an issue for supporting this use case here: https://github.com/realm/realm-java/issues/1208
Until then you will either have to copy your Realm data to standard Java objects or do all realm operations on the same thread (by removing subscribeOn/observeOn).
After update 0.87.1, Realm provide RxJava support.
realm.where(ItemModel.class).findAllAsync().asObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer < RealmResults < ItemModel >> () {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(RealmResults < ItemModel > itemModels) {
System.out.println(itemModels.get(0).getItem());
}
});

Categories

Resources