0

I have an android application which I do many realm operations. I tried to use handler threads to faster the database operations however this time I get these errors. I have reviewed similar questions but could not find out the reason.

12-17 20:07:30.826  6387  8777 W System.err: io.realm.exceptions.RealmFileException: Unable to open a realm at path '/data/data/br.com.gomus.androidapp/files/default.realm': open() failed: Too many open files. (open("/data/data/br.com.gomus.androidapp/files/default.realm") failed: Too many open files) (/data/data/br.com.gomus.androidapp/files/default.realm) in /Users/cm/Realm/realm-java-release/realm/realm-library/src/main/cpp/io_realm_internal_OsSharedRealm.cpp line 101 Kind: ACCESS_ERROR.
12-17 20:07:30.827  6387  8777 W System.err:    at io.realm.internal.OsSharedRealm.nativeGetSharedRealm(Native Method)
12-17 20:07:30.827  6387  8777 W System.err:    at io.realm.internal.OsSharedRealm.<init>(OsSharedRealm.java:184)
12-17 20:07:30.827  6387  8777 W System.err:    at io.realm.internal.OsSharedRealm.getInstance(OsSharedRealm.java:254)
12-17 20:07:30.828  6387  8777 W System.err:    at io.realm.internal.OsSharedRealm.getInstance(OsSharedRealm.java:244)
12-17 20:07:30.828  6387  8777 W System.err:    at io.realm.RealmCache.doCreateRealmOrGetFromCache(RealmCache.java:319)
12-17 20:07:30.828  6387  8777 W System.err:    at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:282)
12-17 20:07:30.828  6387  8777 W System.err:    at io.realm.Realm.getDefaultInstance(Realm.java:332)
12-17 20:07:30.828  6387  8777 W System.err:    at br.com.gomus.androidapp.data.local.database.LocalDatabaseImpl.findRealmObject(LocalDatabaseImpl.java:284)
12-17 20:07:30.829  6387  8777 W System.err:    at br.com.gomus.androidapp.data.local.dao.schedule.SpotScheduleDaoImpl$1.run(SpotScheduleDaoImpl.java:113)
12-17 20:07:30.829  6387  8777 W System.err:    at android.os.Handler.handleCallback(Handler.java:755)
12-17 20:07:30.829  6387  8777 W System.err:    at android.os.Handler.dispatchMessage(Handler.java:95)
12-17 20:07:30.829  6387  8777 W System.err:    at android.os.Looper.loop(Looper.java:154)
12-17 20:07:30.830  6387  8777 W System.err:    at android.os.HandlerThread.run(HandlerThread.java:61)

