1

Hi I am a Beginner in android and still learning and I am making a book library app that has some features and there is a bug or a null pointer exception to be precise in the get item count in the recycler view adapter and this is the first time that something like that happened thanks.

adapter:

package com.example.newbookstoreplzwork;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager;

import com.bumptech.glide.Glide;

import java.util.ArrayList;

public class BookRecViewAdapter extends RecyclerView.Adapter<BookRecViewAdapter.ViewHolder> {
    private static final String TAG = "BookRecViewAdapter";
     ArrayList<Book> books = new ArrayList<>();
    private Context mcontexet;
    private String parent;
    public BookRecViewAdapter(Context mcontexet , String parent) {
        this.mcontexet = mcontexet;
        this.parent = parent;
    }
    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout   .list_item_book , parent ,  false);
       return new ViewHolder(view);
    }
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, final int position) {
        Log.d(TAG, "onBindViewHolder: Working!!!!!");
        holder.txtName.setText(books.get(position).getName());
        Glide.with(mcontexet).asBitmap().load(books.get(position).getImageUrl()).into(holder.imgBook);
        holder.txtDescreption.setText(books.get(position).getShortDesc());
        holder.txtAuthor.setText(books.get(position).getAuthor());
        if (books.get(position).isExpanded()){
            TransitionManager.beginDelayedTransition(holder.parent);
            holder.expandedRealtiveLayout.setVisibility(View.VISIBLE);
            holder.downArrow.setVisibility(View.GONE);

            if (parent.equals(All_Books_Actvity.parentActvity)){
                holder.delete.setVisibility(View.GONE);
            }
            if (parent.equals(Book.fav)){

                holder.delete.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder dialog = new AlertDialog.Builder(mcontexet);
                        dialog.setMessage("Are you sure You want to delete " + books.get(position).getName());
                        dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (Utils.getInstance(mcontexet).removeFromFav(books.get(position))) {
                                    Toast.makeText(mcontexet, " Book removed", Toast.LENGTH_SHORT).show();
                                    notifyDataSetChanged();
                                }
                            }
                        });
                        dialog.setNegativeButton("no", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                        dialog.create().show();
                    }
                });
            }else if (parent.equals(Book.already)){
                holder.delete.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder dialog = new AlertDialog.Builder(mcontexet);
                        dialog.setMessage("Are you sure You want to delete " + books.get(position).getName());
                        dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (Utils.getInstance(mcontexet).removeLreadyRead(books.get(position))) {
                                    Toast.makeText(mcontexet, " Book removed", Toast.LENGTH_SHORT).show();
                                    notifyDataSetChanged();
                                }
                            }
                        });
                        dialog.setNegativeButton("no", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                        dialog.create().show();
                    }
                });
            } else if (parent.equals(Book.currently)){
                holder.delete.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder dialog = new AlertDialog.Builder(mcontexet);
                        dialog.setMessage("Are you sure You want to delete " + books.get(position).getName());
                        dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (Utils.getInstance(mcontexet).removeFromCurrently(books.get(position))) {
                                    Toast.makeText(mcontexet, " Book removed", Toast.LENGTH_SHORT).show();
                                    notifyDataSetChanged();
                                }
                            }
                        });
                        dialog.setNegativeButton("no", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });

                        dialog.create().show();
                    }

                });


            }else{
                holder.delete.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        AlertDialog.Builder dialog = new AlertDialog.Builder(mcontexet);
                        dialog.setMessage("Are you sure You want to delete " + books.get(position).getName());
                        dialog.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (Utils.getInstance(mcontexet).removeFromWantToRead(books.get(position))){
                                    Toast.makeText(mcontexet, "Book Removed", Toast.LENGTH_SHORT).show();
                                }
                            }
                        });
                        dialog.setNegativeButton("no", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                        dialog.create().show();
                    }
                });
            }



        }else{
            TransitionManager.beginDelayedTransition(holder.parent);
            holder.expandedRealtiveLayout.setVisibility(View.GONE);
            holder.downArrow.setVisibility(View.VISIBLE);
        }
        holder.parent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                    Intent intent = new Intent(mcontexet , Book_Actvity.class);
                    intent.putExtra(Book_Actvity.BOOK_ID ,books.get(position).getId());
                    mcontexet.startActivity(intent);

            }
        });


    }
    @Override
    public int getItemCount() {
        return Utils.getInstance(mcontexet).getAllBooksArrayList().size();
    }
    public void setBooks(ArrayList<Book> books) {
        this.books = books;
        notifyDataSetChanged();
    }
    public class ViewHolder extends RecyclerView.ViewHolder{
        private CardView parent;
        private ImageView imgBook;
        private TextView txtName , txtDescreption , txtAuthor ;
        private RelativeLayout expandedRealtiveLayout;
        private ImageView upArrow , downArrow , delete;
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            parent = itemView.findViewById(R.id.parent);
            imgBook = itemView.findViewById(R.id.imgBook);
            txtName = itemView.findViewById(R.id.txtBookName);
            txtDescreption = itemView.findViewById(R.id.txtShortDesc);
            expandedRealtiveLayout = itemView.findViewById(R.id.expandedRealtiveLayout);
            upArrow = itemView.findViewById(R.id.btnArrowUpp);
            downArrow = itemView.findViewById(R.id.btnArrowDown);
            txtAuthor = itemView.findViewById(R.id.txtAuthor);
            delete = itemView.findViewById(R.id.btnDelete);
            downArrow.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Book book = books.get(getAdapterPosition());
                    book.setExpanded(!book.isExpanded());
                    notifyItemChanged(getAdapterPosition());
                }
            });
            upArrow.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Book book = books.get(getAdapterPosition());
                    book.setExpanded(!book.isExpanded());
                    notifyItemChanged(getAdapterPosition());
                }
            });
        }

    }


}

