PDA

View Full Version : بارگذاری اطلاعات حجیم در ریسایکلر ویو !؟



ghasem110deh
سه شنبه 14 شهریور 1396, 17:21 عصر
سلام به مهم :-)
دوستان من یه ریسایکلرویو درام که با آداپتر پر میشه، و بعد از مدتی اطلاعات حجم شون زیاد میشه و با توجه به اینکه از دو تا جدول دیتابیس هم اطلاعات لود میشه یکم زمان بر میشه مثلا در تعداد سطر 50 تا حدود 10 ثانیه طول میکشه !

حالا میخواد موقع دریافت اطلاعات یه پروگرس بار نمایش داده بشه (تا لود کامل بشه)


private class loadTaskHistory extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
HISTORY_RECYCLE.setVisibility(View.GONE);
PROGRESS_LOADER.setVisibility(View.VISIBLE);
super.onPreExecute();
}

@Override
protected void onPostExecute(Boolean result) {
PROGRESS_LOADER.setVisibility(View.GONE);
HISTORY_RECYCLE.setVisibility(View.VISIBLE);
adapter.notifyDataSetChanged();
super.onPostExecute(result);
}

@Override
protected Boolean doInBackground(Void... params) {
try {
Thread.sleep(4000);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}


اینم لود اطلاعات :


private void filterResult() {
try {
adapter = new HistoryAdapter(this, MOMENTS);
HISTORY_RECYCLE.setLayoutManager(new LinearLayoutManager(this));
HISTORY_RECYCLE.setItemAnimator(new DefaultItemAnimator());
HISTORY_RECYCLE.setAdapter(adapter);
} catch (Exception e) {
displayToast(this, "خطای آداپتر:" + "\n" + e.toString());
}
}


حالا چجوری لود دیتا رو با زمان نمایش پروگرس بار مچ کنم !؟ یعنی بجای مقدار فعلی Thread.sleep(4000)چطور زمان واقعی لود رو بزارم !
الان با 4000 حدود 3 تا 4 ثانیه پروگرس نمایش داده میشه ولی وقتی سطرهای دیتابیس از 50 تا رد بشه زمان بیشتری برای لود نیاز داره و دیگه پروگرس بار نمایش داده نمیشه !

امیدوارم مطلب رو رسونده باشم :لبخند:

Nevercom
سه شنبه 14 شهریور 1396, 18:12 عصر
این اطلاعات چی هست که برای 50 رکورد 10 ثانیه زمان نیاز داره ؟!!!

با توجه به اینکه می‌فرمایید از دیتابیس هم خونده میشه و Network هم در این پروسه نقشی نداره، خیلی خیلی عجیب هست این موضوع.


به هرعنوان، تصورم این هست که متدی وجود داره که عملیات دریافت اطلاعات رو انجام میده، قبل از خوندن اطلاعات از دیتابیس، ProgressBar رو نمایش بدید و وقتی اطلاعات دریافت شد هم اون رو مخفی کنید.
اگر قسمت زمانبر نمایش اطلاعات هست و نه دریافت اون (که خیلی بعید به نظر میرسه)، باید کدهای Adapter رو اصلاح کنید.

ghasem110deh
سه شنبه 14 شهریور 1396, 19:31 عصر
این اطلاعات چی هست که برای 50 رکورد 10 ثانیه زمان نیاز داره ؟!!!

با توجه به اینکه می‌فرمایید از دیتابیس هم خونده میشه و Network هم در این پروسه نقشی نداره، خیلی خیلی عجیب هست این موضوع.


به هرعنوان، تصورم این هست که متدی وجود داره که عملیات دریافت اطلاعات رو انجام میده، قبل از خوندن اطلاعات از دیتابیس، ProgressBar رو نمایش بدید و وقتی اطلاعات دریافت شد هم اون رو مخفی کنید.
اگر قسمت زمانبر نمایش اطلاعات هست و نه دریافت اون (که خیلی بعید به نظر میرسه)، باید کدهای Adapter رو اصلاح کنید.

توی آداپتر یه سری اطلاعات از یه جدول خونده میشه و با توجه به آیدی ، از سه تا جدول (هر کدوم یه مقدار عددی) و یه ادرس عکس (که واسه نمایش از آدرس خونده میشه و بعد از تبدیل به بیت مپ نمایش داده میشه) که فکر کنم همین عکس باعث زمان بر شدن میشه ... منتها باید باشه عکس !!!

آداپتر هم اگه لازم هست بزارم (کدش رو)

Nevercom
چهارشنبه 15 شهریور 1396, 00:14 صبح
خب شما اول باید ببینید کدوم قسمت از کدها بیشترین زمان رو به خودش اختصاص داده، خیلی ساده میتونی قبل از اجرای اون تکه کد زمان فعلی رو ذخیره کنی و بعد از اتمام اجرا زمان قبلی که ذخیره کردی رو از زمان فعلی کم کنی تا مدت زمان اجرا بدست بیاد.

بعد که فهمیدی کجا مشکل ایحاد کرده، اگر نتونستی مشکلش رو برطرف کنی کدهاش رو بزار تا شاید مشکلش پیدا بشه

ghasem110deh
چهارشنبه 15 شهریور 1396, 00:48 صبح
خب شما اول باید ببینید کدوم قسمت از کدها بیشترین زمان رو به خودش اختصاص داده، خیلی ساده میتونی قبل از اجرای اون تکه کد زمان فعلی رو ذخیره کنی و بعد از اتمام اجرا زمان قبلی که ذخیره کردی رو از زمان فعلی کم کنی تا مدت زمان اجرا بدست بیاد.

بعد که فهمیدی کجا مشکل ایحاد کرده، اگر نتونستی مشکلش رو برطرف کنی کدهاش رو بزار تا شاید مشکلش پیدا بشه

والا اگه بگم کلافه شدم روی این قضیه دروغ نگفتم :

اینم کد آداپتر هست :


import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;

import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.List;

import ir.rahgoshafan.diary.DiaryUpdateActivity;
import ir.rahgoshafan.diary.HistoryGalleryActivity;
import ir.rahgoshafan.diary.R;
import ir.rahgoshafan.diary.customs.DiaryTextView;
import ir.rahgoshafan.diary.database.DatabaseHelper;
import ir.rahgoshafan.diary.database.TableLocation;
import ir.rahgoshafan.diary.database.TableMoment;
import ir.rahgoshafan.diary.database.TablePeople;
import ir.rahgoshafan.diary.database.TablePhoto;
import ir.rahgoshafan.diary.model.Moment;

import static ir.rahgoshafan.diary.classes.ShowMassage.displayTo ast;

public class HistoryAdapter extends RecyclerView.Adapter<HistoryAdapter.HistoryHolder> implements Filterable {
private Context context;
private List<Moment> MOMENT_LIST;
private List<Moment> MOMENT_FILTER;
private TablePeople MOMENT_PEOPLE;
private TableLocation MOMENT_LOCATION;
private TablePhoto MOMENT_PHOTO;
private TableMoment TBL_MOMENT;
private int MOMENT_ID;
private String FARSI_DATE_TO_UPDATE;

private boolean isFilteredMode;
private String TXT_SEARCH;
private Handler handler = new Handler();

class HistoryHolder extends RecyclerView.ViewHolder {
private DiaryTextView TXT_TITLE, TXT_DATE, TXT_TIME, TXT_NOTE, TXT_LOCATION, TXT_PHOTO, TXT_PEOPLE, TXT_NULL_PHOTO;
private ImageView IMG_PHOTO, IMG_POPUP, IMG_LIKE;
private int ID;

HistoryHolder(View itemView) {
super(itemView);
TXT_TITLE = (DiaryTextView) itemView.findViewById(R.id.history_moment_title);
TXT_TIME = (DiaryTextView) itemView.findViewById(R.id.history_moment_time);
TXT_DATE = (DiaryTextView) itemView.findViewById(R.id.history_moment_persian_ date);
TXT_NOTE = (DiaryTextView) itemView.findViewById(R.id.history_moment_diary);
TXT_LOCATION = (DiaryTextView) itemView.findViewById(R.id.history_moment_location _no);
TXT_PHOTO = (DiaryTextView) itemView.findViewById(R.id.history_moment_photo_no );
TXT_PEOPLE = (DiaryTextView) itemView.findViewById(R.id.history_moment_people_n o);
IMG_PHOTO = (ImageView) itemView.findViewById(R.id.history_image_view);
IMG_POPUP = (ImageView) itemView.findViewById(R.id.history_popup);
TXT_NULL_PHOTO = (DiaryTextView) itemView.findViewById(R.id.history_txt_null_moment _photo);
IMG_LIKE = (ImageView) itemView.findViewById(R.id.history_moment_like);
}
}

public HistoryAdapter(Context context, List<Moment> MOMENT_LIST) {
this.context = context;
this.MOMENT_LIST = MOMENT_LIST;
this.MOMENT_FILTER = MOMENT_LIST;
}

@Override
public HistoryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R .layout.history_card_view_items, parent, false);
DatabaseHelper DB_HELPER = new DatabaseHelper(context);
MOMENT_PEOPLE = new TablePeople(DB_HELPER);
MOMENT_LOCATION = new TableLocation(DB_HELPER);
MOMENT_PHOTO = new TablePhoto(DB_HELPER);
TBL_MOMENT = new TableMoment(DB_HELPER);
return new HistoryHolder(itemView);
}

