2

I want to get data asynchronously with Retrofit2, that part is done well, but my adapter for the recycler view is first fed an empty arraylist as the dataset, but then as soon as the asynch returns the data is added onto the adapter and notifyDAtasetChanged or notifyRangeinserted is called depending on whether the item count for the adapter is 0. My question is how am i supposed t properly update the RV in such a situation cos I keep getting an NPE when I try to update the UI after a successfull connection, but if a connection error occurs the app runs just fine. Where am I going wrong? and btw this is not a duplicate of What is a Null Pointer Exception, and how do I fix it? cos I'm asking where I'm asking what the proper way to assign a dynamic, web reliant dataset to a recycler view is and not what a null pointer exception is. I just happen to be getting an NPE from my efforts to assign the dataset

here is my code : RV 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>

</RelativeLayout>

the calling fragment

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

and finally the log, starting from the point Main Activity starts the fragment transaction

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
ItIsEntropy
  • 306
  • 3
  • 13

1 Answers1

0

I found what I did wron.. dont know what it was exactly, but m adapter was the cause of the NPE, so i pasted a copy of another one of my adapters ad changed what needed to be changed. This is the working adapter:

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);
    }
}
ItIsEntropy
  • 306
  • 3
  • 13