1

I have a ArrayAdapter which updates the ListView by fetching data from an API. API is working fine and I'm getting the data using an AsyncTask.

The OnPostExecute method should clear the ArrayAdapter and add new data to it.

But I'm facing some errors saying java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ArrayAdapter.clear()' on a null object reference

package com.example.sunshine;

import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.format.Time;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * A simple {@link Fragment} subclass.
 * Activities that contain this fragment must implement the
 * {@link WeatherFragment.OnFragmentInteractionListener} interface
 * to handle interaction events.
 * Use the {@link WeatherFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class WeatherFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";
    private ArrayAdapter<String> mAdapter;
    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;
    public EditText locationCity;

    private OnFragmentInteractionListener mListener;

    public WeatherFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment WeatherFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static WeatherFragment newInstance(String param1, String param2) {
        WeatherFragment fragment = new WeatherFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_weather, container, false);


        String[] forecastArray = {"Mon 6/23 - Sunny - 31/17",
                "Tue 6/24 - Foggy - 21/8",
                "Wed 6/25 - Cloudy - 22/17",
                "Thurs 6/26 - Rainy - 18/11",
                "Fri 6/27 - Foggy - 21/10",
                "Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
                "Sun 6/29 - Sunny - 20/7"};
        List<String> weekForecast = new ArrayList<>(Arrays.asList(forecastArray));
        mAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, weekForecast );
        mAdapter.notifyDataSetChanged();
        ListView listView = (ListView) view.findViewById(R.id.listView1);
        Log.d("Log", "If listview is null "+listView);
        listView.setAdapter(mAdapter);

        return view;

    }


    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p/>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }

    public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {

        private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();

        /* The date/time conversion code is going to be moved outside the asynctask later,
 * so for convenience we're breaking it out into its own method now.
 */
        private String getReadableDateString(long time){
            // Because the API returns a unix timestamp (measured in seconds),
            // it must be converted to milliseconds in order to be converted to valid date.
            SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd");
            return shortenedDateFormat.format(time);
        }

        /**
         * Prepare the weather high/lows for presentation.
         */
        private String formatHighLows(double high, double low) {
            // For presentation, assume the user doesn't care about tenths of a degree.
            long roundedHigh = Math.round(high);
            long roundedLow = Math.round(low);

            return roundedHigh + "/" + roundedLow;

        }

        /**
         * Take the String representing the complete forecast in JSON Format and
         * pull out the data we need to construct the Strings needed for the wireframes.
         *
         * Fortunately parsing is easy:  constructor takes the JSON string and converts it
         * into an Object hierarchy for us.
         */
        private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays)
                throws JSONException {

            // These are the names of the JSON objects that need to be extracted.
            final String OWM_LIST = "list";
            final String OWM_WEATHER = "weather";
            final String OWM_TEMPERATURE = "temp";
            final String OWM_MAX = "max";
            final String OWM_MIN = "min";
            final String OWM_DESCRIPTION = "main";

            JSONObject forecastJson = new JSONObject(forecastJsonStr);
            JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);

            // OWM returns daily forecasts based upon the local time of the city that is being
            // asked for, which means that we need to know the GMT offset to translate this data
            // properly.

            // Since this data is also sent in-order and the first day is always the
            // current day, we're going to take advantage of that to get a nice
            // normalized UTC date for all of our weather.

            Time dayTime = new Time();
            dayTime.setToNow();

            // we start at the day returned by local time. Otherwise this is a mess.
            int julianStartDay = Time.getJulianDay(System.currentTimeMillis(), dayTime.gmtoff);

            // now we work exclusively in UTC
            dayTime = new Time();

            String[] resultStrs = new String[numDays];
            for(int i = 0; i < weatherArray.length(); i++) {
                // For now, using the format "Day, description, hi/low"
                String day;
                String description;
                String highAndLow;

                // Get the JSON object representing the day
                JSONObject dayForecast = weatherArray.getJSONObject(i);

                // The date/time is returned as a long.  We need to convert that
                // into something human-readable, since most people won't read "1400356800" as
                // "this saturday".
                long dateTime;
                // Cheating to convert this to UTC time, which is what we want anyhow
                dateTime = dayTime.setJulianDay(julianStartDay+i);
                day = getReadableDateString(dateTime);

                // description is in a child array called "weather", which is 1 element long.
                JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);
                description = weatherObject.getString(OWM_DESCRIPTION);

                // Temperatures are in a child object called "temp".  Try not to name variables
                // "temp" when working with temperature.  It confuses everybody.
                JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE);
                double high = temperatureObject.getDouble(OWM_MAX);
                double low = temperatureObject.getDouble(OWM_MIN);

                highAndLow = formatHighLows(high, low);
                resultStrs[i] = day + " - " + description + " - " + highAndLow;
            }

            for (String s : resultStrs) {
                Log.v(LOG_TAG, "Forecast entry: " + s);
            }
            return resultStrs;

        }

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

            if(params.length == 0){
                return null;
            }

            HttpURLConnection urlConnection = null;
            BufferedReader reader = null;
            String format = "json";
            String units = "metric";
            int numDays = 7;
            String forecastJSONStr = null;

            try {
                final String BASE_URL = "http://api.openweathermap.org/data/2.5/forecast/daily?";
                final String QUERY_PARAM = "q";
                final String FORMAT_PARAM = "mode";
                final String UNITS_PARAM = "units";
                final String COUNT_PARAM = "cnt";
                final String APPID_PARAM = "APPID";


                Uri builtUri = Uri.parse(BASE_URL).buildUpon()
                        .appendQueryParameter(QUERY_PARAM, params[0])
                        .appendQueryParameter(FORMAT_PARAM, format)
                        .appendQueryParameter(UNITS_PARAM, units)
                        .appendQueryParameter(COUNT_PARAM, Integer.toString(numDays))
                        .appendQueryParameter(APPID_PARAM, BuildConfig.OWMAPIKey)
                        .build();


                URL url = new URL(builtUri.toString());
                Log.v(LOG_TAG, "Built Uri"+ builtUri.toString());

                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();

                InputStream inputStream = urlConnection.getInputStream();
                StringBuilder buffer = new StringBuilder();

                if(inputStream == null){
                    forecastJSONStr = null;
                }
                if(inputStream != null){
                    reader = new BufferedReader(new InputStreamReader(inputStream));
                }

                String line;
                if(reader != null){
                    while((line = reader.readLine()) != null){
                        buffer.append(line).append('\n');
                    }
                }

                if(buffer.length() == 0){
                    forecastJSONStr = null;
                }

                forecastJSONStr = buffer.toString();
                Log.e(LOG_TAG, "Forecast String " + forecastJSONStr);
            } catch (ProtocolException | MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(LOG_TAG, "onCreateView: Error", e);
                forecastJSONStr = null;
            }finally {
                if(urlConnection != null){
                    urlConnection.disconnect();
                }
                if(reader != null){
                    try {
                        reader.close();
                    } catch (final IOException e) {
                        Log.e(LOG_TAG, "Error closing stream ", e);
                    }
                }
            }
            try {
                if(forecastJSONStr != null){
                    Log.v(LOG_TAG, "doInBackground: "+forecastJSONStr);
                    String[] results= getWeatherDataFromJson(forecastJSONStr, numDays);
                    Log.v(LOG_TAG, "doInBackground: Check result" +Arrays.toString(results));
                    return results;
//                    return getWeatherDataFromJson(forecastJSONStr, numDays);
                }
                else{
                    Log.e(LOG_TAG, "Check your internet!" );
                }
            }catch (JSONException e){
                Log.e(LOG_TAG, e.getMessage(), e);
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(String[] result) {
            Log.v(LOG_TAG, "onPostExecute: Check result object" + Arrays.toString(result));
            Log.i(LOG_TAG, "onPostExecute: "+mAdapter);
            mAdapter.clear();
            mAdapter.addAll(result);
//            if(result != null){
////                for(String dayForecastStr : result) {
////                    mAdapter.add(dayForecastStr);
////                }
//
//            }
//            super.onPostExecute(result);
        }
    }
}

