java.lang.NullPointerException:尝试调用虚方法' void android.widget.ArrayAdapter.clear()'在null对象引用上

时间:2016-04-02 10:44:42

标签: java android

我有一个ArrayAdapter,它通过从API获取数据来更新ListView。 API工作正常,我使用AsyncTask获取数据。

OnPostExecute方法应清除ArrayAdapter并向其添加新数据。

但我面临一些错误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);
        }
    }
}

检查Line 351以查找导致问题的确切行。

道歉,如果它是一个菜鸟问题,但我是Android开发新手。

2 个答案:

答案 0 :(得分:3)

这是一个非常普遍的问题。考虑这个问题的一种方法可能是在AsyncTask开始之后,你真的无法控制它何时结束,因为这取决于许多因素,任务规模等。所以,调用AsyncTask本身的视图可能无法使用。

因为在您的情况下,mAdapter设置为null因此导致NullPointerException,我建议您执行以下操作之一。

  • 在onPostExecute(..)中,在UI线程上启动一个活动,首先重新初始化mAdapter,如果它是null(如果view本身有变得不活跃,这不会工作)

  • 将回调处理函数传递给AsyncTask;该函数必须在WeatherFragment中,以确保在AsyncTask返回时初始化并运行该片段;如果它为null,则再次重新初始化mAdapter,并根据从AsyncTask收到的数据相应地更新UI。

不幸的是,鉴于您没有发布更具体的问题,这些只是我想到的两条建议。您将能够在谷歌上找到这些设计选择的众多资源。如果有任何一种方法,请告诉我。

答案 1 :(得分:1)

在第25行的WeatherFragment.java

  

mAdapter.clear();

首先尝试使用此代码检查isEmpty

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

否则您可以指定新的

mAdapter = new ArrayAdapter<String>(result);

这个并删除mAdapter.clear();