0

I need that my ArrayAdapter will be loaded with data coming of HTTP Request.

My onCreate method:

private Spinner spiCities;
private ArrayAdapter<String> citiesAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    new Thread() {
        @Override
        public void run() {
            LoadCities lc = new LoadCities();
            lc.execute();
        }
    }.start();
}

I have a inner class in Activity:

class LoadCities extends AsyncTask<Void, Void, String> {
    @Override
    protected String doInBackground(Void... voids) {
        BufferedReader br = null;

        try {
            URL url = new URL("http://10.0.2.2/inter/api/loadCities.php");
            HttpURLConnection con = (HttpURLConnection) url.openConnection();

            if ( con.getResponseCode() != 200 ) {
                throw new RuntimeException( "Error: " + con.getResponseMessage()  );
            }

            br = new BufferedReader( new InputStreamReader( con.getInputStream() ) );
        } catch ( MalformedURLException e ) {
            e.printStackTrace();
        } catch ( IOException e ) {
            e.printStackTrace();
        }

        if ( null == br ) {
            return "[]";
        }
        else {
            StringBuilder sb = new StringBuilder();
            try {
                String linha;
                while ( (linha = br.readLine()) != null ) {
                    sb.append( linha );
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            return sb.toString();
        }
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);

        s = s.substring(1);
        s = s.substring(0, s.length()-1);

        List<String> listCities = new ArrayList<>();
        String s1;

        for ( String aux : s.split(",") ) {
            s1 = aux.substring(1);
            s1 = s1.substring(0, s1.length()-1);

            listaCidades.add(s1);
        }

        citiesAdapter = new ArrayAdapter<String>( context, android.R.layout.simple_spinner_dropdown_item, listCities);
        citiesAdapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
        spiCities.setAdapter( citiesAdapter );
    }
}

The error says that the "citiesAdapter" object is null:

FATAL EXCEPTION: main Process: br.com.interativa, PID: 21386 java.lang.RuntimeException: Unable to start activity ComponentInfo{br.com.interativa/br.com.interativa.FiltrarEventoActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Object.equals(java.lang.Object)' on a null object reference at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856) at android.app.ActivityThread.-wrap11(Unknown Source:0) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6494) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807) Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.Object.equals(java.lang.Object)' on a null object reference at br.com.inter.FilterEventActivity.loadCities(FilterEventActivity.java:183)

What am I doing wrong?

EDIT

I researched a lot here in the forum before posting my question and really did not find anything like it!

My problem was marked as duplicate of another post that asks ONLY about Null Pointer in basic and pure Java. My problem has nothing to do with simple Null Pointer and yes with JSON values ​​coming through a Request / Respose within the Android Async.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Donadon
  • 113
  • 11
  • Hey @Ridcully. You marked me as unfairly duplicated. The post you used as a reply is nowhere near my problem! – Donadon Feb 28 '19 at 16:08
  • Of course, it is: all NPEs are the same story. You used an object before referencing it. – Phantômaxx Feb 28 '19 at 16:12
  • I agree with @Donadon, it shouldn't be considered a duplicate of a NPE question. NPE is a very general subject and obviously millions of cases would be duplicated of it. In this case, I think is more about how to use AsyncTask and not why a NPE would occur in the first place. – Rodrigo Bagni Feb 28 '19 at 16:24
  • What is context variable and where do you initialise it? Also I do not see any call to setContentView/findViewById for inflating/linking the views and then your Spinner could be null when setting the adapter. – Miguel Isla Feb 28 '19 at 16:29
  • I agree that NPE happens because an object was called before creating the pointer or reference however as @Rodrigo said, each case is a case. I do not want to cause controversy with this but sometimes what is obvious to one, is news to another and I researched a lot before posting. – Donadon Feb 28 '19 at 19:32
  • 1
    Many thanks for your comments @MiguelIsla, really the problem was not there but in ASyncTask. I got it sorted out there. – Donadon Feb 28 '19 at 19:35
  • The reason of your problem is the NPE which is clearly visible in your stacktrace. You only have to read it, to see exactly at which line of your code the problem is. – Ridcully Feb 28 '19 at 19:40
  • I agree with you @Ridcully!!! I didn't knew that it was possible to control the AsyncThread by onProgressUpdate to evit NPE. Simply instance the citiesAdapter to evit NPE don't will fixing my problem of wait for data. – Donadon Feb 28 '19 at 20:11

1 Answers1

3

You instantiate citiesAdapter in the AsyncTask, which runs in the background, that is, you never know when it's going to start running or finishing. If you try to access the variable citiesAdapter anywhere else in you Activity, citiesAdapter may or may not have been instantiated already. Therefore you can either execute everything related to citiesAdapter in the onPostExecute method or wait for the variable to be instantiated before trying to use it.

If you really need to wait for onPostExecute to finish in order to execute something else in your Activity, then you should implement onProgressUpdate to inform users that you Activity is loading something.

There is one thing that you don't need to do at all. You are creating a Thread and then running the AsyncTask inside it. That's completely unnecessary because AsyncTask already creates a Thread for you, that's why it exists. So, don't use "new Thread()".

  • Thank you very much for the explanation @Rodrigo. I used a Toast inside the onPostExecute and it responded almost instantly to the opening of the Activity. So theoretically the object should no longer be null. I'm going to implement onProgressUpdate to test! – Donadon Feb 28 '19 at 16:00
  • That's my point @Donadon, if you do everything inside onPostExecute, then you have control of the execution flow of your code inside the AyncTask. But if do something inside there and other things outside of the AsyncTask that depends on the result of it, you might end up with a race condition issue., which I think is what is happening here. – Rodrigo Bagni Feb 28 '19 at 16:28
  • That's it @Rodrigo! I was able to solve the problem by leaving the Spinner invisible in the onProgressUpdate and made it visible and set the Adapter on onPostExecute. I'll mark your answer as a solution because it solved the problem. I just wanted to instead of making it invisible, a suspended View with a Loading field for the user to wait but I do not know how to do that yet, would you point me somewhere to study this? – Donadon Feb 28 '19 at 19:39