使用Retrofit 2异步调用将下载的内容分配给RecyclerView的正确方法

时间:2016-04-24 01:52:05

标签: java android asynchronous android-recyclerview retrofit

我想与Retrofit2异步获取数据,该部分已经完成,但我的循环器视图的适配器首先被送入一个空的arraylist作为数据集,但是当asynch返回时,数据被添加到适配器上调用notifyDAtasetChangednotifyRangeinserted取决于适配器的项目计数是否为0.我的问题是我应该如何在这种情况下正确更新RV因为我不断获得NPE尝试在成功连接后更新UI,但如果发生连接错误,应用程序运行正常。我哪里错了?并且顺便说一下,这不是What is a Null Pointer Exception, and how do I fix it?的副本。我在问我在哪里询问将动态,Web依赖数据集分配给回收器视图的正确方法是什么,而不是空指针异常是什么。我恰好从我分配数据集的努力中获得了NPE

这是我的代码: RV适配器

public class PlaceListRecyclerViewAdapter extends RecyclerView.Adapter<PlaceListRecyclerViewAdapter.PlaceObjectHolder>{
private static String LOG_TAG = "PlaceRVAdapter";
private ArrayList<Store> mDataSet;
private static MyClickListener myClickListener;


public static class PlaceObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    protected ImageView placePicture;
    protected TextView placeName;

    public PlaceObjectHolder(View view){
        super(view);
        Log.i(LOG_TAG, "Object Holder constructor");

        placePicture = (ImageView) view.findViewById(R.id.place_picture);
        placeName = (TextView) view.findViewById(R.id.place_name);

        itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        myClickListener.onItemClick(getAdapterPosition(), v);
        Log.d(LOG_TAG, "Listener has been called, returning control to calling class");
    }
}

public void setOnItemClickListener (MyClickListener myClickListener){
    PlaceListRecyclerViewAdapter.myClickListener = myClickListener;
}

public PlaceListRecyclerViewAdapter(ArrayList<Store> myDataSet){
    mDataSet = myDataSet;
}

@Override
public PlaceObjectHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_item, parent, false);
    PlaceObjectHolder productObjectHolder = new PlaceObjectHolder(view);
    //return new PlaceObjectHolder(view);
    return productObjectHolder;
}


@Override
public void onBindViewHolder(PlaceObjectHolder holder, int position) {
    // TODO: Replace with proper, complete logic here
    if (mDataSet != null) {
        //FixMe: NPE on this;
        holder.placeName.setText(mDataSet.get(position).getName());
    }
    // TODO: Image addition goes here
}

public void addItems(ArrayList<Store> stores, int index){
    int cursize = mDataSet.size();
    mDataSet.addAll(index, stores);
    if (getItemCount() != 0) {
        notifyItemRangeInserted(index, (mDataSet.size() - cursize));
    } else {
        notifyDataSetChanged();
    }
}


   public void deleteItem (int index){
        mDataSet.remove(index);
       notifyItemRemoved(index);
}

   @Override
   public int getItemCount() {
       return mDataSet.size();
   }


   public interface MyClickListener {
       void onItemClick(int position, View v);
   }
}

放置项目

**<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:cardView="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">

<android.support.v7.widget.CardView
android:id="@+id/place_cardview"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:focusable="true"
android:layout_gravity="center"
cardView:cardPreventCornerOverlap="true"
cardView:cardElevation="15dp">

    <ImageView
        android:id="@+id/place_picture"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/place_name"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:textStyle="bold"
        android:textSize="16dp" />
</android.support.v7.widget.CardView>

</RelativeLayout>

调用片段