@SuppressLint("RecyclerView")
@Override
public void onBindViewHolder(final HistoryHolder holder, final int position) {
handler.post(new Runnable() {
@Override
public void run() {
try {
Moment moment = MOMENT_LIST.get(position);
holder.ID = moment.getId();
holder.TXT_TITLE.setText(moment.getMomentTitle());
holder.TXT_TIME.setText(moment.getOnTime());
holder.TXT_DATE.setText(moment.getFarsiDate());
FARSI_DATE_TO_UPDATE = moment.getFarsiDate();
holder.TXT_NOTE.setText(moment.getMoment());
holder.TXT_LOCATION.setText(String.valueOf(MOMENT_ LOCATION.MOMENT_LOCATION_COUNT(holder.ID)));
holder.TXT_PEOPLE.setText(String.valueOf(MOMENT_PE OPLE.MOMENT_PEOPLE_COUNT(holder.ID)));
holder.TXT_PHOTO.setText(String.valueOf(MOMENT_PHO TO.MOMENT_PHOTO_COUNT(holder.ID)));
if (moment.isHeartLike()) {
holder.IMG_LIKE.setImageResource(R.drawable.heart_ on);
} else if (!moment.isHeartLike())
holder.IMG_LIKE.setImageResource(R.drawable.heart_ off);
holder.IMG_POPUP.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MOMENT_ID = holder.ID;
showPopupMenu(holder.IMG_POPUP);
}
});
Cursor cursor = MOMENT_PHOTO.LAST_MOMENT_PHOTO(holder.ID);
if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; cursor.getCount() > i; i++) {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(cursor.getString(0)));
holder.IMG_PHOTO.setImageBitmap(Bitmap.createScale dBitmap(bitmap, 240, 320, false));
cursor.moveToNext();
}
if (cursor.getCount() > 0)
holder.TXT_NULL_PHOTO.setVisibility(View.INVISIBLE );
cursor.close();
}
if (isFilteredMode) {
String text = moment.getMoment();
String htmlText = text.replace(TXT_SEARCH, "<font color='#F7941F'>" + TXT_SEARCH + "</font>");
//noinspection deprecation
holder.TXT_NOTE.setText(Html.fromHtml(htmlText));
}
} catch (Exception e) {
displayToast(context, "خطای آداپتر:" + "\n" + e.toString());
}
}
});
}

