Skip to content Skip to sidebar Skip to footer

Asynchronously Copy From One Realm To Another

From my understanding, Realm can/should only be accessed from the main thread. I use two realms, one for storing 'dirty' data that, upon validation, will be moved to the real real

Solution 1:

Alternatively to EpicPandaForces answer, one can employ a quick workaround for this problem:

Use a simple synchronous query (like findAll()) and realm.copyFromRealm(results) inside an executeTransactionAsync block - it might not be good practice to execute a transaction that does not include write operations, but it gets the job done without the need to change the whole code.

TLDR; Move your query and copyFromRealm to an executeTransactionAsync block.

Solution 2:

From my understanding, Realm can/should only be accessed from the main thread.

This is a misconception. While Realm auto-updates only on looper threads (such as the main thread), this does not mean you cannot create a new Realm instance on any thread.

If you want to open a Realm on your background thread, you could easily do this:

new Thread(new Runnable() {
    @Override
    publicvoidrun() {
        Realm firstRealm = null;
        Realm secondRealm = null;
        try {
           firstRealm = Realm.getInstance(firstConfiguration);
           secondRealm = Realm.getInstance(secondConfiguration);

           firstRealm.beginTransaction();
           secondRealm.beginTransaction();
           RealmResults<SomeObject> someObjects = firstRealm.where(SomeObject.class)
                                                            .equalTo(SomeObjectFields.VALID, true)
                                                            .findAll();
           secondRealm.copyToRealmOrUpdate(someObjects); // I am not sure if you have to detach it first.
           someObjects.deleteAllFromRealm();
           secondRealm.commitTransaction();
           firstRealm.commitTransaction();
        } catch(Throwable e) {
           if(firstRealm != null && firstRealm.isInTransaction()) {
               firstRealm.cancelTransaction();
           }
           if(secondRealm != null && secondRealm.isInTransaction()) {
               secondRealm.cancelTransaction();
           }
           throw e;
        } finally {
           if(firstRealm != null) {
              firstRealm.close();
           }
           if(secondRealm != null) {
              secondRealm.close();
           }
        }
    }
}).start();

And to access the elements on the UI thread, you'd just need a UI thread Realm and a RealmResults with a RealmChangeListener bound to it.

publicclassMainActivityextendsAppCompatActivity {
    Realm realm;

    @BindView(R.id.realm_recycler)
    RecyclerView recyclerView;

    RealmResults<SomeObject> listenerSet;
    RealmChangeListener realmChangeListener = newRealmChangeListener() {
        @OverridepublicvoidonChange(Object element) {
           if(recyclerView != null && recyclerView.getAdapter() != null) {
              recyclerView.getAdapter().notifyDataSetChanged();
           }
        }
    });

    @OverridepublicvoidonCreate(Bundle bundle) {
       super.onCreate(bundle);
       realm = Realm.getDefaultInstance();
       setContentView(R.layout.activity_main);
       ButterKnife.bind(this);

       listenerSet = realm.where(SomeObject.class).findAll();
       listenerSet.addChangeListener(realmChangeListener);

       // set up recyclerView

       adapter.updateData(realm.where(SomeObject.class).findAll());
    }

    @OverridepublicvoidonDestroy() {
       super.onDestroy();
       if(realm != null) {
          realm.close();
          realm = null;
       }
    }
}

Solution 3:

There is no standard way of doing that in realm according to this comment by beeender

I think there is no standard way to do this in Realm. I would like to create a static function in RealmObjectA like copyToRealmObjectB which takes two params, one is RealmObjectA and the other is RealmObjectB. And call setters and getters in the static function to do the copy. I am sorry but it doesn't look like a normal and reasonable requirement. Why don't you just use a RealmObject filed instead? See realm.io/docs/java/latest/#field-types

Solution 4:

I used RxJava Completable to complete this operation in background thread

publicclassRealmCopier{

    privateString errorLog = "";

    public Completable copyTo(Realm realm) {//in my case remote realm on ROSreturn Completable.create(emitter -> {

            boolean isCopied = copy(realm);

            if (!emitter.isDisposed()){

                if (isCopied)
                    emitter.onComplete();
                else
                    emitter.onError(newThrowable(errorLog));

            }
        });

    }

    privateboolean copy(Realm realm) {

        try {
            realm.beginTransaction();

            realm.insertOrUpdate(getItems(SomeClassA.class));
            realm.insertOrUpdate(getItems(SomeClassB.class));

            realm.commitTransaction();

        }catch(Exception e){
            realm.cancelTransaction();
            errorLog = e.getMessage();
            returnfalse;

        } finally {
            realm.close();
        }

        returntrue;
    }

    privateList<?extends RealmObject> getItems(Class<? extendsRealmObject> classType) {

        RealmConfiguration localConfiguration = ConfigurationManager.createLocalConfiguration();

        Realm realm = Realm.getInstance(localConfiguration);//local realmreturn realm.where(classType).findAll();
    }

}

Post a Comment for "Asynchronously Copy From One Realm To Another"