Utils class

        package com.example.newbookstoreplzwork;
    
    import android.content.Context;
    import android.content.SharedPreferences;
    import android.view.inputmethod.InlineSuggestionsRequest;
    
    import com.google.gson.Gson;
    import com.google.gson.reflect.TypeToken;
    
    import java.lang.reflect.Type;
    imp

ort java.util.ArrayList;


public class Utils {
    private SharedPreferences sharedPreferences;
    public static Utils instance;
    private static final String ALL_Books = "ALLBOOKS";
    private static String DB_KEY = "db";
    public static final String booksIntext = "booksInText";
    private static final String ALREADY_READ_BOOKS = "AlreadyReadArraylist";
    private static final String FAVOURITE_BOOKS = "Favourite_Books";
    private static final String CURRENTLY_READING_BOOKS = "Currently_Reading_Books";
    private static final String WANT_TO_READ_BOOKS = "Want_To_Read_Books";
    private static ArrayList<Book> allBooksArrayList;

    Utils(Context context) {
        sharedPreferences = context.getSharedPreferences(DB_KEY, Context.MODE_PRIVATE);
        if (null == getAllBooksArrayList()) {
            initData();
        }
        SharedPreferences.Editor editor = sharedPreferences.edit();
        Gson gson = new Gson();
        if (getCurrentlyReadingArrayList() == null) {
            editor.putString(CURRENTLY_READING_BOOKS, gson.toJson(new ArrayList<Book>()));
            editor.commit();

        }
        if (getAlreadyReadArrayList() == null) {
            editor.putString(ALREADY_READ_BOOKS, gson.toJson(new ArrayList<Book>()));
            editor.commit();

        }
        if (getWishListArrayList() == null) {
            editor.putString(WANT_TO_READ_BOOKS, gson.toJson(new ArrayList<Book>()));
            editor.commit();

        }
        if (getFavoritesArrayList() == null) {
            editor.putString(FAVOURITE_BOOKS, gson.toJson(new ArrayList<Book>()));
            editor.commit();

        }

    }


    private void initData() {
        ArrayList<Book> books = new ArrayList<>();
        books.add(new Book(1, "wimpy kid", "karam Shadi", 134, "https://kbimages1-a.akamaihd.net/a93f1391-68be-478b-acb0-63cd7881aed8/1200/1200/False/the-getaway-diary-of-a-wimpy-kid-book-12.jpg",
                "asdfasd", "jhkjhkhkjhkjghjghgfg"));
        books.add(new Book(2, "majduddin Book", "majduddin", 90, "https://s3images.coroflot.com/user_files/individual_files/702459_am5ekrun22pd6wav_kbesce8q.png ", "long", "dodo"));
        SharedPreferences.Editor editor = sharedPreferences.edit();
        Gson gson = new Gson();
        String booksInText = gson.toJson(books);
        editor.putString(booksInText, booksInText);
        editor.commit();

    }

    public static Utils getInstance(Context context) {
        if (instance != null) {

            return instance;
        } else {
            instance = new Utils(context);
            return instance;
        }
    }


