0

I have a JSONObject receiving from an API call within a AsyncTask class. I want to pass that JSON object to my MainActivity class to show the JSONObject data in the GUI. I found some solutions throughout some searches and came up with a close solution. But when i access from the MainActivity class it says the JSONObject is null. What have i dont wrong here? Is this the best way to do this ?

Following is my AsyncTask Class

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

/**
 * Created by Nisal on 13-Sep-17.
 */

public class GetStationsAPICall extends AsyncTask<String, Void, JSONObject> {

    Context ctx;
    JSONObject responseObj;
    String result;

    public interface AsyncResponse {
        void processFinish(JSONObject output);
    }

    public AsyncResponse delegate = null;

    public GetStationsAPICall(AsyncResponse delegate){
        this.delegate = delegate;
    }

//    GetStationsAPICall(Context ctx){
//        this.ctx=ctx;
//    }

    @Override
    protected JSONObject doInBackground(String... params) {

        String method = params[0];

        if(method.equals("getStations")){

            try {
                HttpClient client = new DefaultHttpClient();
                String getURL = "http://api.gate.com/?lang=en";
                HttpGet httpGet = new HttpGet(getURL);
                httpGet .setHeader("Authorization", "Bearer 690");

                HttpResponse response = client.execute(httpGet);
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    //parse response.
                    Log.e("Response", EntityUtils.toString(resEntity));
//                    return "Successfully Connected!";
                }else{
//                    return "Connection Failed!";
                }

            } catch (Exception e) {
                e.printStackTrace();
//                return "Connection Failed!";
            }
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected void onPostExecute(JSONObject obj) {
        delegate.processFinish(obj);
    }
}

Following is my MainActivity Class

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import org.json.JSONObject;

public class MainActivity extends Activity implements GetStationsAPICall.AsyncResponse{

    Button btnSearch;
    String method = "getStations";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GetStationsAPICall getStations = new GetStationsAPICall(this);
        new GetStationsAPICall(this).execute(method);
    }

    public void searchClicked(View view){
        Toast.makeText(MainActivity.this,"Search Clicked",Toast.LENGTH_SHORT).show();
    }

    @Override
    public void processFinish(JSONObject output) {
        Toast.makeText(MainActivity.this,"ProcessFinish",Toast.LENGTH_SHORT).show();

        if(output != null){
            Toast.makeText(MainActivity.this,"not null",Toast.LENGTH_SHORT).show();
        }else{
            Toast.makeText(MainActivity.this," null",Toast.LENGTH_SHORT).show();
        }
    }
}

I can get the JSONObject within the AsyncTask class, But when i try to pass it to the MainActivity class and use it there. The JSONObject becomes null. What have I done wrong here?

2 Answers2

2

Although I'm sure you got a working solution, I wouldn't recommend making your activity into a listener that could potentially leak it (what happens if you press the home button before you get a result back?). Also, as mentioned previously, the main problem you had was that you were returning null from your doInBackground function. So let's address these two issues for now:

MainActivity.java

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import android.content.BroadcastReceiver;
import android.content.IntentFilter;
import android.support.v4.content.LocalBroadcastManager;

import org.json.JSONObject;

public class MainActivity extends Activity {

    Button btnSearch;
    String method = "getStations";
    BroadcastReceiver apiListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        BroadcastReceiver apiListener = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String output = intent.getStringExtra("API_Response); //Getting the string extra you input earlier in the AsyncTask.
                Toast.makeText(MainActivity.this,"ProcessFinish",Toast.LENGTH_SHORT).show();

                if(output != null){
                    Toast.makeText(MainActivity.this,"not null",Toast.LENGTH_SHORT).show();
                }else{
                    Toast.makeText(MainActivity.this," null",Toast.LENGTH_SHORT).show();
                }
            }
        };

        //Since you are starting your AsyncTask here, might want to set up the receiver prior to calling the task.
        LocalBroadcastManager.getInstance(this).registerReceiver(apiListener, new IntentFilter(""));

        GetStationsAPICall getStations = new GetStationsAPICall(this);
        new GetStationsAPICall(this).execute(method);
    }

    public void searchClicked(View view){
        Toast.makeText(MainActivity.this,"Search Clicked",Toast.LENGTH_SHORT).show();
    }

    //Registers the receiver whenever this acitivty is currently being shown or about to.
    @Override
    public void onStart() {
        super.onStart();
        LocalBroadcastManager.getInstance(this)
            .registerReceiver(apiListener, new IntentFilter("StationsAPI")); //This is the intent filter this receiver will be listening for
    }

    //This stops the receiver from listening when the activity is no longer shown.
    @Override
    public void onStop() {
        super.onStop();
        LocalBroadcastManager.getInstance(this).unregisterReceiver(apiListener);
    }
}

I made quite a few changes with some comments describing them. The most important thing to notice here is that this Activity is no longer a listener, as I've replaced that functionality for a LocalBroadcastManager. Using this, I can register as many BroadcastReceiver objects as I wish to handle the response of the AsyncTask, without worrying about interrupting the process of the AsyncTask.

As you can see, I used an IntentFilter to let the manager know that only intents with this action ("StationsAPI") should go to the receiver I registered (apiListener). Also, so I don't have a situation where I potentially leak the activity, I unregister the receiver as soon as the activity is no longer visible, and re-register when it is visible again.

GetStationsAPICall.java

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;

/**
* Created by Nisal on 13-Sep-17.
*/

public class GetStationsAPICall extends AsyncTask<String, Void, String> {

    LocalBroadcastManager localBroadcastManager; //This is the manager to send a result back to your activity

    GetStationsAPICall(Context ctx){
        localBroadcastManager = LocalBroadcastManager.getInstance(ctx); //No matter where you call this, it is the same object throughout your app.
    }

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

        String method = params[0];

        if(method.equals("getStations")){

            try {
                HttpClient client = new DefaultHttpClient();
                String getURL = "http://api.gate.com/?lang=en";
                HttpGet httpGet = new HttpGet(getURL);
                httpGet .setHeader("Authorization", "Bearer 690");

                HttpResponse response = client.execute(httpGet);
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    //parse response.
                    String responseString = EntityUtils.toString(resEntity);
                    Log.e("Response", responseString;
                    return responseString; //This will be the parameter of your onPostExecute method
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    @Override
    protected void onPostExecute(String obj) {
        localBroadcastManager
            .sendBroadcast( //Sending the intent with the response!
                new Intent("StationsAPI") //Remember that IntentFilter? Here is where you declare where to send intent.
                    .putExtra("API_Response", obj)); //This is the extra data you want to send to your receivers.
    }
}

As with the MainActivity class above, I've commented the sections with the changes I made. The JSONObject class isn't parcelable, so instead of creating it and then reconverting it into something I can place inside the Intent, I just left it as a String object, and returned that value in the doInBackground method.

Now whenever you call your GetStationsAPICall task, you should get the response in your MainActivity (or any other receiver with an IntentFilter for "API_Response"). Although this solution itself isn't perfect (what if the user rotates the device?), it should help avoid some of the pitfalls Android has to offer.

Pablo Baxter
  • 2,144
  • 1
  • 17
  • 36
1

You are recreating the GetStationsAPICall object again before executing it. So your AsyncResponse object will be null.

Change your code like this:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GetStationsAPICall getStations = new GetStationsAPICall(this);
        getStations.execute(method);
    }
Bhuvanesh BS
  • 13,474
  • 12
  • 40
  • 66