how to enhance & increase performance to recyclerView adapter

Viewed 128

I have this adapter which work pretty good so far as expected but I have made some bad practice code on it like : -using onClickListener in OnBindView.

  • used GC to clean my memory which is also a bad practice.
  • I have set the adapter with a list and other lists to be loaded when needed on scrolling. is that right ? or should I load all of the lists needed before set the adapter.
  • when updating adapter I usally sets a new adapter for that not notifyDataSetChanged because it's not always works.
  • to be noticed of the whole project, I have a category adapter which sets the itemViewAdapter.
  • itemViewAdapter sets colorItemsAdapter if item has more than a color.

I need to increase performance of that code because I feel it's going to get laggy on scrolling with many data.

here is code of itemViewAdapter:

public class ItemViewAdapter extends RecyclerView.Adapter<ItemViewAdapter.imagesHolder> {

List<iItem> Items;
Context context;
ExecutorService pool;
boolean isAdmin;

public ItemViewAdapter(List<iItem> items) {
    Items = items;
}

@NonNull
@Override
public ItemViewAdapter.imagesHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_main_items, parent, false);
    context = parent.getContext();
    isAdmin = ((Activity) context).getIntent().getBooleanExtra("isAdmin", false);

    return new imagesHolder(view);
}

@Override
public void onBindViewHolder(@NonNull imagesHolder holder, int position) {
    if (pool == null) {
        pool = Executors.newFixedThreadPool(10);
    }

    pool.execute(() -> {
        holder.colorItems = iItemDatabase.getDB(context).iItemDAO().getColorItems(Items.get(holder.getAdapterPosition()).getItemID());
        if (holder.colorItems != null) {
            if (holder.colorItems.size() != 0) {
                holder.tvItemName.setText(holder.colorItems.get(0).getItemName());
                if (holder.colorItems.get(0).getItemBPrice().equals("0")) {
                    holder.tvItemPriceB.setVisibility(View.INVISIBLE);
                } else {
                    holder.tvItemPriceB.setText(holder.colorItems.get(0).getItemBPrice() + " L.E");
                }
                holder.tvItemPriceA.setText(holder.colorItems.get(0).getItemAPrice() + " L.E");

                pool.execute(() -> {
                    holder.getImages = (List<iItemImages>) iItemDatabase.getDB(context).iItemImagesDAO().getImages(holder.colorItems.get(0).getItemID());
                    holder.semaphore.release();
                });
                holder.semaphore.acquireUninterruptibly();

                ((Activity) context).runOnUiThread(() -> {
                    if (holder.getImages.size() != 0) {
                        holder.tvImageSize.setText("1/" + holder.getImages.size());

                        holder.imageAdapter = new ImageAdapter(holder.getImages, isLongCLick -> {
                            if (!isLongCLick) {
                                onImageClick(holder.colorItems.get(holder.colorItemPosition).getItemID());
                            } else {
                                if (isAdmin) {
                                    onImageLongClick(holder.btnAddColor, holder.btnEdit, holder.btnDelete, holder.btnDeleteAll,
                                            holder.colorItems, holder.colorItemPosition, holder.alertDialog);
                                }
                            }
                        });
                        holder.rvItemImages1.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
                        holder.rvItemImages1.setAdapter(holder.imageAdapter);

                        if (holder.getImages.size() > 1) {
                            if (holder.handler == null) {
                                holder.handler = new Handler(Looper.getMainLooper());
                                holder.handler.postDelayed(holder.runnable, 4000);
                            }
                        }
                    }
                    holder.semaphore.release();
                });
                holder.semaphore.acquireUninterruptibly();

                if (holder.colorItems.size() >= 1) {
                    holder.imagesURL.clear();
                    for (iItem colorItem : holder.colorItems) {
                        holder.imagesURL.addAll(iItemDatabase.getDB(context).iItemImagesDAO().getFirstImageURL(colorItem.getItemID()));
                    }
                    holder.colorItemsAdapter = new ColorItemsAdapter(holder.colorItems, holder.imagesURL, position1 -> {
                        holder.colorItemPosition = position1;
                        holder.tvItemName.setText(holder.colorItems.get(position1).getItemName());
                        if (holder.colorItems.get(position1).getItemBPrice().equals("0")) {
                            holder.tvItemPriceB.setVisibility(View.INVISIBLE);
                        } else {
                            holder.tvItemPriceB.setText(holder.colorItems.get(position1).getItemBPrice() + " L.E");
                        }
                        holder.tvItemPriceA.setText(holder.colorItems.get(position1).getItemAPrice() + " L.E");

                        pool.execute(() -> {
                            holder.getImages = (List<iItemImages>) iItemDatabase.getDB(context).iItemImagesDAO().getImages(holder.colorItems.get(position1).getItemID());
                            holder.semaphore.release();
                        });
                        holder.semaphore.acquireUninterruptibly();

                        if (holder.getImages.size() != 0) {
                            holder.tvImageSize.setText("1/" + holder.getImages.size());

                            holder.imageAdapter = new ImageAdapter(holder.getImages, isLongCLick -> {
                                if (!isLongCLick) {
                                    onImageClick(holder.colorItems.get(holder.colorItemPosition).getItemID());
                                } else {
                                    if (isAdmin) {
                                        onImageLongClick(holder.btnAddColor, holder.btnEdit, holder.btnDelete, holder.btnDeleteAll,
                                                holder.colorItems, holder.colorItemPosition, holder.alertDialog);
                                    }
                                }
                            });
                            holder.rvItemImages1.setAdapter(holder.imageAdapter);

                            if (holder.getImages.size() > 1) {
                                if (holder.handler == null) {
                                    holder.handler = new Handler(Looper.getMainLooper());
                                    holder.handler.postDelayed(holder.runnable, 4000);
                                }
                            }
                        }
                        holder.colorItemsAdapter.notifyDataSetChanged();
                    });
                    ((Activity) context).runOnUiThread(() -> {
                        holder.rvColorItems.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
                        holder.rvColorItems.setAdapter(holder.colorItemsAdapter);
                        holder.semaphore.release();
                    });
                    holder.semaphore.acquireUninterruptibly();
                    Runtime.getRuntime().gc();
                }
            }
        }
        holder.rvItemImages1.addOnScrollListener(new RecyclerView.OnScrollListener() {

            @Override
            public void onScrollStateChanged(@NonNull @NotNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    holder.tvImageSize.setText((((LinearLayoutManager) Objects.requireNonNull(recyclerView.getLayoutManager())).findFirstVisibleItemPosition() + 1) + "/" + Objects.requireNonNull(recyclerView.getAdapter()).getItemCount());
                }
            }
        });
    });
    Runtime.getRuntime().gc();
}