PlacesListFragment extends Fragment {
    private static String LOG_TAG = "PlaceListFragment";
    private RecyclerView mRecyclerView;
    private PlaceListRecyclerViewAdapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;
    private Context mContext = getContext();
    private ArrayList<Store> storePlaces = new ArrayList<Store>();
    private ArrayList<Store> viewDataSet = new ArrayList<Store>();
    private String BASE_URL;
    private String FORMAT_SPECIFIER;
    private TextView storeName;
    private TextView phoneNumber;
    private TextView email;
    private TextView website;
    private TextView address;
    private LayoutInflater supinf;
    int statusCode;
    int ressize;
public PlacesListFragment() {
    // Required empty public constructor
}


public static PlacesListFragment newInstance() {
    PlacesListFragment fragment = new PlacesListFragment();
    return fragment;
}


@Override
public void onStart(){
    super.onStart();

    getDataSet(new com.urbanity.apps.shoplist.adapter.Callback<List<Store>>() {
                   @Override
                   public void next(List<Store> results, int requestCode) {
                       int cursize = mAdapter.getItemCount();
                       if (statusCode == 200) {
                           viewDataSet.addAll(results);
                           if (cursize != 0) {
                               mAdapter.addItems(viewDataSet, cursize);
                               Log.d(LOG_TAG, "Success, new data set is of size " + storePlaces.size() + " with status code: " + statusCode + " from source sized: " + ressize);
                           } else {
                               mAdapter.addItems(viewDataSet,cursize);
                               mAdapter.notifyDataSetChanged();
                           }
                       }
                   }
               });
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    BASE_URL = getResources().getString(R.string.base_url);
    FORMAT_SPECIFIER = getResources().getString(R.string.format_specifier);
    Log.d(LOG_TAG, " Created");
}

@Override
public void onResume(){
    super.onResume();
    Log.d(LOG_TAG, " Resumed");
    //set listener for this RV adapter instance
    mAdapter.setOnItemClickListener(new PlaceListRecyclerViewAdapter.MyClickListener() {
        @Override
        public void onItemClick(int position, View v) {
            Log.i(LOG_TAG, " Clicked on Item " + position);
            Store store = (Store) storePlaces.get(position);
            // TODO: place ALERT DIALOG here..
            displayAlert(supinf);

        }
    });
}

public void displayAlert(LayoutInflater inflater){
    View selectedPlaceDiag = inflater.inflate(R.layout.dialog_place_details, null);





    AlertDialog.Builder alert = new AlertDialog.Builder(getContext());
    alert.setTitle(R.string.alert_dialog_create_shlist);
    alert.setView(selectedPlaceDiag);
    alert.setCancelable(false);
    alert.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Toast.makeText(getContext(), R.string.ok, Toast.LENGTH_SHORT).show();
        }
    });

    alert.setPositiveButton(R.string.show_map, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // TODO: add logic here to display map
        }
    });
    AlertDialog dialog = alert.create();
    dialog.show();
}


public void getDataSet(final com.urbanity.apps.shoplist.adapter.Callback<List<Store>> callback) {

    Gson gson = new GsonBuilder()
            .setExclusionStrategies(new ExclusionStrategy() {
                @Override
                public boolean shouldSkipField(FieldAttributes f) {
                    return f.getDeclaringClass().equals(RealmObject.class);
                }

                @Override
                public boolean shouldSkipClass(Class<?> clazz) {
                    return false;
                }
            })
            .create();

    Retrofit retrofitClient = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create(gson))
            .build();

    // create a new service call variable from the interface with retrofitClient
    ShoplistApiEndpointInterface apiService = retrofitClient.create(ShoplistApiEndpointInterface.class);

    //create a new APICall variable of the api interface with its type being that of the call method you'll invoke from the interface
    Call<List<Store>> storeListCall = apiService.getStoreList(FORMAT_SPECIFIER);

    // make the call asynchronously by calling enqueue on the Call type
    //FIXME: chane serverside encoding is option 1 (type:app/json, encoding: utf8)
    storeListCall.enqueue(new Callback<List<Store>>() {

        @Override
        public void onResponse(Call<List<Store>> call, Response<List<Store>> response) {

            if (response.isSuccessful()) {
                // get status code from the HTTP response code
                statusCode = response.code();
                // add the parsed response body data (parsed pojo object list in this case)
                // to the arrayList with the addAll(Collection<type>) method
                List<Store> results = response.body();
                ressize = response.body().size();
                //int initialSize = storePlaces.size();
                // FIXME: source of NPE?
                storePlaces.addAll(results);//.addAll(initialSize, results);

                callback.next(storePlaces, statusCode);
            }
        }

        @Override
        public void onFailure(Call<List<Store>> call, Throwable t) {
            // log the error here
            Log.e(LOG_TAG, "Failed to download Place list", t.getCause());
            Toast.makeText(getActivity(), getResources().getString(R.string.failed_to_download_list) + t.getMessage() , Toast.LENGTH_SHORT).show();

        }
    });
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View v = inflater.inflate(R.layout.fragment_places_list, container, false);
    // TODO: in event of an NPE, uncomment this
    supinf = inflater;

    Log.d(LOG_TAG, "setting RV");
    mRecyclerView = (RecyclerView) v.findViewById(R.id.place_list_rv);
    mRecyclerView.setHasFixedSize(true);
    mLayoutManager = new GridLayoutManager(mContext, 2, GridLayoutManager.VERTICAL, false);
    Log.d(LOG_TAG, "setting Layout Manager");
    mRecyclerView.setLayoutManager(mLayoutManager);
    Log.d(LOG_TAG, "setting dataset");
    mAdapter = new PlaceListRecyclerViewAdapter(storePlaces);
    Log.d(LOG_TAG, "setting adapter");
    mRecyclerView.setAdapter(mAdapter);

    Log.d(LOG_TAG, "Setting up endless scrolling listener");
    mRecyclerView.addOnScrollListener(new EndlessRecyclerViewScrollListener((GridLayoutManager) mLayoutManager) {
        @Override
        public void onLoadMore(int page, int totalItemsCount) {
            //Query server for more data
            getDataSet(new com.urbanity.apps.shoplist.adapter.Callback<List<Store>>() {
                @Override
                public void next(List<Store> result, int requestCode) {
                    ArrayList<Store> stores = new ArrayList<Store>();
                    stores.addAll(result);
                    int cursize = mAdapter.getItemCount();
                    mAdapter.addItems(stores, cursize);
                }
            });
        }
    });
    Log.d(LOG_TAG, " returning view");

    AppCompatButton showMap = (AppCompatButton) v.findViewById(R.id.btn_show_map);

    showMap.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            showOnMap();
        }
    });

    getActivity().setTitle(R.string.store_locator);

    return v;
}