    public ArrayList<Book> getCurrentlyReadingArrayList() {
        Gson gson = new Gson();
        Type type = new TypeToken<ArrayList<Book>>() {
        }.getType();
        ArrayList<Book> books = gson.fromJson(sharedPreferences.getString(CURRENTLY_READING_BOOKS, null), type);
        return books;
    }

    public ArrayList<Book> getAlreadyReadArrayList() {
        Gson gson = new Gson();
        Type type = new TypeToken<ArrayList<Book>>() {
        }.getType();
        ArrayList<Book> books = gson.fromJson(sharedPreferences.getString(ALREADY_READ_BOOKS, null), type);
        return books;
    }

    public ArrayList<Book> getWishListArrayList() {
        Gson gson = new Gson();
        Type type = new TypeToken<ArrayList<Book>>() {
        }.getType();
        ArrayList<Book> books = gson.fromJson(sharedPreferences.getString(WANT_TO_READ_BOOKS, null), type);
        return books;
    }

    public ArrayList<Book> getFavoritesArrayList() {
        Gson gson = new Gson();
        Type type = new TypeToken<ArrayList<Book>>() {
        }.getType();
        ArrayList<Book> books = gson.fromJson(sharedPreferences.getString(FAVOURITE_BOOKS, null), type);
        return books;
    }

    public  ArrayList<Book> getAllBooksArrayList() {
        Gson gson = new Gson();
        Type type = new TypeToken<ArrayList<Book>>() {}.getType();
        ArrayList<Book> books = gson.fromJson(sharedPreferences.getString(ALL_Books, null), type);
        return books;
    }


    public Book getBookById(int id) {
        ArrayList<Book> books = getAllBooksArrayList();
        if (null != books) {
            for (Book b : books) {
                if (b.getId() == id) {
                    return b;
                }
            }
        }
        return null;
    }