void onImageClick(String ItemID) {
    Fragment fragment = new ItemsFragment2();
    Bundle bundle = new Bundle();
    bundle.putString("selectedItem", ItemID);
    fragment.setArguments(bundle);

    FragmentManager fragmentManager = ((AppCompatActivity) context).getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    Fragment fragment1 = fragmentManager.findFragmentByTag("ItemsFragment1");
    fragmentTransaction.hide(fragment1);
    fragmentTransaction.add(R.id.fl_MainItems, fragment, "ItemsFragment2").addToBackStack(null);
    fragmentTransaction.commit();
}

void onImageLongClick(Button btnAddColor, Button btnEdit, Button btnDelete, Button btnDeleteAll,
                      List<iItem> colorItems, int colorItemPosition, AlertDialog alertDialog) {
    Semaphore semaphore = new Semaphore(0);

    btnAddColor.setOnClickListener(v1 -> {
        Intent in = new Intent(context, AEDItem.class);
        in.putExtra("type", (byte) 3);
        in.putExtra("itemID", colorItems.get(0).getItemID());
        in.putExtra("colorID", colorItems.get(0).getColorID());
        context.startActivity(in);

        alertDialog.dismiss();
    });

    btnEdit.setOnClickListener(v1 -> {
        Intent in = new Intent(context, AEDItem.class);
        in.putExtra("type", (byte) 2);
        in.putExtra("itemID", colorItems.get(colorItemPosition).getItemID());
        in.putExtra("colorID", colorItems.get(colorItemPosition).getColorID());
        in.putExtra("itemName", colorItems.get(colorItemPosition).getItemName());
        context.startActivity(in);

        alertDialog.dismiss();
    });

    btnDelete.setOnClickListener(v12 -> {
        pool.execute(() -> {
            saveAdminUpdate(context,true);

            iItemDatabase.getDB(context).iItemDAO().DeleteItem(colorItems.get(colorItemPosition).getItemID());
            iItemDatabase.getDB(context).iItemImagesDAO().DeleteItemImage(colorItems.get(colorItemPosition).getItemID());

            FirebaseFirestore db = FirebaseFirestore.getInstance();
            db.collection("Items").document(colorItems.get(colorItemPosition).getItemID())
                    .delete()
                    .addOnSuccessListener(aVoid ->
                    {
                        FirebaseStorage storage = FirebaseStorage.getInstance();
                        StorageReference storageRef = storage.getReference();
                        StorageReference desertRef = storageRef.child("itemImages/" + colorItems.get(colorItemPosition).getItemID());

                        Task<ListResult> listResultTask = desertRef.listAll();
                        listResultTask.addOnSuccessListener(listResult -> {
                            for (StorageReference item : listResult.getItems()) {
                                item.delete().addOnSuccessListener(l -> {
                                });
                            }
                        });
                        semaphore.release();
                    });

            semaphore.acquireUninterruptibly();

            final int[] ExternalVersion = {retrieveVersion(context)};
            if (ExternalVersion[0] == 0) {
                // not found on shared, get it from server
                db.collection("Version").document("items")
                        .get()
                        .addOnCompleteListener(task -> {
                            pool.execute(() -> {
                                if (task.isSuccessful()) {
                                    if (task.getResult().get("v") != null) {
                                        // if found, add 1 to it
                                        ExternalVersion[0] = Integer.parseInt(task.getResult().get("v").toString());
                                        ExternalVersion[0]++;
                                    } else {
                                        // not found, create it
                                        ExternalVersion[0] = 1;
                                    }
                                }
                                semaphore.release(2);
                            });
                        });

                semaphore.acquireUninterruptibly();
            } else {
                // found on shared, add 1 to it
                ExternalVersion[0]++;
            }

            // update version on server and local
            Map<String, Integer> externalVersion = new HashMap<>();
            externalVersion.put("v", ExternalVersion[0]);
            db.collection("Version")
                    .document("items")
                    .set(externalVersion)
                    .addOnSuccessListener(documentReference1 -> {
                        pool.execute(() -> {
                            saveVersion(context, ExternalVersion[0]);
                            semaphore.release();
                        });
                    });
            semaphore.acquireUninterruptibly();

            ((Activity) context).runOnUiThread(() -> {
                View view = View.inflate(context, R.layout.rv_main_items, null);
                Snackbar.make(view, "Item deleted successfully", Snackbar.LENGTH_LONG).show();
                alertDialog.dismiss();

            });
        });
    });

    btnDeleteAll.setOnClickListener(v12 -> {
        pool.execute(() -> {
            saveAdminUpdate(context,true);
            List<String> itemIDs = new ArrayList<>();
            for (iItem colorItem : colorItems) {
                itemIDs.add(colorItem.getItemID());
            }
            iItemDatabase.getDB(context).iItemDAO().DeleteItems(itemIDs);
            iItemDatabase.getDB(context).iItemImagesDAO().DeleteItemImages(itemIDs);

            FirebaseFirestore db = FirebaseFirestore.getInstance();
            for (String itemID : itemIDs) {
                db.collection("Items").document(itemID)
                        .delete()
                        .addOnSuccessListener(aVoid ->
                        {
                            FirebaseStorage storage = FirebaseStorage.getInstance();
                            StorageReference storageRef = storage.getReference();
                            StorageReference desertRef = storageRef.child("itemImages/" + itemID);

                            Task<ListResult> listResultTask = desertRef.listAll();
                            listResultTask.addOnSuccessListener(listResult -> {
                                for (StorageReference item : listResult.getItems()) {
                                    item.delete().addOnSuccessListener(l -> {
                                    });
                                }
                            });
                            semaphore.release();
                        });
                semaphore.acquireUninterruptibly();
            }
            itemIDs.clear();

            final int[] ExternalVersion = {retrieveVersion(context)};
            if (ExternalVersion[0] == 0) {
                // not found on shared, get it from server
                db.collection("Version").document("items")
                        .get()
                        .addOnCompleteListener(task -> {
                            pool.execute(() -> {
                                if (task.isSuccessful()) {
                                    if (task.getResult().get("v") != null) {
                                        // if found, add 1 to it
                                        ExternalVersion[0] = Integer.parseInt(task.getResult().get("v").toString());
                                        ExternalVersion[0]++;
                                    } else {
                                        // not found, create it
                                        ExternalVersion[0] = 1;
                                    }
                                }
                                semaphore.release(2);
                            });
                        });

                semaphore.acquireUninterruptibly();
            } else {
                // found on shared, add 1 to it
                ExternalVersion[0]++;
            }

            // update version on server and local
            Map<String, Integer> externalVersion = new HashMap<>();
            externalVersion.put("v", ExternalVersion[0]);
            db.collection("Version")
                    .document("items")
                    .set(externalVersion)
                    .addOnSuccessListener(documentReference1 -> {
                        pool.execute(() -> {
                            saveVersion(context,ExternalVersion[0]);
                            semaphore.release();
                        });
                    });
            semaphore.acquireUninterruptibly();

            ((Activity) context).runOnUiThread(() -> {
                View view = View.inflate(context, R.layout.rv_main_items, null);
                Snackbar.make(view, "Items deleted successfully", Snackbar.LENGTH_LONG).show();
                alertDialog.dismiss();

            });
        });
    });
    alertDialog.show();

    Runtime.getRuntime().gc();
}

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

