Android 使用 Volley 的 ListAdapter NullPointerException

Android ListAdapter NullPointerException using Volley

当我的 ListAdapter 在初始化后调用 getCount() 时,我 运行 陷入了获取 NPE 的永久性问题。我使用 Volley 填充了我的 "Sound" 项数组,但应用程序仍然在启动时崩溃。

我一直在使用本教程寻求帮​​助:http://www.androidhive.info/2014/06/android-facebook-like-custom-listview-feed-using-volley/。我的应用程序的棘手之处在于我必须在片段内完成所有操作,因为我有一个选项卡式界面,并且我尝试构建的提要托管在一个选项卡中。

下面是我的代码:

相关代码来自LandingActivity.java:

Tab tab = actionBar.newTab()
                           .setIcon(R.drawable.feed_icon)
                           .setTabListener(new TabListener<SoundFragment>(this, "feed", SoundFragment.class));
        actionBar.addTab(tab);

我的 SoundFragment.java 文件:

public class SoundFragment extends ListFragment {
    private static final String TAG = "SoundFragment";
    private ArrayList<Sound> mSounds;
    private AudioRecorder mPlayer = new AudioRecorder();
    private ImageButton mImageButton;
    SoundAdapter adapter;
    private String URL_FEED = "http://puncture.org/wavesdemo/feed.json";

    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.fragment_sound, parent, false);
            return v;
        }

    @Override
        public void onCreate(Bundle savedInstanceState){
            super.onCreate(savedInstanceState);
            setHasOptionsMenu(true);
            // First check for cached request
            Cache cache = AppController.getInstance().getRequestQueue().getCache();
            Entry entry = cache.get(URL_FEED);
            if (entry != null) {
                // Fetch the data from cache
                try {
                    String data = new String(entry.data, "UTF-8");
                    try {
                        parseJsonFeed(new JSONObject(data));
                        Log.w("DEBUG", "JSON feed parsed from cache!");
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                } catch (UnsupportedEncodingException e) {
                    Log.w("DEBUG", "UnsupportedEncodingException");
                    e.printStackTrace();
                }

            } else {
                // making fresh volley request and getting JSON
                Log.w("DEBUG", "onResponse method hit within JsonObjectRequest");
                JsonObjectRequest jsonReq = new JsonObjectRequest(Method.GET,
                        URL_FEED, null, new Response.Listener<JSONObject>() {


                        @Override
                        public void onResponse(JSONObject response) {
                        Log.w("DEBUG", "onResponse method hit within JsonObjectRequest");
                        VolleyLog.d(TAG, "Response: " + response.toString());
                        if (response != null) {
                        parseJsonFeed(response);
                        }
                        }
                        }, new Response.ErrorListener() {

                        @Override
                        public void onErrorResponse(VolleyError error) {
                        Log.w("DEBUG", "onErrorResponse method hit within JsonObjectRequest");
                        VolleyLog.d(TAG, "Error: " + error.getMessage());
                        }
                        });



                // Adding request to volley request queue
                AppController.getInstance().addToRequestQueue(jsonReq);
                Log.w("DEBUG", "AppController getInstance request queue thing hit.");

            }
            Log.w("DEBUG", "Made it to the end!!");
            adapter = new SoundAdapter(getActivity(), com.littlecloudcollective.waves.R.layout.fragment_sound_item, mSounds);
            setListAdapter(adapter);
            Log.w("DEBUG", "ListAdapter set!");

        }

    /**      * Parsing JSON response and passing the data to feed view list adapter      * */
    private void parseJsonFeed(JSONObject response) {
        try {
            JSONArray feedArray = response.getJSONArray("feed");

            for (int i = 0; i < feedArray.length(); i++) {
                JSONObject feedObj = (JSONObject) feedArray.get(i);

                Sound s = new Sound();
                s.setId(feedObj.getInt("sound_id"));
                s.setSound(feedObj.getString("sound"));
                Log.w("DEBUG", feedObj.getString("sound"));
                s.setSoundViz(feedObj.getString("sound_viz"));
                Log.w("DEBUG", feedObj.getString("sound_viz"));
                s.setLocation(feedObj.getString("location"));
                // Title might be null sometimes
                String feedTime = feedObj.isNull("title") ? null : feedObj
                    .getString("title");
                s.setTitle(feedTime);
                s.setTime(feedObj.getString("time"));
                s.setLatitude(feedObj.getDouble("latitude"));
                s.setLongitude(feedObj.getDouble("longitude"));
                s.setUserId(feedObj.getString("user_id"));
                s.setUsername(feedObj.getString("username"));
                //Profile photo might be null sometimes
                String image = feedObj.isNull("profile_photo") ? null : feedObj
                    .getString("profile_photo");
                s.setProfilePhoto(image);

                mSounds.add(s);             }

            // Notify data changes to list adapter          
            adapter.notifyDataSetChanged();
        } catch (JSONException e) {
            e.printStackTrace();
        }
    }

}

