Search code examples
androidandroid-asynctaskrealm

Realm objects can only be accessed on the thread they were created _ Volley and AsyncTask


I already gone through these similar questions for the issue, but could not find the answer

SO question1 , SO question2 and SO question3

My application flow is on button click, network is requested as follows using Volley. Included only relevant code . Error getting in ActivityCustomer in the following line

Object obj = realmObj.where(ExplorerFolderData.class)
                    .equalTo(Constants.DBPARENTID,"0")
                    .or()
                    .equalTo(Constants.DBPARENTID,"-1")
                    .findAllAsync();

Error:

Caused by: java.lang.IllegalStateException: Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.

1) MyApplication

public class MyApplication extends Application
{
    private static Context appContext;
    @Override
    public void onCreate() {
        super.onCreate();
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder(this)
                .name(Realm.DEFAULT_REALM_NAME)
                .schemaVersion(0)
                .deleteRealmIfMigrationNeeded()
                .build();
        Realm.setDefaultConfiguration(realmConfiguration);
        appContext = getApplicationContext();
    }

     public static Context getAppContext(){
        return appContext;
    }
}

2) Interface OnAsyncTaskComplition

//This interface will get back the status to Activity/Fragment
public interface OnAsyncTaskComplition {
    public void networkResponse(ResultObject responseObject);

}

3) NetworkProcessor

public class NetworkProcessor{

    private OnAsyncTaskComplition mCaller;

    public NetworkProcessor(Activity activity){
        //setting caller Activity/Fragment to get back data
        mCaller=(OnAsyncTaskComplition)activity;
        processNetworkData();
    }

    //Method for Volley Network Procesing
    public void processNetworkData(){

    JsonObjectRequest jsonObjReq = new JsonObjectRequest(methodReq,urlBuffer.toString(),null,
                        new Response.Listener<JSONObject>(){

        @Override
        public void onResponse(JSONObject response) {
                JsonProcessor jsonProcessor=new JsonProcessor();

                    mCaller.networkResponse(jsonProcessor.getThingList(response));

                }
            }, new Response.ErrorListener(){
            @Override
            public void onErrorResponse(VolleyError error) {
               //Handle error also back to caller
             }
            });
    }

    }

4) JsonProcessor

public class JsonProcessor {

    public status void getThingList(JSONObject response){
        boolean status=false;
        try{
            RealmProcessor realmProcessor=RealmProcessor.with(MyApplication.getAppContext());
            Realm realmObj =  realmProcessor.getRealm();

            //Code for setting values to RealmObject class  ExplorerFolderData

            realmObj.beginTransaction();
            realmObj.copyToRealm(ExplorerFolderData RealmObject which has values populated);
            realmObj.commitTransaction();
            status=true;
        }catch(Exception e){

        }
    }
}

5) RealmProcessor

public class RealmProcessor {

    private static RealmProcessor instance;
    private Realm realm;

     private RealmProcessor(Context context) {
        realm = Realm.getDefaultInstance();
    }

    public static RealmProcessor with(Context context) {

        if (instance == null) {
            instance = new RealmProcessor(context);
        }
        return instance;
    }

    public Realm getRealm() {
        return realm;
    }

    }

6) Activity class ActivityCustomer

public class ActivityCustomer extends AppBaseActivity implements OnAsyncTaskComplition
{

    //Method called on Button click
    private void callNetwork(){
    new NetworkProcessor(this);
    }

    @Override
    public void networkResponse(ResultObject responseObject) {

        new ExplorerDBOperation().execute();

    }

    class ExplorerDBOperation extends AsyncTask<Void,Boolean,Boolean> {
        ProgressDialog dialog;

        @Override
        protected Boolean doInBackground(Void... params) {
            RealmProcessor realmProcessor=RealmProcessor.with(MyApplication.getAppContext());
            Realm realmObj =  realmProcessor.getRealm();

            //ERROR OVER HERE
            Object obj = realmObj.where(ExplorerFolderData.class)
                    .equalTo(Constants.DBPARENTID,"0")
                    .or()
                    .equalTo(Constants.DBPARENTID,"-1")
                    .findAllAsync();
                    return true;
        }

}

I am getting realm object using the same line in Activity as well as JsonProcessor class. What is the mistake I am making over here.


Solution

  • The way you are set up with the singleton you have only 1 Realm instance.

    If you call realmProcessor.getRealm(); in thread A, and then call it again in thread B, they get back the same instance. Realm does not allow sharing instances between threads. Since the AsyncTask's doInBackground runs on a separate thread, this is not working.

    Changing to this will rid you of the error. However you have some redesigning to do.

    @Override
    protected Boolean doInBackground(Void... params) {
        Realm realmObj =  Realm.getDefaultInstance();
    
        try {
            Object obj = realmObj.where(ExplorerFolderData.class)
                .equalTo(Constants.DBPARENTID,"0")
                .or()
                .equalTo(Constants.DBPARENTID,"-1")
                .findAll();
        } catch (Exception ex) {
            // handle error
        } finally {
            realmObj.close();
        }
    
        return true;
    }
    

    Note that you are responsible for each and every realm instance. This means that you must manually ensure that you close every instance once you're done with it. A common practice for AsyncTasks is that you wrap your doInBackground operations in a try/catch/finally and close the realm instance in the finally block to ensure it gets closed.

    See more in the docs