private void showPopupMenu(View view) {
PopupMenu popup = new PopupMenu(context, view);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.history_cardview_popup, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupItemClickListener());
popup.show();
}

private class PopupItemClickListener implements PopupMenu.OnMenuItemClickListener {
PopupItemClickListener() {
}

@Override
public boolean onMenuItemClick(MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.history_moment_update:
Intent intent = new Intent(context, DiaryUpdateActivity.class);
intent.putExtra("moment_id", MOMENT_ID);
intent.putExtra("moment_farsi_date", FARSI_DATE_TO_UPDATE);
context.startActivity(intent);
return true;
case R.id.history_moment_go_to_gallery:
Intent gallery_intent = new Intent(context, HistoryGalleryActivity.class);
gallery_intent.putExtra("moment_id", MOMENT_ID);
context.startActivity(gallery_intent);
return true;
case R.id.history_moment_delete:
boolean delete = TBL_MOMENT.DELETE_DIARY(MOMENT_ID);
if (delete) {
MOMENT_PEOPLE.DELETE_PEOPLE(MOMENT_ID);
MOMENT_PHOTO.DELETE_PHOTO(MOMENT_ID);
MOMENT_LOCATION.DELETE_LOCATION(MOMENT_ID);
displayToast(context, "حذف شد!");
}
return true;
case R.id.history_moment_location:
// Intent location_intent = new Intent(context, LocationGalleryActivity.class);
// location_intent.putExtra("moment_id", MOMENT_ID);
// context.startActivity(location_intent);
return true;
}
return false;
}
}