public void showOnMap(){
    // Insert the fragment by replacing any existing fragment
    SupportMapFragment mapFragment = PlacesListMapFragment.newInstance();
    FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.flContent, mapFragment);
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

最后是日志,从主要活动点开始,启动片段事务

04-21 16:33:57.690 17420-17420/com.urbanity.apps.shoplist D/MainActivity:: : fragment replaced with FragmentClass data
    04-21 16:33:57.710 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment:  Created
    04-21 16:33:57.720 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment: setting RV
    04-21 16:33:57.720 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment: setting Layout Manager
    04-21 16:33:57.720 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment: setting dataset
    04-21 16:33:57.720 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment: setting adapter
    04-21 16:33:57.720 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment: Setting up endless scrolling listener
    04-21 16:33:57.720 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment:  returning view
    04-21 16:33:57.790 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment:  Resumed
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;)
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;)
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist I/dalvikvm: Could not find method java.nio.file.Files.newOutputStream, referenced from method okio.Okio.sink
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to resolve static method 61886: Ljava/nio/file/Files;.newOutputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/OutputStream;
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist D/dalvikvm: VFY: replacing opcode 0x71 at 0x000a
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;)
    04-21 16:33:59.740 17420-17502/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;)
    04-21 16:33:59.750 17420-17502/com.urbanity.apps.shoplist I/dalvikvm: Could not find method java.nio.file.Files.newInputStream, referenced from method okio.Okio.source
    04-21 16:33:59.750 17420-17502/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to resolve static method 61885: Ljava/nio/file/Files;.newInputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;
    04-21 16:33:59.750 17420-17502/com.urbanity.apps.shoplist D/dalvikvm: VFY: replacing opcode 0x71 at 0x000a
    04-21 16:34:01.140 17420-17420/com.urbanity.apps.shoplist D/PlaceListFragment: Success, new data set is of size 4 with status code: 200 from source sized: 2
    04-21 16:34:01.170 17420-17420/com.urbanity.apps.shoplist I/PlaceRVAdapter: Object Holder constructor
    04-21 16:34:01.170 17420-17420/com.urbanity.apps.shoplist D/AndroidRuntime: Shutting down VM
    04-21 16:34:01.170 17420-17420/com.urbanity.apps.shoplist W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x4162ae00)
    04-21 16:34:01.210 17420-17420/com.urbanity.apps.shoplist E/AndroidRuntime: FATAL EXCEPTION: main
                                                                                Process: com.urbanity.apps.shoplist, PID: 17420
                                                                                java.lang.NullPointerException
                                                                                    at com.urbanity.apps.shoplist.adapter.PlaceListRecyclerViewAdapter.onBindViewHolder(PlaceListRecyclerViewAdapter.java:69)
                                                                                    at com.urbanity.apps.shoplist.adapter.PlaceListRecyclerViewAdapter.onBindViewHolder(PlaceListRecyclerViewAdapter.java:20)
                                                                                    at android.support.v7.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:5453)
                                                                                    at android.support.v7.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:5486)
                                                                                    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4723)
                                                                                    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4599)
                                                                                    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1988)
                                                                                    at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:528)
                                                                                    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1347)
                                                                                    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:574)
                                                                                    at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:170)
                                                                                    at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3003)
                                                                                    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2881)
                                                                                    at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1457)
                                                                                    at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:147)
                                                                                    at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:285)
                                                                                    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:768)
                                                                                    at android.view.Choreographer.doCallbacks(Choreographer.java:581)
                                                                                    at android.view.Choreographer.doFrame(Choreographer.java:550)
                                                                                    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:754)
                                                                                    at android.os.Handler.handleCallback(Handler.java:733)
                                                                                    at android.os.Handler.dispatchMessage(Handler.java:95)
                                                                                    at android.os.Looper.loop(Looper.java:136)
                                                                                    at android.app.ActivityThread.main(ActivityThread.java:5333)
                                                                                    at java.lang.reflect.Method.invokeNative(Native Method)
                                                                                    at java.lang.reflect.Method.invoke(Method.java:515)
                                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:895)
                                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:711)
                                                                                    at dalvik.system.NativeStart.main(Native Method)
    04-21 16:39:01.380 17420-17420/com.urbanity.apps.shoplist I/Process: Sending signal. PID: 17420 SIG: 9
    04-21 16:44:52.530 22302-22320/com.urbanity.apps.shoplist I/GMPM: Tag Manager is not found and thus will not be used