还有我的SoundAdapter.javaclass:

public class SoundAdapter extends ArrayAdapter<Sound> {
    private Activity activity;
    private LayoutInflater inflater;
    private ArrayList<Sound> feedItems;
    private Date now = new Date();
    private long now1 = now.getTime();
    ImageLoader imageLoader = AppController.getInstance().getImageLoader();

    public SoundAdapter(Context context, int resource, ArrayList<Sound> feedItems) {
        super(context, resource, feedItems);
        Log.w("DEBUG", "It made it to the SoundAdapter creation!");
        this.activity = activity;
        this.feedItems = feedItems;
    }

    @Override
        public int getCount() {
            Log.w("DEBUG", "Now it's trying to getCount! Cue the crash.");
            return feedItems.size();
        } 

    @Override
        public Sound getItem(int location) {
            return feedItems.get(location);
        }

    @Override
        public long getItemId(int position) {
            return position;
        }

    @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (inflater == null)
                inflater = (LayoutInflater) activity
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            if (convertView == null)
                convertView = inflater.inflate(R.layout.fragment_sound_item, null);

            if (imageLoader == null)
                imageLoader = AppController.getInstance().getImageLoader();

            Sound s = getItem(position);

            TextView titleTextView = (TextView) convertView.findViewById(R.id.sound_title);
            titleTextView.setText(s.getTitle().toString());
            TextView usernameTextView = (TextView)convertView.findViewById(R.id.username_label);
            usernameTextView.setText(s.getUsername().toString());
            TextView locationTextView = (TextView)convertView.findViewById(R.id.sound_location);
            locationTextView.setText(s.getLocation().toString());


            String timestamp = (String) DateUtils.getRelativeTimeSpanString(Long.parseLong(s.getTime()), System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS);
            TextView timeTextView = (TextView)convertView.findViewById(R.id.time_label);
            timeTextView.setText(timestamp);

            FeedImageView mSoundVizView = (FeedImageView) convertView.findViewById(R.id.sound_vis);

            ImageView mapImageView = new ImageView(activity);
            Picasso.with(activity)
                .load("https://maps.googleapis.com/maps/api/staticmap?center=Rio+de+Janeiro,Brazil&zoom=11&size=250x150")
                .into(mapImageView);

            NetworkImageView profilePhotoView = (NetworkImageView) convertView.findViewById(R.id.profile_photo);

            // Chcek for empty status message
            if (!TextUtils.isEmpty(s.getTitle())) {
                titleTextView.setText(s.getTitle());
                titleTextView.setVisibility(View.VISIBLE);
            } else {
                // status is empty, remove from view
                titleTextView.setVisibility(View.GONE);
            }


            // user profile pic
            profilePhotoView.setImageUrl(s.getProfilePhoto(), imageLoader);

            // Feed image
            if (s.getSoundViz() != null) {
                mSoundVizView.setImageUrl(s.getSoundViz(), imageLoader);
                mSoundVizView.setVisibility(View.VISIBLE);
                mSoundVizView
                    .setResponseObserver(new FeedImageView.ResponseObserver() {
                            @Override
                            public void onError() {
                            }

                            @Override
                            public void onSuccess() {
                            }
                            });
            } else {
                mSoundVizView.setVisibility(View.GONE);
            }


            return convertView;
        }


}

然后这是我的 XML 文件:

在fragment_sound_item.xml中:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <ImageView
        android:id="@+id/pin_map_icon"
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_alignBottom="@+id/sound_location"
        android:layout_alignParentLeft="true"
        android:paddingBottom="2dp"
        android:src="@drawable/pin_map_icon" />

    <TextView
        android:id="@+id/sound_location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignRight="@+id/sound_title"
        android:layout_marginTop="20dp"
        android:text="@string/sound_location_label"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <TextView
        android:id="@+id/time_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/sound_location"
        android:layout_alignBottom="@+id/sound_location"
        android:layout_alignParentRight="true"
        android:text="@string/sound_time_label"
        android:layout_marginRight="5dp"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <LinearLayout
        android:id="@+id/container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true"
        android:orientation="vertical"
        android:layout_marginTop="45dp"
        android:layout_marginBottom="10dp" >

        <com.littlecloudcollective.waves.FeedImageView
            android:id="@+id/sound_map"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true" />

        <ImageButton
            android:id="@+id/sound_vis"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:adjustViewBounds="true"
            android:background="@null"
            android:cropToPadding="true"
            android:scaleType="fitStart"
            android:src="@drawable/sound_vis_2" />

    </LinearLayout>

    <TextView
        android:id="@+id/username_label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/container"
        android:layout_marginLeft="8dp"
        android:text="@string/user_username_label"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <TextView
        android:id="@+id/sound_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/divider"
        android:layout_marginBottom="10dp"
        android:layout_marginLeft="8dp"
        android:paddingBottom="20dp"
        android:text="@string/sound_title_label"
        android:textSize="14sp" />

    <ImageView
        android:id="@+id/divider"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/username_label"
        android:layout_below="@+id/username_label"
        android:src="@drawable/divider" />

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/profile_photo"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/time_label"
        android:layout_below="@+id/container"
        android:src="@drawable/default_profile_thumbnail" />