@Override
public int getItemCount() {
try {
return MOMENT_LIST.size();
} catch (Exception e) {
displayToast(context, "خطای آداپتر:" + "\n" + e.toString());
return 0;
}
}

private Filter filterResult = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
ArrayList<Moment> tempList = new ArrayList<>();
if (MOMENT_FILTER != null) {
if (TextUtils.isEmpty(constraint)) {
tempList = (ArrayList<Moment>) MOMENT_FILTER;
} else {
int length = MOMENT_LIST.size();
int i = 0;
while (i < length) {
Moment item = MOMENT_FILTER.get(i);
if (item.getMoment().contains(constraint))
tempList.add(item);
i++;
}
}
}
filterResults.values = tempList;
filterResults.count = tempList.size();
return filterResults;
}

@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
MOMENT_LIST = (ArrayList<Moment>) results.values;
if (results.count > 0) {
notifyDataSetChanged();
isFilteredMode = true;
TXT_SEARCH = constraint.toString();
} else {
isFilteredMode = false;
}
}
};

@Override
public Filter getFilter() {
return filterResult;
}
}


دقیقا همین یه قسمت برنامه مونده فقط ... خطا و چیزی هم نداره فقط موقع لود به قول معروف هنگ میکنه تا لود تکمیل بشه

Nevercom
چهارشنبه 15 شهریور 1396, 01:16 صبح
این شکلی که نمیشه مشکل رو فهمید، همونطور که گفتم اول زمانبر ترین بخش کد رو پیدا کنید.

دلیل اینکه کدها در Handler قرار داده شده چی هست ؟ من کدها رو که نگاه کردم دلیلی ندیدم، قبل از هرچیز کدها رو از تو هندلر خارج کنید.

ضمن اینکه در مورد خط های 123 تا 127 هم توضیج بدید، چرا تمام عناصر داخل Cursor پیمایش میشه، آبجکت Bitmap هم ایجاد میشه درحالی که تنها آخرینشون فرصت نمایش پیدا می کنه ؟

ghasem110deh
چهارشنبه 15 شهریور 1396, 01:31 صبح
هندلر رو گفتم شاید باعث بشه سرعت لود بهتر بشه !


Cursor cursor = MOMENT_PHOTO.LAST_MOMENT_PHOTO(holder.ID); if (cursor != null && cursor.moveToFirst()) {
for (int i = 0; cursor.getCount() > i; i++) {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(cursor.getString(0)));
holder.IMG_PHOTO.setImageBitmap(Bitmap.createScale dBitmap(bitmap, 240, 320, false));
cursor.moveToNext();
}
if (cursor.getCount() > 0)
holder.TXT_NULL_PHOTO.setVisibility(View.INVISIBLE );
cursor.close();




