0

As the title says, android needs queries out of main thread since it will trhow java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time otherwise. So I managed to make async queries as many tutorials explain, but it doesn't make so much sense (so far) as I could achieve.

public class NewDetalleDiarioActivity extends AppCompatActivity {

    @Override
    protected void onStart() {
        super.onStart();
        db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database").build();
        findPeriodo();
        findDiario();
    }

    private void findPeriodo() {
        periodo = Diarios.getPeriodo(db);
        if (periodo == null) {
            Intent intent = new Intent(NewDetalleDiarioActivity.this, NewPeriodoActivity.class);
            startActivity(intent);
        }
    }

PROBLEM/ERROR:

If periodo is null, another activity is started, otherwise this one continues its thread. The problem is that, when I debug it (which slows proceses, of course) periodo returns an instance from the database, but when I run the code without debugging, periodo is null.

public class Diarios {
    public static Periodo getPeriodo(AppDatabase db) {
        return Factory.getIntPeriodo().getPeriodo(db);
    }
}

.

public class Factory {
    private static IntPeriodo intPeriodo;
    public static IntPeriodo getIntPeriodo() {
        return (intPeriodo == null) ? intPeriodo = new BusPeriodo() : intPeriodo;
    }
}

.

public class BusPeriodo implements IntPeriodo {
    // I don't think it's necessary to post the interface...
    @Override
    public Periodo getPeriodo(final AppDatabase db) {
        final Periodo[] periodo = new Periodo[1];
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() { //the async query that is driving me mad.
                periodo[0] = db.periodoDao().getPeriodo(new Date());
            }
        });
        return periodo[0];
    }
}

What's the proper way to make select queries without getting them delayed? The select query is indeed working, I don't think is necessary to post it (because it is returning an unique result when I debug), but it returns null when I run the code without debugging!! Please help.

Gonzalo Dambra
  • 891
  • 2
  • 19
  • 34
  • That's because `Runnable#run()` that you passed to `AsyncTask.execute(...)` will be run in the background thread and its execution is indeterministic. Before the background thread finished, you already return `periodo[0]` which might have not been set – cartesify May 02 '20 at 04:45
  • Sounds good, thanks! How can I handle it? – Gonzalo Dambra May 02 '20 at 04:49
  • You can try to follow something similar like this https://stackoverflow.com/questions/44167111/android-room-simple-select-query-cannot-access-database-on-the-main-thread – cartesify May 02 '20 at 05:01

1 Answers1

0

SOLUTION:

As @user7041125 suggested, but instead I made a new class with an interface to call methods back to the activity, like this:

public class PeriodoBridge extends AsyncTask<Void, Void, Periodo> implements IntPeriodoBridge {

    private WeakReference<Activity> weakActivity;
    private IntPeriodoBridge caller; //implement this interface in the activity which needs to query
    private AppDatabase db;
    private Periodo periodo;

    public PeriodoBridge(Activity activity, IntPeriodoBridge caller, AppDatabase db) {
        weakActivity = new WeakReference<>(activity);
        this.caller = caller; // assign activity instance to the local interface instance
        this.db = db;
        executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    protected Periodo doInBackground(Void... voids) {
        periodo = Diarios.getPeriodo(db);
        return periodo;
    }

    @Override
    protected void onPostExecute(Periodo periodo) {
        Activity activity = weakActivity.get();
        if (activity == null) {
            return;
        }
        if (periodo == null) {
            Intent intent = new Intent(activity, NewPeriodoActivity.class);
            activity.startActivity(intent);
        } else {
            setPeriodo(periodo);
        }
    }

    @Override  //this is an interface method (IntPeriodoBridge)
    public void setPeriodo(Periodo periodo) {
        caller.setPeriodo(periodo);  //I can set the query result back to the activity class with this
    }

Call the init method of this class. The activity implements IntPeriodoBridge and in that way I can set the query result object to the activity class.

Gonzalo Dambra
  • 891
  • 2
  • 19
  • 34