</RelativeLayout>

和fragment_sound.xml:

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@android:id/list" />

这是我遇到的错误:

02-19 14:02:11.229: W/DEBUG(20532): onResponse method hit within JsonObjectRequest
02-19 14:02:11.239: W/DEBUG(20532): AppController getInstance request queue thing hit.
02-19 14:02:11.239: W/DEBUG(20532): Made it to the end!!
02-19 14:02:11.239: W/DEBUG(20532): It made it to the SoundAdapter creation!
02-19 14:02:11.239: W/DEBUG(20532): ListAdapter set!
02-19 14:02:11.289: D/AbsListView(20532): Get MotionRecognitionManager
02-19 14:02:11.319: W/DEBUG(20532): Now it's trying to getCount! Cue the crash.
02-19 14:02:11.319: D/AndroidRuntime(20532): Shutting down VM
02-19 14:02:11.319: W/dalvikvm(20532): threadid=1: thread exiting with uncaught exception (group=0x41764438)
02-19 14:02:11.319: E/AndroidRuntime(20532): FATAL EXCEPTION: main
02-19 14:02:11.319: E/AndroidRuntime(20532): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.littlecloudcollective.waves/com.littlecloudcollective.waves.LandingActivity}: java.lang.NullPointerException
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2114)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2139)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.app.ActivityThread.access0(ActivityThread.java:143)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1241)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.os.Handler.dispatchMessage(Handler.java:99)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.os.Looper.loop(Looper.java:137)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.app.ActivityThread.main(ActivityThread.java:4963)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at java.lang.reflect.Method.invokeNative(Native Method)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at java.lang.reflect.Method.invoke(Method.java:511)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at dalvik.system.NativeStart.main(Native Method)
02-19 14:02:11.319: E/AndroidRuntime(20532): Caused by: java.lang.NullPointerException
02-19 14:02:11.319: E/AndroidRuntime(20532):    at com.littlecloudcollective.waves.SoundAdapter.getCount(SoundAdapter.java:42)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.widget.ListView.setAdapter(ListView.java:466)
02-19 14:02:11.319: E/AndroidRuntime(20532):    at android.support.v4.app.ListFragment.setListAdapter(ListFragment.java:182)

次要错误,在对上下文进行 activity 更改并更新 SoundAdapter 中的 this.feedItems 位之后:

02-19 14:46:21.479: E/AndroidRuntime(21538): Caused by: java.lang.NullPointerException
02-19 14:46:21.479: E/AndroidRuntime(21538):    at com.littlecloudcollective.waves.SoundAdapter.<init>(SoundAdapter.java:36)
02-19 14:46:21.479: E/AndroidRuntime(21538):    at com.littlecloudcollective.waves.SoundFragment.onCreate(SoundFragment.java:97)
02-19 14:46:21.479: E/AndroidRuntime(21538):    at android.support.v4.app.Fragment.performCreate(Fragment.java:1477)

这是我的第一个 Android 应用程序,所以我怀疑我在某个地方搞混了 Fragments。任何帮助将不胜感激。谢谢!

永远避免做

this.feedItems = feedItems;

这样更好:

//Initialize your List
private ArrayList<Sound> feedItems = new ArrayList<Sound>();

public SoundAdapter(Context context, int resource, ArrayList<Sound> feedItems) {
    super(context, resource, feedItems);
    this.feedItems.clear();
    this.feedItems.addAll(feedItems);
}

这样 feedItems 将永远不会指向 null 对象。

您忘记初始化 mSounds 列表。在设置适配器之前将此行添加到您的 onCreate 方法中。当您调用 parseJsonFeed 时,还要确保您的适配器不为空。也许这就是您得到的 NPE。只是为了确保您应该先创建适配器:

@Override
public void onCreate(Bundle savedInstanceState) {

    ...

    setHasOptionsMenu(true);
    // First check for cached request
    Cache cache = AppController.getInstance().getRequestQueue().getCache();
    Entry entry = cache.get(URL_FEED);

    mSounds = new ArrayList<Sound>();       //<-- Add this line so your list is not null

    //And initialize your adapter from the beggining
    adapter = new SoundAdapter(getActivity(), com.littlecloudcollective.waves.R.layout.fragment_sound_item, mSounds);
    setListAdapter(adapter);
    Log.w("DEBUG", "ListAdapter set!");

    ...
}