    public boolean addToAlreadyReadBooks(Book book) {
        ArrayList<Book> books = getAlreadyReadArrayList();
        if (null != books) {
            if (books.add(book)) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                Gson gson = new Gson();
                editor.remove(ALREADY_READ_BOOKS);
                editor.putString(ALREADY_READ_BOOKS, gson.toJson(books));
                editor.commit();
                return true;
            }
        }
        return false;
    }

    public boolean addToCurrentlyReadingBooks(Book book) {
        ArrayList<Book> books = getCurrentlyReadingArrayList();
        if (null != books) {
            if (books.add(book)) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                Gson gson = new Gson();
                editor.remove(CURRENTLY_READING_BOOKS);
                editor.putString(CURRENTLY_READING_BOOKS, gson.toJson(books));
                editor.commit();
                return true;
            }
        }
        return false;
    }

    public boolean addToFavourites(Book book) {
        ArrayList<Book> books = getFavoritesArrayList();
        if (null != books) {
            if (books.add(book)) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                Gson gson = new Gson();
                editor.remove(FAVOURITE_BOOKS);
                editor.putString(FAVOURITE_BOOKS, gson.toJson(books));
                editor.commit();
                return true;
            }
        }
        return false;
    }

    public boolean addTOWantToWantToRead(Book book) {
        ArrayList<Book> books = getWishListArrayList();
        if (null != books) {
            if (books.add(book)) {
                SharedPreferences.Editor editor = sharedPreferences.edit();
                Gson gson = new Gson();
                editor.remove(WANT_TO_READ_BOOKS);
                editor.putString(WANT_TO_READ_BOOKS, gson.toJson(books));
                editor.commit();
                return true;
            }
        }
        return false;
    }

    public boolean removeFromFav(Book book) {
        ArrayList<Book> books = getFavoritesArrayList();
        if (null != books) {
            for (Book b : books) {
                if (b.getId() == book.getId()) {
                    if (books.remove(b)) {
                        Gson gson = new Gson();
                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.remove(FAVOURITE_BOOKS);
                        editor.putString(FAVOURITE_BOOKS, gson.toJson(books));
                        editor.commit();
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public boolean removeFromCurrently(Book book) {
        ArrayList<Book>books = getCurrentlyReadingArrayList();
        if (null!=books){
            for (Book b: books){
                if (b.getId() == book.getId()){
                    if (books.remove(b)){
                        Gson gson = new Gson();
                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.remove(CURRENTLY_READING_BOOKS);
                        editor.putString(CURRENTLY_READING_BOOKS , gson.toJson(books));
                        editor.commit();
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public boolean removeLreadyRead(Book book) {
        ArrayList<Book>books = getAlreadyReadArrayList();
        if (null!=books){
            for (Book b: books){
                if (b.getId() == book.getId()){
                    if (books.remove(b)){
                        Gson gson = new Gson();
                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.remove(ALREADY_READ_BOOKS);
                        editor.putString(ALREADY_READ_BOOKS , gson.toJson(books));
                        editor.commit();
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public boolean removeFromWantToRead(Book book) {
        ArrayList<Book>books = getWishListArrayList();
        if (null!=books){
            for (Book b: books){
                if (b.getId() == book.getId()){
                    if (books.remove(b)){
                        Gson gson = new Gson();
                        SharedPreferences.Editor editor = sharedPreferences.edit();
                        editor.remove(WANT_TO_READ_BOOKS);
                        editor.putString(WANT_TO_READ_BOOKS , gson.toJson(books));
                        editor.commit();
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

the Error message:

    java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference
    at com.example.newbookstoreplzwork.BookRecViewAdapter.getItemCount(BookRecViewAdapter.java:179)
    at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4044)
    at androidx.recyclerview.widget.RecyclerView.onMeasure(RecyclerView.java:3534)
    at android.view.View.measure(View.java:25086)
    at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:792)
    at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.measure(BasicMeasure.java:480)
    at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.measureChildren(BasicMeasure.java:134)
    at androidx.constraintlayout.solver.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:277)
    at androidx.constraintlayout.solver.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:119)
    at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1578)
    at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1690)
    at android.view.View.measure(View.java:25086)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:146)
    at android.view.View.measure(View.java:25086)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
    at androidx.appcompat.widget.ActionBarOverlayLayout.onMeasure(ActionBarOverlayLayout.java:490)
    at android.view.View.measure(View.java:25086)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at android.view.View.measure(View.java:25086)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
    at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
    at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
    at android.view.View.measure(View.java:25086)
    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6872)
    at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
    at com.android.internal.policy.DecorView.onMeasure(DecorView.java:742)
    at android.view.View.measure(View.java:25086)
    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:3083)
    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1857)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2146)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1745)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7768)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:967)
    at android.view.Choreographer.doCallbacks(Choreographer.java:791)
    at android.view.Choreographer.doFrame(Choreographer.java:726)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
    at android.os.Handler.handleCallback(Handler.java:883)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loop(Looper.java:214)
    at android.app.ActivityThread.main(ActivityThread.java:7356)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

and I am really sorry it is somewhat long to read but I really do not know what to copy to know what is enough

2 Answers2

1

try the following instead

//member declaration
private ArrayList<Book> books;

public BookRecViewAdapter(Context mcontexet , String parent) {
        this.mcontexet = mcontexet;
        this.parent = parent;
        this.books = new ArrayList<>();
    }

    @Override
    public int getItemCount() {
        return this.books.size()
    }
Sekiro
  • 1,537
  • 1
  • 7
  • 21
0

In your adapter, you use this ArrayList<Book> books = new ArrayList<>(); to set data to items.

But in here

@Override
    public int getItemCount() {
        return Utils.getInstance(mcontexet).getAllBooksArrayList().size();
    }

It is quite problematic,

The current issue you face because Utils.getInstance(mcontexet).getAllBooksArrayList() returns null, calling size() upon null causes NPE,

Lets say it does not return null, then at times you may get IndexOutOfBound, Utils.getInstance(mcontexet).getAllBooksArrayList() returns an ArrayList of size 5 but the books which you use to set data to view is of size 4 then IOB.

Lets say you escaped that too, Utils.getInstance(mcontexet).getAllBooksArrayList() returns an ArrayList of size 4 and books is of size 5, then one element won't show.

The best practice is that you always create a Collection instance that is final, and then clear, add, remove and notify the adapter.

Recommended Solution

final ArrayList<Book> books = new ArrayList<>();

public BookRecViewAdapter(Context mcontexet , String parent) {
        this.mcontexet = mcontexet;
        this.parent = parent;
        books.addAll(Utils.getInstance(mcontexet).getAllBooksArrayList());
        //if you want data from `Utils.getInstance(mcontexet).getAllBooksArrayList()`,
        //even if it returns null, the book is not null, So no **NPE**
    }


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

    public void setBooks(ArrayList<Book> books) {
        //this.books = books; derefrencing can be dangerours, argument books can be null at times
        this.books.clear();//remove this line if you want to keep previous items too.
        this.books.addAll(books);
        notifyDataSetChanged();
    }
rahat
  • 1,840
  • 11
  • 24