0

I want to find out how to update a recyclerview with asychronous data.. I start the fragment out with an empty dataset and then go on to add values to it onSuccess of the Retrofit call then next I call notifyDatasetChanged if the adapter itemcount is 0, or notifyItemRangeInserted if it isnt..To my knowledge that shouldbe enough to make the ViewHolder be refreshed with the new dataset but it seems it's still referencing the empty one when binding and throws an NPE.. please help adapter

    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);
}
}

place item

    <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>

Calling fragment

    public class 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
    getActivity().setTitle(R.string.place_map_title);
    SupportMapFragment mapFragment = PlacesListMapFragment.newInstance();
    FragmentTransaction fragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
    fragmentTransaction.replace(R.id.flContent, mapFragment);
    fragmentTransaction.addToBackStack(null);
    fragmentTransaction.commit();
}

}

and finally the log starting from the point MainActivity begins the Fragment Transaction

    MainActivity:: : fragment replaced with FragmentClass data
04-21 21:18:10.183 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment:  Created
04-21 21:18:10.193 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment: setting RV
04-21 21:18:10.193 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment: setting Layout Manager
04-21 21:18:10.193 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment: setting dataset
04-21 21:18:10.193 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment: setting adapter
04-21 21:18:10.193 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment: Setting up endless scrolling listener
04-21 21:18:10.193 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment:  returning view
04-21 21:18:10.253 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment:  Resumed
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;)
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;)
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist I/dalvikvm: Could not find method java.nio.file.Files.newOutputStream, referenced from method okio.Okio.sink
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to resolve static method 61852: Ljava/nio/file/Files;.newOutputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/OutputStream;
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist D/dalvikvm: VFY: replacing opcode 0x71 at 0x000a
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature (Ljava/nio/file/Path;)
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to find class referenced in signature ([Ljava/nio/file/OpenOption;)
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist I/dalvikvm: Could not find method java.nio.file.Files.newInputStream, referenced from method okio.Okio.source
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist W/dalvikvm: VFY: unable to resolve static method 61851: Ljava/nio/file/Files;.newInputStream (Ljava/nio/file/Path;[Ljava/nio/file/OpenOption;)Ljava/io/InputStream;
04-21 21:18:10.463 11953-13554/com.urbanity.apps.shoplist D/dalvikvm: VFY: replacing opcode 0x71 at 0x000a
04-21 21:18:10.763 11953-11953/com.urbanity.apps.shoplist D/PlaceListFragment: Success, new data set is of size 4 with status code: 200 from source sized: 2
04-21 21:18:10.803 11953-11953/com.urbanity.apps.shoplist I/PlaceRVAdapter: Object Holder constructor
04-21 21:18:10.803 11953-11953/com.urbanity.apps.shoplist D/AndroidRuntime: Shutting down VM
04-21 21:18:10.803 11953-11953/com.urbanity.apps.shoplist W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x4162ae00)
04-21 21:18:10.813 11953-11953/com.urbanity.apps.shoplist E/AndroidRuntime: FATAL EXCEPTION: main
                                                                            Process: com.urbanity.apps.shoplist, PID: 11953
                                                                            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)
ItIsEntropy
  • 306
  • 3
  • 13
  • Do you initialize placeName in your custom ViewHolder? – user Apr 21 '16 at 19:32
  • Yes I do. The exception comes from the line in onDingViewHolder that sets placename to the name from the dataset's getName metod – ItIsEntropy Apr 21 '16 at 19:41
  • Go to the line and figure out what's null. Then find where it should be set, and figure out why that's null or why it isn't being called. You need to do a reasonable amount of legwork yourself – Gabe Sechan Apr 21 '16 at 19:43
  • Both of your views- `place_picture` and `place_name` are a part of CardView `place_cardview` and not the immediate children of your parent view in `product_item.xml`. So you need to initialize those views using your `CardView` like this - `View cardView = view.findViewById(R.id.place_cardview); placePicture = (ImageView) cardView.findViewById(R.id.place_picture); placeName = (TextView) cardView.findViewById(R.id.place_name);` – Shadab Ansari Apr 21 '16 at 19:44
  • @ShadabAnsari No he doesn't. findViewById does a recursive walk – Gabe Sechan Apr 21 '16 at 19:45
  • Since I'm in a good mood, I'll give you a hint- it isn't the adapter, its the calling code. Your backing array is null. – Gabe Sechan Apr 21 '16 at 19:48
  • so you say the mDataset variable is null? I'm trying out something different her, making a new list and initialising it to mDataSet.get(position), will that work? What I don't get is I nested the line within an f that said if mDataset isn't null it should equalte, but stil the NPE.. so what you suggest I switch back to using StorePlaces? – ItIsEntropy Apr 21 '16 at 19:59
  • I get that the initial list passed onto the constructor is empty (initialised to new ArrayList but contains nothing) but doesnt calling the notify methods on the adapter make the dataset change? – ItIsEntropy Apr 21 '16 at 20:45
  • You should be able to tell me what's null- the first thing to do with a NPE is to use a debugger to check every variable that's being accessed and see which is null. But unless I'm wrong about the timing in your fragment, I think get(0) returns null. – Gabe Sechan Apr 21 '16 at 22:11
  • I think so too, but the thing is it only does that on successful connection. Before that when the dataset is empty it moves along just fine, but as soon as I assign a newly created dataset with values in it it returns null – ItIsEntropy Apr 22 '16 at 18:19

0 Answers0