0

I have an async Retrofit-based API call in Android and need to wait with the DB calls until the API call finishes, so that I am sure the proper data gets entered into the DB.

I read that you can use Futures to accomplish this task, however with my current implementation I get a null pointer exception.

Below is the API method:

public Future<Void> postPrintMode(String authorization, final int userid, String deviceuid, final Map payload){
    api.postPrintMode(authorization, userid, deviceuid, payload, new Callback<PrintMode>() {

        @Override
        public void success(PrintMode printMode, Response response) {

            if (printMode.get_id() != 0) {
                dbOps.writePrintMode(userid, printMode);
                bus.getBus().post(new EVTNewPrintMode(printMode));
            }
        }

        @Override
        public void failure(RetrofitError retrofitError) {
            retrofitError.printStackTrace();
            APIUtils.showAPIResponseBody(retrofitError);
        }
    });

    return null;
}

And here the block where I want to ensure that the async code is executed BEFORE I continue to read the DB results.

Future<Void> f = APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, payload);
    // here I get the null pointer exception
    f.get();
    // the code below needs to be executed after the postPrintMode(...) async method;
    DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
    assertNotNull("Print Mode does not exist", printMode);
  • I don't think `Future` is what you want here. (you would need a synchronous call to retrofit inside a runnable for example). Your pattern would suit better a blockingqueue, a countdownlatch or possibly a semaphore – njzk2 Sep 01 '15 at 15:01
  • You are right, I used the CountDownLatch and stored globally the countdown signal and decrement it upon success or failure of the async method. – Zbyslaw Railerski Sep 02 '15 at 16:37
  • `stored globally the countdown signal` you can just declare it final in your method and return it. – njzk2 Sep 02 '15 at 17:39

2 Answers2

0

You can make your class that calls public Future<Void> postPrintMode method implement the new Callback<PrintMode> interface. After, you can your postPrintMode from it and pass a reference to itself into the method.

Here is a rough example (code not tested)

class Foo implements Callback<PrintMode> {

        Future<Void>  f;

        public Foo(){
            f = APIExec.getInstance().postPrintMode(IConstants.authorization, IConstants.userId, IConstants.deviceUid, this);
        }

        @Override
        public void success(PrintMode printMode, Response response) {

            if (printMode.get_id() != 0) {
                dbOps.writePrintMode(userid, printMode);
                bus.getBus().post(new EVTNewPrintMode(printMode));
            }
            if (f != null){
                f.get();
                // the code below needs to be executed after the postPrintMode(...) async method;
                DBPrintMode printMode = APIDBOps.getInstance().readPrintModeByPrintModeID(6);
                assertNotNull("Print Mode does not exist", printMode);
            }
        }

        @Override
        public void failure(RetrofitError retrofitError) {
            retrofitError.printStackTrace();
            APIUtils.showAPIResponseBody(retrofitError);
        }

}
C0D3LIC1OU5
  • 8,600
  • 2
  • 37
  • 47
0

Create a AsyncTaskThread class as below,

public class AsyncTaskThread extends AsyncTask<Void, Void, Void> {

Context context;
Handler myHandler;

public AsyncTaskThread( Context activityContext, Handler handler ) {
    this.context = activityContext;
    this.myHandler = handler;
}

@Override
protected void onPreExecute() {
    // before starting thread you can pre process few things here if needed
}

@Override
protected Void doInBackground(Void... params) {

// do whatever you want to do here like calling your API and return your result return null; }

@Override
protected void onPostExecute(Void result) {
    super.onPostExecute(result);
    // after doIn Background this method is called which will set the meesage object and give it back to handler
    Message message = new Message();
    message.obj = result;
    myHandler.sendMessage(message);
}

}

call this async class as,

new AsyncTaskThread(this, new MyHandler()).execute();

and You will have to put this handler class inside the class you are putting above line, depending upon the result you get in handle you can perform further operations,

    private class MyHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
    }
}
Rohit Jagtap
  • 1,660
  • 1
  • 14
  • 12
  • Thanks, but this would mean for each API call I need to write an extra async task, basically wrapping my async methods into async tasks. I do not want to alter my code base to such extend just for the purpose of being able to run unit tests. The sequential execution I need for the purpose of testing my existing code. – Zbyslaw Railerski Sep 02 '15 at 10:22
  • Well this won't be much of a change, as you just have to create a seprate async class, put the API call i doInBackground() method and then in handler you can proceed with the DB calls, anyways, choice is yours as I am not actually aware of the actual requirements – Rohit Jagtap Sep 02 '15 at 10:28
  • I have accomplished this challenge using the CountDownLatch and storing the signal globally. Once the async method completes I do: AppMemory.doneSignal.countDown(); and in case of success AppMemory.apiCallResult = true; – Zbyslaw Railerski Sep 11 '15 at 11:40