Mauricio Reis
Mauricio Reis

Reputation: 76

Android: Wait for a response by API before continue the execution using Retrofit

I'm facing a problem on my android programming, I have an app that needs run some routines like scan some barcode. Once that I scanned a barcode I will send the data of this barcode to my API, "wait" to get some information and after that do another things.

The problem it's when I call my API to get the information and after I try to use the information that was supposed to come from the API, I'm falling on a NULL Exception, see the code below:

My scan routine that is called every time I scan a barcode:

    // 1 - First Stage: Scan barcode of tank
    if(stageScannerTank == 1)
    {
        // Verify if the user scanned the correct Tank
        if (!resultScan.equals(expectedTank)) {
            Toast.makeText(getContext(), "Wrong Tank, scan the correct ", Toast.LENGTH_LONG).show();
            stageScannerTank = 0;
            return;
        }


        // Get the tank information

        //new TanqueTask().execute(resultScan);

        **HERE IS MY PROBLEM, When I call the function below, I fill my tankMixPlant with data**

        getTankMixPlant(resultScan);

        ** I always fall into this IF **
        // If null information
        if(tankMixPlant == null) {
            Toast.makeText(getContext(), "Error to read tank information", Toast.LENGTH_LONG).show();
            return;
        }

    }

getTankMixPlant, using Retrofit to call my service, it is working if I test out of my routine:

private void getTankMixPlant(String tankName){
    mService.getTankMixPlant(tankName).enqueue(new Callback<TankMixPlant>() {
        @Override
        public void onResponse(Call<TankMixPlant> call, Response<TankMixPlant> response) {
            tankMixPlant = response.body();

        }

        @Override
        public void onFailure(Call<TankMixPlant> call, Throwable t) {
            // On failure show the error message to the User
            AlertDialog alert;
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle("Error");
            builder.setMessage("Error: " + t.getMessage());
            alert = builder.create();
            alert.show();
        }
    });
}

How can I wait tankMixPlant be filled up with data to continue my routine?

UPDATES:

I continue with the problem, but I changed some things that you can see below: Now I get NullPointerException.

Now I verify if my object is null inside the onResponse method:

     private void getTankMixPlant(String tankName){
    mService.getTankMixPlant(tankName).enqueue(new Callback<TankMixPlant>() {
        @Override
        public void onResponse(Call<TankMixPlant> call, Response<TankMixPlant> response) {
            tankMixPlant = response.body();
            if(tankMixPlant == null) {
              Toast.makeText(getContext(), "Error to read tank information", 
              Toast.LENGTH_LONG).show();
              return;
           }
        }

        @Override
        public void onFailure(Call<TankMixPlant> call, Throwable t) {
            // On failure show the error message to the User
            AlertDialog alert;
            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle("Error");
            builder.setMessage("Error: " + t.getMessage());
            alert = builder.create();
            alert.show();
        }
    });
}

But when I try to verify some information about tankMixPlant:

        // Verify tank stage
        if(!tankMixPlant.getBlenderCode().equals("0") && tankMixPlant.getIngr() == 1)
        {
            getPackages(tankMixPlant.getRecipeNumber(), tankMixPlant.getBlenderCode(), tankMixPlant.getTankName());
            // 2 - Mix Packages: Scan package barcode
            stageScannerTank = 2;
        }

        if(!tankMixPlant.getIngrSAPNumber().equals("0") && tankMixPlant.getIngr() == 1)
        {
            getIngr(tankMixPlant.getRecipeNumber(), tankMixPlant.getTankName());
            // 3 - P.I Packages: Scan package barcode
            stageScannerTank = 3;
        }

My app crashes with this error:

E/AndroidRuntime: FATAL EXCEPTION: main
              Process: com.example.labtse.kibonapp, PID: 21519
              java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=114910, result=-1, data=Intent { act=com.google.zxing.client.android.SCAN flg=0x80000 VirtualScreenParam=Params{mDisplayId=-1, null, mFlags=0x00000000)} (has extras) }} to activity {com.example.labtse.kibonapp/com.example.labtse.kibonapp.TabPagesActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.example.labtse.kibonapp.model.TankMixPlant.getBlenderCode()' on a null object reference
                  at android.app.ActivityThread.deliverResults(ActivityThread.java:5007)
                  at android.app.ActivityThread.handleSendResult(ActivityThread.java:5050)
                  at android.app.ActivityThread.access$1600(ActivityThread.java:230)
                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1876)
                  at android.os.Handler.dispatchMessage(Handler.java:102)
                  at android.os.Looper.loop(Looper.java:148)
                  at android.app.ActivityThread.main(ActivityThread.java:7409)
                  at java.lang.reflect.Method.invoke(Native Method)
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
               Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String com.example.labtse.kibonapp.model.TankMixPlant.getBlenderCode()' on a null object reference
                  at com.example.labtse.kibonapp.MixPlantFragment.scanRoutine(MixPlantFragment.java:367)
                  at com.example.labtse.kibonapp.MixPlantFragment.onActivityResult(MixPlantFragment.java:399)
                  at android.support.v4.app.FragmentActivity.onActivityResult(FragmentActivity.java:151)
                  at android.app.Activity.dispatchActivityResult(Activity.java:7165)
                  at android.app.ActivityThread.deliverResults(ActivityThread.java:5003)
                  at android.app.ActivityThread.handleSendResult(ActivityThread.java:5050) 
                  at android.app.ActivityThread.access$1600(ActivityThread.java:230) 
                  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1876) 
                  at android.os.Handler.dispatchMessage(Handler.java:102) 
                  at android.os.Looper.loop(Looper.java:148) 
                  at android.app.ActivityThread.main(ActivityThread.java:7409) 
                  at java.lang.reflect.Method.invoke(Native Method) 
                  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230) 
                  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120) 

