Я работаю над музыкальным приложением и новичком в приложениях для Android. Я использую ListView
, чтобы показать контент, который является музыкальным файлом на устройстве. Вот то, что я делаю в ListView
:Оптимизация ListView с помощью Asynctask для Smoother Scroll
- Отображение заголовка музыкального файла, исполнителя и длительность
- обложки альбома музыкального файла, если таковая имеется
Так
Первая точка из того, что я делаю, легко сделать и достаточно эффективно при действительно гладком прокрутке, проблема начинается со второй точки, т.е. загрузка обложки альбома музыкального файла, которую я делаю с AsyncTask
, Я получаю AsyncTask
, но это не самый умный AsyncTask
. Например, я хочу, чтобы эти два поведения от моего ListView
и AsyncTask
:
- начать делать
AsyncTask
только тогда, когда пользователь прекращает прокрутку - Я хочу, чтобы сохранить важные циклы процессора, когда пользователь пытается броситься через представление (прокручивается быстрее всего в поперечнике). Прямо сейчас с моим подходом, когда происходит всплытие, всеAsyncTask
начинает загрузку сразу. Я внедрил функциюcancelPotentialTask()
, и она отменяет задачу сразу, если мне это не нужно, но что, если я могу контролировать запуск задачи, тогда это было бы более эффективно, чем отменить ее. Например, когда пользователь останавливает прокруткуListView
Я получаю триггер, чтобы начатьAsyncTask
. - Если
AsyncTask
один раз вернет результат, если нет обложки альбомов, тогда вам не нужно снова входить в задание, если я его снова назову - это легко понять на картинке ниже (извините за плохое редактирование).
В первой части я позволяю представлениям загружаться вместе с AsyncTask. В следующем, я прокручиваю вверх и получаю другое представление и позволяю одному ряду идти к recycler. В третий раз я прокручиваю вниз, я получаю обратный просмотр назад, но Asynctask загружается, но мне не нужен AsyncTask, так как это действительно очевидный результат будет нулевым, как это было в первый раз. Я знаю, что код не знает этого, пока мы этого не скажем. Но как я могу сказать. У меня есть ключ в getView()
, где я вижу, convertView == null
.
Спасибо за чтение до сих пор, я ценю ваше терпение. Вот сыр для вас -
public Bitmap getAlbumart(Long album_id) {
Bitmap bm = null;
try {
final Uri sArtworkUri = Uri
.parse("content://media/external/audio/albumart");
Uri uri = ContentUris.withAppendedId(sArtworkUri, album_id);
ParcelFileDescriptor pfd = activity.getContentResolver()
.openFileDescriptor(uri, "r");
if (pfd != null) {
FileDescriptor fd = pfd.getFileDescriptor();
bm = BitmapFactory.decodeFileDescriptor(fd);
}
} catch (Exception ignored) {
}
return bm;
}
// This is what I call to get image in getView()
public void loadBitmap(String resId, ImageView imageView) {
if (cancelPotentialWork(resId, imageView)) {
String imageKey = String.valueOf(resId);
LoadRows task = new LoadRows(imageView);
SoftReference<Bitmap> bitmap1;
try {
bitmap1 = new SoftReference<>(Bitmap.createScaledBitmap(getBitmapFromMemCache(imageKey), 80, 80, true));
imageView.setImageBitmap(bitmap1.get());
} catch (Exception e) {
SoftReference<AsyncDrawable> asyncDrawable = new SoftReference<>(new AsyncDrawable(resId, null,
task));
imageView.setImageDrawable(asyncDrawable.get());
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, resId);
}
}
}
public class LoadRows extends AsyncTask<String, Void, Bitmap> {
String data = null;
private final WeakReference<ImageView> imageViewReference;
public LoadRows(ImageView imageView) {
// Use a WeakReference to ensure the ImageView can be garbage
// collected
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground(String... params) {
data = params[0];
Bitmap imageResult;
imageResult = getAlbumart(Long.parseLong(data));
if (imageResult != null) {
imageResult = Bitmap.createScaledBitmap(imageResult, 300, 300, true);
addBitmapToMemoryCache(data, imageResult);
imageResult = Bitmap.createScaledBitmap(imageResult, 80, 80, true);
}
return imageResult;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
if (isCancelled()) {
bitmap = null;
}
if (bitmap != null) {
ImageView imageView = imageViewReference.get();
LoadRows bitmapWorkerTask = getLoadRows(imageView);
if (this == bitmapWorkerTask) {
imageView.setImageBitmap(bitmap);
}
}
}
}
static class AsyncDrawable extends BitmapDrawable {
private final WeakReference<LoadRows> LoadRowsReference;
@SuppressWarnings("deprecation")
public AsyncDrawable(String res, Bitmap bitmap, LoadRows LoadRows) {
super(bitmap);
LoadRowsReference = new WeakReference<LoadRows>(LoadRows);
}
public LoadRows getLoadRows() {
return LoadRowsReference.get();
}
}
public static boolean cancelPotentialWork(String data, ImageView imageView) {
final LoadRows LoadRows = getLoadRows(imageView);
if (LoadRows != null) {
final String bitmapData = LoadRows.data;
// If bitmapData is not yet set or it differs from the new data
if (bitmapData == "0" || bitmapData != data) {
// Cancel previous task
LoadRows.cancel(true);
} else {
// The same work is already in progress
return false;
}
}
// No task associated with the ImageView, or an existing task was
// cancelled
return true;
}
private static LoadRows getLoadRows(ImageView imageView) {
if (imageView != null) {
final Drawable drawable = imageView.getDrawable();
if (drawable instanceof AsyncDrawable) {
final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
return asyncDrawable.getLoadRows();
}
}
return null;
}
Пожалуйста, ребята, не торопитесь, я не спешит принять любой ответ очень скоро. Надеюсь, что кто-то может сыграть с кодом или если ничего, кроме как объяснить мне, каким должен быть мой подход.
Если вам, ребята, нужно что-то еще знать, просто дайте мне знать. Спасибо!
Редактировать
GetView() метод -
@SuppressWarnings("deprecation")
@SuppressLint("SimpleDateFormat")
public View getView(final int position, View convertView, ViewGroup parent) {
View vi = convertView;
final ViewHolder holder;
if (convertView == null) {
/****** Inflate tabitem.xml file for each row (Defined below) *******/
vi = inflater.inflate(R.layout.row, null);
/****** View Holder Object to contain tabitem.xml file elements ******/
holder = new ViewHolder();
holder.text = (TextView) vi.findViewById(R.id.text);
holder.text1 = (TextView) vi.findViewById(R.id.text1);
holder.duration = (TextView) vi.findViewById(R.id.textTime);
holder.image = (ImageView) vi.findViewById(R.id.image);
holder.btn = (Button) vi.findViewById(R.id.button1);
/************ Set holder with LayoutInflater ************/
vi.setTag(holder);
} else {
holder = (ViewHolder) vi.getTag();
}
if (data.size() > 0) {
/***** Get each Model object from Arraylist ********/
tempValues = null;
tempValues = (ListModel) data.get(position);
final String duration = tempValues.getDuration();
final String artist = tempValues.getArtist();
final String songName = tempValues.getName();
final String title = tempValues.getTitle();
final String albumid = tempValues.getAlbumID();
String finalTitle;
if (title != null) {
finalTitle = title;
} else {
finalTitle = songName;
}
loadBitmap(albumid, holder.image); // HERE I GET IMAGE ***********
holder.text.setText(finalTitle);
holder.text1.setText(artist);
holder.duration.setText(duration);
vi.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
MusicUtils.play(position, listModelToArrayList(), activity, songsManager);
}
});
final PopupMenu pop = new PopupMenu(activity, holder.btn);
int[] j = new int[6];
j[0] = MusicUtils.PLAY;
j[1] = MusicUtils.PLAY_NEXT;
j[2] = MusicUtils.ADD_TO_QUEUE;
j[3] = MusicUtils.ADD_TO_PLAYLIST;
j[4] = MusicUtils.SHUFFLE_PLAY;
j[5] = MusicUtils.DELETE;
MusicUtils.generateMenu(pop, j);
pop.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case MusicUtils.PLAY:
MusicUtils.play(position, listModelToArrayList(), activity, songsManager);
return true;
case MusicUtils.DELETE:
return true;
case MusicUtils.PLAY_NEXT:
MusicUtils.playNext(listModelToArrayList().get(position));
return true;
case MusicUtils.ADD_TO_QUEUE:
MusicUtils.addToQueue(listModelToArrayList().get(position), songsManager);
return true;
case MusicUtils.ADD_TO_PLAYLIST:
MusicUtils.addToPlaylist(position, listModelToArrayList().get(position), activity, songsManager);
return true;
case MusicUtils.SHUFFLE_PLAY:
MusicUtils.shufflePlay(position, listModelToArrayList(), activity, songsManager);
return true;
default:
return false;
}
}
});
holder.btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
pop.show();
}
});
tempValues = null;
vi.setVisibility(View.VISIBLE);
} else {
vi.setVisibility(View.INVISIBLE);
}
return vi;
}
Обновление
С помощью UIL
, то результат будет почти такой же, как код, который я до предусмотренного т.е.лаг. Но Picasso
действительно стоит попробовать, я бы сказал, что отставание почти исчезло. Это почти то, что я хотел реализовать.
Почему вы не используете библиотеку для загрузки изображений? например, UIL (универсальный загрузчик изображений) или Picasso или ...? –
Я не уверен, но это работает только для загрузки изображений. Если это может сработать, то как я могу это сделать с альбомами музыкальных файлов? – Gavin
Я попробовал UIL, и нет никакой разницы. Лаг все еще такой же. Пожалуйста помоги. – Gavin