This is SpotScheduleDaoImp.java and line 113 corresponds to the mDatabase.findRealmObject(ScheduleCompound.class, query, line.

@Override
public void find(Func1<RealmQuery<ScheduleCompound>, RealmQuery<ScheduleCompound>> query,
                 Action1<Result<List<ScheduleCompound>>> onResult) {
    if (query != null) {


        handlerThread = new HandlerThread("MyHandlerThreadspotscheduleDao");
        handlerThread.start();
        looper = handlerThread.getLooper();

        Handler handler = new Handler(looper);

        handler.post(new Runnable(){
            @Override
            public void run() {
                Timber.log(Log.DEBUG, "find Thread.currentThread().getName() " + Thread.currentThread().getName());

                mDatabase.findRealmObject(ScheduleCompound.class, query,
                        result -> {

                            onResult.call(Result.getData(SpotScheduleMapper.copyFromRealmResults(result.data)));
                            //handlerThread.quit();

                        },

                        err -> {
                            onResult.call(Result.getError(err));
                            //handlerThread.quit();

                        }
                );
            }
        });
    }
}

This is LocalDatabaseImpl.java and line 284 corresponds to the realm = Realm.getDefaultInstance(); command.

public <T extends RealmModel> void findRealmObject(Class<T> clazz,
                                                       Func1<RealmQuery<T>, RealmQuery<T>> filter,
                                                       Action1<Result<RealmResults<T>>> onResult,
                                                       Action1<Throwable> onError) {

        Realm realm = null;
        RealmResults<T> realmResults = null;
        try {
            realm = Realm.getDefaultInstance();

            RealmQuery<T> realmQuery = filter.call(realm.where(clazz));

            Timber.log(Log.DEBUG, "findRealmObject Thread.currentThread().getName() " + Thread.currentThread().getName());

            realmResults = realmQuery.findAll();

            if(realmResults.isValid()) {
                Timber.log(Log.DEBUG, clazz.getCanonicalName() + "findRealmObject realmQuery.findAll = " + realmResults);
                onResult.call(Result.getData(realmResults));

            }

        } catch (Exception e) {

            e.printStackTrace();
            onError.call(e);
        }
        finally
        {
            if(realm != null)
                if(realmResults != null && realmResults.isValid())
                    if(!realm.isClosed()) {
                        realm.close();
                    }
        }
    }

What I understand is something is wrong with the closing realm instances but I do not know why. Because in finally block I close the realm instance. And before that I was closing both at the end of the try and catch blocks by checking if it is not closed before like this !realm.isClosed() But at that time it was giving error and saying that the realm instance is already closed making it unusable. So I moved the closing part to the finally block.

This is the other stack trace.

--------- beginning of crash
12-17 20:07:31.796  6387  8776 E AndroidRuntime: FATAL EXCEPTION: MyHandlerThreadplaylistScheduleDao
12-17 20:07:31.796  6387  8776 E AndroidRuntime: Process: br.com.gomus.androidapp, PID: 6387
12-17 20:07:31.796  6387  8776 E AndroidRuntime: io.realm.exceptions.RealmError: Unrecoverable error. Too many open files in /Users/cm/Realm/realm-java-release/realm/realm-library/src/main/cpp/io_realm_internal_OsSharedRealm.cpp line 101
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.internal.OsSharedRealm.nativeGetSharedRealm(Native Method)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.internal.OsSharedRealm.<init>(OsSharedRealm.java:184)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.internal.OsSharedRealm.getInstance(OsSharedRealm.java:254)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.internal.OsSharedRealm.getInstance(OsSharedRealm.java:244)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.RealmCache.doCreateRealmOrGetFromCache(RealmCache.java:319)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:282)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at io.realm.Realm.getDefaultInstance(Realm.java:332)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at br.com.gomus.androidapp.data.local.database.LocalDatabaseImpl.findRealmObject(LocalDatabaseImpl.java:284)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at br.com.gomus.androidapp.data.local.dao.schedule.PlaylistScheduleDaoImpl$1.run(PlaylistScheduleDaoImpl.java:110)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at android.os.Handler.handleCallback(Handler.java:755)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at android.os.Handler.dispatchMessage(Handler.java:95)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at android.os.Looper.loop(Looper.java:154)
12-17 20:07:31.796  6387  8776 E AndroidRuntime:    at android.os.HandlerThread.run(HandlerThread.java:61)

I have troubles about closing realm instances. Any help would be appreciated.

Thanks.

Hilal
  • 902
  • 2
  • 22
  • 47
  • 1
    in the javadoc they say that https://realm.io/docs/java/latest/api/io/realm/Realm.html#getDefaultInstance-- the `getDefaultInstance()` method is a constructor... You *must* keep the first instance and reuse it. Never call that again – eduyayo Dec 18 '19 at 17:09
  • Realm is also Autocloseable. if you get a new instance each time you should be using try with resources so it gets closed wach time it gets instanced https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html – eduyayo Dec 18 '19 at 17:12
  • @eduyayo Thank you so much for your help. So I do not need to close it at all if I first create it using try with resources, right? – Hilal Dec 18 '19 at 17:21
  • @eduyayo So should I do all my database operations with only one realm instance? Would not that be a problem when I use it with different threads?Also what if I use the realm object with UI and other background threads? Should I have different usages of it? Thanks – Hilal Dec 18 '19 at 17:23
  • 1
    the doc reads: `Realm instances cannot be used across different threads. This means that you have to open an instance on each thread you want to use Realm`. So you better go with the try-with-resources stuff and transactions. You must assure the close() after use and the try with resources will do. – eduyayo Dec 18 '19 at 17:28
  • @eduyayo So can I remove the `close()` parts from all of them. And it would probably solve the error that too many open files, right? Also I wont need the finally block then to close the realm instance because it will be closed when it is needed. – Hilal Dec 18 '19 at 17:35
  • 1
    The problem is the too many ifs... Some of them are not closed. Use the try with resources and it'll close even after an error – eduyayo Dec 18 '19 at 17:37
  • 1
    And use an ExecutorService to limit tha threads or you'll clog the cpu and none of them will end. – eduyayo Dec 18 '19 at 17:39
  • I have never used the ExecutorService before. I have used handler threads for every tasks. Do I need it still? Thanks – Hilal Dec 18 '19 at 17:50

0 Answers0