My tankMixPlant class:

package com.example.labtse.kibonapp.model;

public class TankMixPlant {
    private String tankName;
    private String recipeNumber;
    private int idBatch;
    private String ingrSAPNumber;
    private String blenderCode;
    private short ingr;
    private boolean QRUN;
    private boolean QEND;
    private Boolean QDOSANDO;
    private Boolean CONF_ING;

    public TankMixPlant() {
    }

    public String getTankName() {
        return tankName;
    }

    public void setTankName(String tankName) {
        this.tankName = tankName;
    }

    public String getRecipeNumber() {
        return recipeNumber;
    }

    public void setRecipeNumber(String recipeNumber) {
        this.recipeNumber = recipeNumber;
    }

    public int getIdBatch() {
        return idBatch;
    }

    public void setIdBatch(int idBatch) {
        this.idBatch = idBatch;
    }

    public String getIngrSAPNumber() {
        return ingrSAPNumber;
    }

    public void setIngrSAPNumber(String ingrSAPNumber) {
        this.ingrSAPNumber = ingrSAPNumber;
    }

    public String getBlenderCode() {
        return blenderCode;
    }

    public void setBlenderCode(String blenderCode) {
        this.blenderCode = blenderCode;
    }

    public short getIngr() {
        return ingr;
    }

    public void setIngr(short ingr) {
        this.ingr = ingr;
    }

    public boolean isQRUN() {
        return QRUN;
    }

    public void setQRUN(boolean QRUN) {
        this.QRUN = QRUN;
    }

    public boolean isQEND() {
        return QEND;
    }

    public void setQEND(boolean QEND) {
        this.QEND = QEND;
    }

    public Boolean getQDOSANDO() {
        return QDOSANDO;
    }

    public void setQDOSANDO(Boolean QDOSANDO) {
       this.QDOSANDO = QDOSANDO;
    }

    public Boolean getCONF_ING() {
        return CONF_ING;
    }

    public void setCONF_ING(Boolean CONF_ING) {
        this.CONF_ING = CONF_ING;
    }

}

Upvotes: 3

Views: 8710

Answers (2)

Solution:

You must write this:

    if(tankMixPlant == null) {
        Toast.makeText(getContext(), "Error to read tank information", Toast.LENGTH_LONG).show();
        return;
    }

Inside your onResponse, after the line tankplant = ...

Now, null exception will be eliminated if you are getting any data from the server.

Hope it helps.

Update1:

Write this:

    if(!tankMixPlant.getBlenderCode().equals("0") && tankMixPlant.getIngr() == 1)
    {
        getPackages(tankMixPlant.getRecipeNumber(), tankMixPlant.getBlenderCode(), tankMixPlant.getTankName());
        // 2 - Mix Packages: Scan package barcode
        stageScannerTank = 2;
    }

    if(!tankMixPlant.getIngrSAPNumber().equals("0") && tankMixPlant.getIngr() == 1)
    {
        getIngr(tankMixPlant.getRecipeNumber(), tankMixPlant.getTankName());
        // 3 - P.I Packages: Scan package barcode
        stageScannerTank = 3;
    }

in your onResponse method as shown below:

 private void getTankMixPlant(String tankName){
mService.getTankMixPlant(tankName).enqueue(new Callback<TankMixPlant>() {
    @Override
    public void onResponse(Call<TankMixPlant> call, Response<TankMixPlant> response) {
       tankMixPlant = response.body();
       if(tankMixPlant == null) {
          Toast.makeText(getContext(), "Error to read tank information", 
          Toast.LENGTH_LONG).show();
          return;
       }

       ...... (Write Here)

    }

    @Override
    public void onFailure(Call<TankMixPlant> call, Throwable t) {
        // On failure show the error message to the User
        AlertDialog alert;
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle("Error");
        builder.setMessage("Error: " + t.getMessage());
        alert = builder.create();
        alert.show();
    }
});

Try it.

Upvotes: 2

Mauker
Mauker

Reputation: 11487

The problem is that the retrofit call is done asynchronously, while the rest of your code isn't. That's why your data is null, since it's nor ready yet.

First of all, you have to remove the part of the code where you're checking for the "pending data", and move it inside your retrofit onResponse or somewhere else that'll be later called after the network response by using callbacks.

There are quite a few ways on exactly how you can do that. I'd suggest that you read the documentation and some tutorials like this one, and this other one.

Also, while you're making the retrofit call, it's best to display a loading message to the user, to keep him aware that you're making a network call and is waiting for the results.

Upvotes: 2

Related Questions