2

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

}

a_local_nobody
  • 7,947
  • 5
  • 29
  • 51

1 Answers1

0

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().

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • 1) so I should load the data outside the adapter then enter the adapter with it. 2) Semaphore is only used to wait for specific task like database work to finish then get those data to work after, if I didn't that, code will pass pool then execute it at the end. 3) GC >>> got it 4) I will consider using data binding what else do you have for me ? – user3650773 Feb 20 '21 at 19:35