BretD
BretD

Reputation: 366

Android custom adapter and asyncTask not updating listView

I've searched all the posts I can find, and none seem to help with my situation. I have an android project that uses web services to pull down hourly weather data and populate a listView with the results.

The weird problem I'm having is that when I debug the project on my android phone, the main activity is blank and the listView isn't populated. If I run the project from android studio with my phone locked, and then unlock my phone the app opens on my phone with all of the listView properly formatted and populated.

I feel like it's a race condition issue between the asynctask and the adapter, but I can't seem to resolve it. I tried making my asyncTask an inner private class and calling notifyDataSetChanged on the adapter inside the onPostExecute method, but to no avail. I feel it must be something simple, but I'm relatively new to Android dev, so I'm stuck.

I have three classes that I'll post the pertinent code from

MainActivity.java (onCreate)

public class MainActivity extends ActionBarActivity {

    ArrayList<Weather> w = new ArrayList<Weather>();

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

        DownloadWeatherTask myTask = new DownloadWeatherTask(w);
        WeatherAdapter myAdapter = new WeatherAdapter(this,w);


        ListView l = (ListView) findViewById(R.id.weatherList);
        l.setAdapter(myAdapter);

        myTask.execute();
    }
}

WeatherAdapter.java

public class WeatherAdapter extends ArrayAdapter<Weather>{

    public WeatherAdapter(Context context, ArrayList<Weather> weather) {
        super(context, R.layout.item_weather, weather);
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        // Get the data item for this position
        Weather forecast = getItem(position);
        // Check if an existing view is being reused, otherwise inflate the view
        if (convertView == null) {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_weather, parent, false);
        }
        // Lookup view for data population
        TextView tvTime = (TextView) convertView.findViewById(R.id.listTime);
        TextView tvDescr = (TextView) convertView.findViewById(R.id.listDescr);
        TextView tvTemp = (TextView) convertView.findViewById(R.id.listTemp);
        TextView tvHumid = (TextView) convertView.findViewById(R.id.listHumid);
        ImageView ivWeather = (ImageView)     convertView.findViewById(R.id.weatherImg);
        // Populate the data into the template view using the data object
        tvTime.setText(forecast.time);
        tvDescr.setText(forecast.description);
        tvTemp.setText(forecast.temperature+"°(F)");
        tvHumid.setText(forecast.humidity+"% humidity");
        ivWeather.setImageBitmap(forecast.weatherImg);
        // Return the completed view to render on screen
        return convertView;
    }

}

DownloadWeatherTask.java

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

    ArrayList<Weather> data;

    public DownloadWeatherTask(ArrayList<Weather> a){
        data = a;
    }

    public ArrayList<Weather> getData() {
        return data;
    }


    protected Void doInBackground(Void...params) {
        try {
            String website =     "http://api.wunderground.com/api/1111111111111/geolookup/q/autoip.json";
            URL site = new URL(website);
            HttpURLConnection weatherUnderground = (HttpURLConnection)     site.openConnection();
            weatherUnderground.connect();

            JsonParser weatherParser = new com.google.gson.JsonParser();

            JsonElement weatherJson = weatherParser.parse(new InputStreamReader((InputStream) weatherUnderground.getContent()));

            JsonObject weatherObj = weatherJson.getAsJsonObject();

            String zip = weatherObj.get("location").getAsJsonObject().get("zip").getAsString();
            String city = weatherObj.get("location").getAsJsonObject().get("city").getAsString();
            String state = weatherObj.get("location").getAsJsonObject().get("state").getAsString();

            String hourly = "http://api.wunderground.com/api/111111111111/hourly/q/" + state + "/" + city + ".json";
            URL hourlySite = new URL(hourly);
            HttpURLConnection hourlyConnection = (HttpURLConnection) hourlySite.openConnection();
            hourlyConnection.connect();

            com.google.gson.JsonParser hourlyParser = new com.google.gson.JsonParser();

            JsonElement hourlyWeatherJson = weatherParser.parse(new InputStreamReader((InputStream) hourlyConnection.getContent()));

            JsonArray weatherArr = hourlyWeatherJson.getAsJsonObject().get("hourly_forecast").getAsJsonArray();
            int l = weatherArr.size();

            for (int i = 0; i < l; i++) {
                String date = weatherArr.get(i).getAsJsonObject().get("FCTTIME").getAsJsonObject().get("pretty").getAsString();
                String temp = weatherArr.get(i).getAsJsonObject().get("temp").getAsJsonObject().get("english").getAsString();
                String condition = weatherArr.get(i).getAsJsonObject().get("condition").getAsString();
                String humidity = weatherArr.get(i).getAsJsonObject().get("humidity").getAsString();
                String iconUrl = weatherArr.get(i).getAsJsonObject().get("icon_url").getAsString();
                Bitmap icon = getBitmapFromURL(iconUrl);
                data.add(new Weather(date, condition, temp, humidity, icon));
            }
        } catch (IOException e) {
            Log.e("Error: ",e.toString());
        }

        return null;

    }


    protected void onPostExecute(Void...params){

    }
}

Below are links to my screenshots showing the app not populating the listView, and the app working properly when the program is run while the phone is initially locked.

Any help would be greatly appreciated!!

Thanks

Upvotes: 0

Views: 3430

Answers (2)

BretD
BretD

Reputation: 366

I figured out what the issue was! I hadn't added @Override to my onPostExecute() method so it was never being called.

I added the notifyDataSetChanged to my onPostExecute as suggested, which worked once I added the @override to my method.

Upvotes: 0

GreyBeardedGeek
GreyBeardedGeek

Reputation: 30088

In postExecute(), you need to update the adapter's List and then invoke its notifyDataSetChanged method. I suspect that you were forgetting to update the adapter's data.

The other option is to create a new adapter with the new data, and set the new adapter on the ListView.

Upvotes: 1

Related Questions