توی جدول عکس به ازای هر آیدی ممکنه چندین عکس باشه ... اینجاخواستم اخرین عکس رو که ذخیره شده بگیرم !!!
اشتباه روش کار ؟

ghasem110deh
چهارشنبه 15 شهریور 1396, 01:49 صبح
اینجا رو سوتی دادم ...
با کوئری هم میشه اخرین آدرس عکس رو گرفت و یه استرینگ فرستاد اینجا ... که دیگه نیازی نباشه کرسر توی لیست بگرده

ولی فکر نکنم خیلی زمان بر باشه اینم

دیگه اعصابم خورد شد :لبخند:

Nevercom
چهارشنبه 15 شهریور 1396, 01:53 صبح
اگه قصد دارید آخرین رو بگیرید، تو کوئری دیتابیس بر اساس id و بصورت نزولی مرتب کنید و limit رو 1 بزارید تا تنها یک رکورد که آخری هست برگرده. دیگه نیازی به for هم نیست.

حتی اگه به هردلیلی از همین for هم قرار باشه استفاده بشه، دیگه نباید تو حلقه هربار یه Bitmap ساخته بشه، دیگه نهایتش با حلقه آخرین آدرس رو بدست میارید و بعد از اتمام حلقه عکس رو لود می کنید.

این Handler کمکی به سرعت نمیکنه، الان دارید این بخش از کدها رو میفرستید به UI Thread تا اونجا اجرا بشه (درحالی که الان تو UI Thread هستی و نیازی به این کار هم نیست و قطعاً سرعت رو بالاتر نمیبره)

و به بهینه سازی نهایی، میتونی OnClickListener ها رو منتقل کنی به ViewHolder. الان به ازای هر رکورد داری یه کلاس OnClickListener ایجاد می کنی که نیازی هم بهش نیست (این کمتر در سرعت تاثیر داره و بهتره بگم با اینکه درست نیست، اما کدها رو خیلی کندتر نمیکنه)

ghasem110deh
چهارشنبه 15 شهریور 1396, 02:23 صبح
اگه قصد دارید آخرین رو بگیرید، تو کوئری دیتابیس بر اساس id و بصورت نزولی مرتب کنید و limit رو 1 بزارید تا تنها یک رکورد که آخری هست برگرده. دیگه نیازی به for هم نیست.

حتی اگه به هردلیلی از همین for هم قرار باشه استفاده بشه، دیگه نباید تو حلقه هربار یه Bitmap ساخته بشه، دیگه نهایتش با حلقه آخرین آدرس رو بدست میارید و بعد از اتمام حلقه عکس رو لود می کنید.

این Handler کمکی به سرعت نمیکنه، الان دارید این بخش از کدها رو میفرستید به UI Thread تا اونجا اجرا بشه (درحالی که الان تو UI Thread هستی و نیازی به این کار هم نیست و قطعاً سرعت رو بالاتر نمیبره)

و به بهینه سازی نهایی، میتونی OnClickListener ها رو منتقل کنی به ViewHolder. الان به ازای هر رکورد داری یه کلاس OnClickListener ایجاد می کنی که نیازی هم بهش نیست (این کمتر در سرعت تاثیر داره و بهتره بگم با اینکه درست نیست، اما کدها رو خیلی کندتر نمیکنه)


اگه قصد دارید آخرین رو بگیرید، تو کوئری دیتابیس بر اساس id و بصورت نزولی مرتب کنید و limit رو 1 بزارید تا تنها یک رکورد که آخری هست برگرده. دیگه نیازی به for هم نیست.

اینو اصلاح کردم ... ظاهرا بهتر شده ! هندلر هم برداشتم ؛
منتها باید دوباره روی گوشی نصب کنم و داده وارد کنم !