Search code examples
androidlistrealm

Realm add item to RealmList that is added to RealmObject


I need to solve putting data to a realm database like this:

I have an object called obtained_code; I have a realmList of Obtained codes in an object called Offer; I download obtained codes separately, and by their offer id assign them to the lists of each object. The problem is that I can't add them because when I check the size, it's always 0.

Here is the code:

ObtainedCodes codes = response.body();
                for (ObtainedCode c : codes.getObtainedCodes()) {
                    Offer offer = RealmController.with(SplashActivity.this).getOffer(c.getOffer_id());
                    if (offer != null) {
                        Log.d("Size", "Offer not null");
                        realm1.beginTransaction();
                        RealmList<ObtainedCode> list = offer.getObtained_codes();
                        if (!list) { // if the 'list' is managed, all items in it is also managed
                            RealmList<ObtainedCode> managedImageList = new RealmList<>();
                            for (ObtainedCode item : list) {
                                if (item) {
                                    managedImageList.add(item);
                                } else {
                                    managedImageList.add(realm1.copyToRealm(item));
                                }
                            }
                            list = managedImageList;
                        }

                        offer.setObtained_codes(obtainedCodes);
                        Log.d("Size", String.valueOf(offer.getObtained_codes().size()));
                        realm1.copyToRealmOrUpdate(offer);
                        realm1.commitTransaction();
                    }
                    offer = RealmController.with(SplashActivity.this).getOffer(c.getOffer_id());
                    Log.d("Size", String.valueOf(offer.getObtained_codes().size()));
                }

Solution

  • 1.) the Ravi Tamada tutorial on InfoHive is a terrible mess, please refer to my remake of that example instead.

    If you managed to start using 0.82.1 because Ravi Tamada claimed that a 4 years old version is "stable", well I know that it's not. Use 1.2.0 instead (or the latest version which is 3.4.1)

    And if you see a RealmController.with(), run, because it ignores thread-confinement. The moment you try to access it from a background thread, it'll crash.

    On background threads, you'd need to do

    @Override
    public void run() {
        try(Realm realm = Realm.getDefaultInstance()) {
           repository.whatever(realm); // pass Realm instance to database methods
        } // auto-close
        // end of thread
    }
    

    2.) you are executing writes on the UI thread, that is bad, from UI thread you should use realm.executeTransactionAsync(), but in your case you should actually execute the Retrofit call on a background thread using Ęxecutors.newSingleThreadedPool() and call it with call.execute() instead of call.enqueue().

    3.) You should write to Realm on the background thread, and on the UI thread you should use RealmChangeListener to listen to writes.

    4.) your code doesn't work because you're setting an unmanaged list to a managed RealmObject.

    You should modify the existing RealmList inside the RealmObject, and add only managed objects to it.


    Executor executor = Executors.newSingleThreadExecutor(); // field variable
    // ...
    
    void someMethod() {
        executor.execute(new Runnable() {
            @Override
            public void run() {
                Response<ObtainedCodes> response = retrofitService.getObtainedCodes().execute(); // run on current thread
                ObtainedCodes codes = response.body();
                if(codes == null) return;
                try(Realm r = Realm.getDefaultInstance()) {
                    r.executeTransaction(new Realm.Transaction() {
                        @Override
                        public void execute(Realm realm) {
                            for(ObtainedCode obtainedCode : codes.getObtainedCodes()) {
                                Offer offer = realmRepository.getOffer(realm, obtainedCode.getOfferId());
                                if(offer == null) {
                                    offer = realm.createObject(Offer.class, obtainedCode.getOfferId());
                                    // map properties to offer if possible
                                }
                                RealmList<ObtainedCode> offerCodes = offer.getObtainedCodes();
                                ObtainedCode managedObtainedCode = realm.where(ObtainedCode.class).equalTo("obtainedCodeId", obtainedCode.getId()).findFirst();
                                if(managedObtainedCode == null) {
                                    managedObtainedCode = realm.createObject(ObtainedCode.class, obtainedCode.getId());
                                    // map properties from obtained code to managed obtained code
                                }
                                if(!offerCodes.contains(managedObtainedCode)) {
                                    offerCodes.add(managedObtainedCode);
                                }
                            }
                        }
                    });
                }
            }
        });
    }