2

I am making an Android App using the Room Data Persistence Library, however I have difficulty to figure out how I can query the database in a new thread and return the value to the main thread...

For instance I have a method in my ProductDAO to retrieve one product by his barCode:

@Query("SELECT * FROM Product WHERE barCode = :barCode")
Product getProductById (String barCode);

And in another class I tried this :

public Product getProduct(final String barcode){
    final Product[] product = new Product[1];
    new Thread(new Runnable() {
        @Override
        public void run() {
            product[0] = db.inventory().getProductById(barcode);
        }
    });
    return product[0];
}

However the result is always null and I don't know how to return the value from getProductById in the main thread. I'm sure however that the database configuration is correct because all my queries works in the main thread when I build the database with allowMainThreadQueries()

I also have the same problem when inserting a product from a new thread :

public void AddProduct(final Product product){
    new Thread(new Runnable() {
        @Override
        public void run() {
            db.inventory().insertOneProduct(product);
        }
    });
}

because it doesn't seem to insert anything.

Thanks a lot !!

obrassard
  • 117
  • 3
  • 10
  • First, don't use `Thread`, use [AsyncTask](https://developer.android.com/reference/android/os/AsyncTask.html) instead with promise to update your UI. Second, your insert `Thread` didn't start, try call start after your thread, like `new Thread(new Runnable() { @Override public void run() { db.inventory().insertOneProduct(product); } }).start();` – Randyka Yudhistira Mar 27 '18 at 01:31
  • Indeed I forgot to start the tread duh ! hahaha However, can I retrieve the return value from an AsyncTask ? – obrassard Mar 27 '18 at 02:36
  • @obrassard See https://stackoverflow.com/q/6964011 for some elaborate explanations – user1643723 Mar 27 '18 at 02:38

2 Answers2

2

The problem appears to be with this code:

 public void AddProduct(final Product product){
new Thread(new Runnable() {
    @Override
    public void run() {
        db.inventory().insertOneProduct(product);
    }
});
 }

Although you have created a thread, you did not call the start method. So change it to this:

 public void AddProduct(final Product product){
new Thread(new Runnable() {
    @Override
    public void run() {
        db.inventory().insertOneProduct(product);
    }
}).start();
}

And then it should work.

As a side note, you should be using Async Task instead of the thread as AsyncTask was designed to work with the android threading model and allows you to track progress easily while doing work on a separate thread among other benefits.

MarGin
  • 2,078
  • 1
  • 17
  • 28
SteelToe
  • 2,477
  • 1
  • 17
  • 28
  • Thanks ! it's a bit silly to have forgotten to start my thread ! I didn't know AsyncTasks.. Is there a way to retrieve return values of an AsyncTasks ? – obrassard Mar 27 '18 at 02:35
  • Sure just override the `onPostExecute()` method which runs on the UI thread, this method will recevie an arguement containing the result that was returned from the doInBackground method. – SteelToe Mar 27 '18 at 02:37
  • Oh ! I understand! Thanks im gonna try that – obrassard Mar 27 '18 at 02:38
  • I still don't understand how i can retrieve the value outside the `onPostExecute()`... This is what I have so far : https://pastebin.com/GnxTWfJb – obrassard Mar 27 '18 at 02:58
  • You dont retreive the value, since the OnPostExecute() method is operated on the main thread so you do whatever you need to do with the value in that method – SteelToe Mar 27 '18 at 03:21
  • Also remember you need to call the execute method once you create your AsyncTask to run it. I would advise you to take a look at the android AsyncTask documentation as it is pretty good – SteelToe Mar 27 '18 at 03:23
  • I see. I tought I could retrieve the return value... Thanks!! – obrassard Mar 27 '18 at 03:23
  • In my case I only retrieve very minimal data, that gets erased once the user goes online. I think using async task for everywhere I access the DB is an overkill. Unless if someone can explain otherwise. – Peterstev Uremgba May 10 '18 at 20:46
  • @Light all Dm access must be done on a different thread, as you dont control disk speeds, so if the disk is really slow and you access it on the main thread you will have to deal with a completely non responsive ui – SteelToe May 11 '18 at 20:22
  • Also when I tried working off the main thread, I the app keeps crashing, async task didn't work as well as regular threads. LiveData was the only thing that worked (didn't try rx). – Peterstev Uremgba May 12 '18 at 12:00
  • @obrassard you can accomplish what you want using LiveData – portfoliobuilder Nov 29 '18 at 20:04
1

This is an example using AsyncTask. Since the only method that actually runs in a different thread is 'doInBackground()' it is easy to write back data to the UI-Thread. The first parameter of the AsyncTask class signature defines the parameter type of the'doInBackground(Param param)'-method. Here is where one could pass a reference to the calling class/Activity which is your UI-Thread. Let the task do what you want it to do and return the result to the onPostExecute(Result result)-method. Here you can simply call any method on the UI-thread and pass your data. Fire the whole process up by calling the following from your UI-thread.

new AllUsersLoader().execute(this); 

public class AllUsersLoader extends AsyncTask<Context, Void, List<User>>{
        private AppDataBase db;
        private UserDao dao;
        private List<User> users;
        private MainActivity con;

    /**
     * This method actually runs on a Non-UI-Thread.
     * @param contexts A reference to the MainActivity
     * @return Result that is passed to onPostExecute()
     */
    @Override
    protected List<User> doInBackground(Context... contexts) {

        this.con = (MainActivity) contexts[0];
        db = Room.databaseBuilder(con.getApplicationContext(), AppDataBase.class, con.getResources().getString(R.string.db_scema_name)).build();
        dao = db.getUserDao();
        users = dao.getAllUsers();
        db.close();
        return users;
    }

    /**
     * This method is again part of the UI-Thread but waits till it receives the result from doInBackground()
     * @param users
     */
    @Override
    protected void onPostExecute(List<User> users) {            
        super.onPostExecute(users);
        con.updateRecyclerView(users); //a method of the UI-thread expecting the result
    }
} // end of class

You could also cramp all of it into a nested class or Listener and save yourself passing the context.

Pauliman
  • 59
  • 5