7

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.

Val Okafor
  • 3,371
  • 13
  • 47
  • 72
  • 1
    This is called from a Presenter class. I did not start a thread, I thougth Realm spins of a thread of its own to execute async query. – Val Okafor Nov 05 '16 at 06:10

4 Answers4

3

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();
        }
    }
}
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
2

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

Alex Shutov
  • 3,217
  • 2
  • 13
  • 11
  • Would you mind providing an example, I am running the code from a Repository that is called by a Presenter. I have access to a Context object in this class, how can I use to fix the issue. – Val Okafor Nov 05 '16 at 06:09
  • http://stackoverflow.com/questions/25094330/example-communicating-with-handlerthread – Alex Shutov Nov 05 '16 at 06:14
0

You need to call addCustomer from UI thread.

Maksim Ostrovidov
  • 10,720
  • 8
  • 42
  • 57
-1

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();
            }
        });
    }
Jimit Patel
  • 4,265
  • 2
  • 34
  • 58