1 个答案:

答案 0 :(得分:0)

我发现我做了什么wron ..不知道它到底是什么,但是m适配器是NPE的原因,所以我粘贴了另一个适配器的副本广告改变了需要更改的内容。这是工作适配器:

public class PlaceListRecyclerViewAdapter extends RecyclerView.Adapter<PlaceListRecyclerViewAdapter.StoreObjectHolder>{
    private static String LOG_TAG = "StoreRecyclerViewAdapter: ";
    private ArrayList <Store> mDataSet;
    private static MyClickListener myClickListener;



    public static class StoreObjectHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        protected ImageView StoreImage;
        protected TextView StoreName;
        protected TextView StorePrice;
        public StoreObjectHolder(View view){
            super(view);
            StoreImage = (ImageView) view.findViewById(R.id.place_picture);
            StoreName = (TextView) view.findViewById(R.id.place_name);
            Log.i(LOG_TAG, "Adding Listener");
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            myClickListener.onItemClick(getAdapterPosition(), v);
            Log.d(LOG_TAG, "Listener has been called, returning control to calling class");
        }
    }

    public void setOnItemClickListener (MyClickListener myClickListener){
        PlaceListRecyclerViewAdapter.myClickListener = myClickListener;
    }

    public PlaceListRecyclerViewAdapter(ArrayList<Store> myDataSet){
        mDataSet = myDataSet;
    }

    @Override
    public StoreObjectHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.place_item, parent, false);
        StoreObjectHolder StoreObjectHolder = new StoreObjectHolder(view);
        //return new StoreObjectHolder(view);
        return StoreObjectHolder;
    }

    @Override
    public void onBindViewHolder(StoreObjectHolder holder, int position) {
        // TODO: Replace with proper, complete logic here
        holder.StoreName.setText(mDataSet.get(position).getName());
    }

    public void addItems(ArrayList<Store> stores, int index){
        int cursize = mDataSet.size();
        mDataSet.addAll(index, stores);
        if (getItemCount() != 0) {
            notifyItemRangeInserted(index, (mDataSet.size() - cursize));
        } else {
            notifyDataSetChanged();
        }
    }

    public void deleteItem (int index){
        mDataSet.remove(index);
        notifyItemRemoved(index);
    }

    @Override
    public int getItemCount() {
        return mDataSet.size();
    }

    public interface MyClickListener {
        void onItemClick(int position, View v);
    }
}