class imagesHolder extends RecyclerView.ViewHolder {
    TextView tvItemName, tvItemPriceA, tvItemPriceB, tvImageSize;
    RecyclerView rvItemImages1, rvColorItems;
    Handler handler;
    Runnable runnable;
    List<iItem> colorItems;
    List<iItemImages> getImages;
    Semaphore semaphore;
    ImageAdapter imageAdapter;
    ColorItemsAdapter colorItemsAdapter;
    List<String> imagesURL;
    int colorItemPosition = 0;
    AlertDialog.Builder alert;
    View mView;
    Button btnAddColor;
    Button btnEdit;
    Button btnDelete;
    Button btnDeleteAll;
    AlertDialog alertDialog;


    public imagesHolder(@NonNull View itemView) {
        super(itemView);
        rvItemImages1 = itemView.findViewById(R.id.rvItemImages1);
        rvColorItems = itemView.findViewById(R.id.rvColorItems);
        tvItemName = itemView.findViewById(R.id.tvItemName);
        tvItemPriceA = itemView.findViewById(R.id.tvItemPriceA);
        tvItemPriceB = itemView.findViewById(R.id.tvItemPriceB);
        tvImageSize = itemView.findViewById(R.id.tvImageSize);
        alert = new AlertDialog.Builder(itemView.getContext());
        mView = ((Activity) itemView.getContext()).getLayoutInflater().inflate(R.layout.custom_dialog, null);
        btnAddColor = mView.findViewById(R.id.btnAddColor);
        btnEdit = mView.findViewById(R.id.btnEdit);
        btnDelete = mView.findViewById(R.id.btnDelete);
        btnDeleteAll = mView.findViewById(R.id.btnDeleteAll);
        alert.setView(mView);
        alertDialog = alert.create();
        imagesURL = new ArrayList<>();
        semaphore = new Semaphore(0);
        runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    if ((((LinearLayoutManager) Objects.requireNonNull(rvItemImages1.getLayoutManager())).findFirstVisibleItemPosition() + 1) < Objects.requireNonNull(imageAdapter).getItemCount()) {
                        rvItemImages1.smoothScrollToPosition((((LinearLayoutManager) rvItemImages1.getLayoutManager()).findFirstVisibleItemPosition() + 1));
                    } else {
                        rvItemImages1.smoothScrollToPosition(0);
                    }
                    handler.postDelayed(this, 4000);
                } catch (Exception ignored) {
                }
            }
        };


        Runtime.getRuntime().gc();
    }
}

}

1 Answers

You're not meant to run database queries onBindViewHolder(). The adapter should already know about the data. Replace the UI operations on every single one view-holder with data-binding library and preferably set all the data at once. To begin with, remove this heavily-abused line: Semaphore semaphore and learn how to write asynchronous code. Also this is usually not required: Runtime.getRuntime().gc() and it won't improve performance, when looking what the code does.

Or just inflate a whole different XML when isAdmin == true in createViewHolder().

Related