Check Line 351 for the exact line which is causing the problem.

Apologies if it's a noob question, but I am new to Android Development.

7
  • post the relevant code here instead of linking us to see/check some repo sources... Commented Apr 2, 2016 at 10:47
  • Try to set Adapter in on Post Execute method. Transfer that from on Create View to on Post Execute. Commented Apr 2, 2016 at 10:51
  • @itsashis4u Post code snippets from the repo (you can use it as a secondary source, though). Show where you instantiate your ArrayAdapter and show where you attempt to clear it. Code dumps and "Why doesn't this work?" are not good questions. Commented Apr 2, 2016 at 10:52
  • 1
    Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Complete, and Verifiable example Commented Apr 2, 2016 at 10:52
  • @TEK I have initialized the ArrayAdapter here . Tried to clear it here Commented Apr 2, 2016 at 10:56

2 Answers 2

3

This is quite a common problem. One way to think about this could be that after an AsyncTask is started, you don't really have much control over when it will end, since that depends on a number of factors, task size, etc. So, the view that called the AsyncTask itself might not be available.

Since, in your case, the mAdapter is getting set to null thus causing a NullPointerException, I would suggest you do one of the following.

  • inside the onPostExecute(..), start an activity on the UI thread that first re-initializes your mAdapter if it is null (if the view itself has become inactive, this wont'work)

  • pass a callback handler function to your AsyncTask; the function must be in WeatherFragment to ensure that the fragment is initialized and running when the AsyncTask returns; again re-initialize mAdapter if it is null and update UI accordingly based on data received from AsyncTask.

Unfortunately, given that you have not posted a more specific problem, these are the only 2 suggestions that comes to mind. You will be able to find numerous resources for each of these design choices on google. Let me know if either way worked.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. I am new to Android Dev. Need to understand the basics.
@itsashis4u Did it help in any way?
Yes, the problem was related to activity lifecycle.
1

In WeatherFragment.java at line no 251

mAdapter.clear();

first try to check isEmpty using this code

if(mAdapter.getCount()!=0 && mAdapter!=null){
  mAdapter.clear();
}

else you can assign new one like

mAdapter = new ArrayAdapter<String>(result);

this and remove mAdapter.clear();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.