【问题标题】:Optimization of ListView with Asynctask for Smoother Scroll使用 Asynctask 优化 ListView 以获得更平滑的滚动
【发布时间】:2015-11-09 15:23:57
【问题描述】:

我正在开发一个音乐应用程序和一个 android 应用程序的新手。我正在使用ListView 来显示设备中的音乐文件内容。这是我在ListView 中所做的:

  1. 显示音乐文件标题、艺术家和持续时间
  2. 音乐文件的专辑封面(如果有)

所以

我正在做的第一点很容易做到并且非常有效,滚动行为非常流畅,问题从第二点开始,即加载我正在使用AsyncTask 做的音乐文件的专辑封面。我得到AsyncTask 工作,但它不是最聪明的AsyncTask。例如,我希望 ListViewAsyncTask 提供这两种行为:

  1. 仅当用户停止滚动时才开始执行AsyncTask - 我想在用户尝试浏览视图(快速滚动)时节省重要的 CPU 周期。现在,使用我的方法,当所有AsyncTask 发生投掷时立即开始加载。我已经实现了函数cancelPotentialTask(),如果我不需要它,它会立即取消任务,但是如果我可以控制启动任务,那将比取消它更有效。就像当用户停止滚动 ListView 时,我得到一个启动 AsyncTask 的触发器。
  2. 如果AsyncTask一旦返回结果是没有专辑封面,那么我再调用它就不需要再次进入任务了-这可以通过下图很容易理解(抱歉编辑不好)。

在第一部分,我让视图与 AsyncTask 一起加载。在下一个中,我向上滑动并获得另一个视图,然后让一排去回收站。第三次向下滑动时,我得到了回收的视图,但 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 或 ....?
  • 我不确定,但那些可能只用于下载图像。如果它可以工作,那么我该如何处理音乐文件的专辑封面?
  • 我试过UIL,没有什么区别。滞后仍然是相同的。请帮忙。
  • 您是否从developer.android.com 获得此代码?我正在使用相同的代码进行修改,它可以工作。你能发布你实际上是如何调用 loadBitmap() 的吗?
  • 是的,我从 Google Developers 那里得到了这个,并且为支持 URI 的音乐文件做了一些修改。我在if-else 下方对convertView 进行了检查。

标签: android android-listview android-asynctask android-scroll


【解决方案1】:

使用库Picasso 后,滞后的滚动消失了。我仍然不确定这可能是什么确切原因,但只是用毕加索替换我的 AsyncTask 就像魅力一样。由于缓存由库本身完成,因此更加流畅和高效。

这里是图书馆https://github.com/square/picasso

【讨论】:

    猜你喜欢
    • 2018-12-30
    • 2017-03-12
    • 2014-01-12
    • 1970-01-01
    • 2016-05-06
    • 2011-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多