【问题标题】:Updating Listview Images in another thread在另一个线程中更新 Listview 图像
【发布时间】:2013-04-20 19:48:31
【问题描述】:

我正在尝试制作一个自定义的 listview 适配器,它可以异步更新每个视图中的 imageView,这样它就不会变慢。

到目前为止,我正在使用一个向量来存储图像视图的位图,并且在适配器构造函数中,我正在调用一个获取所有位图的线程。 在 getView() 方法中,我检查 vector[position] 是否不为 null 以实际更改 imageView 位图。

代码如下:

public class SongAdapter extends ArrayAdapter<Song> {
private int resource;
private LayoutInflater inflater;
private ArrayList<Song> items;
private Context cntx;
private final Utilities util = new Utilities(); //una per tutte le canzoni
private Bitmap[] covers; //la utilizzo per fare un cashing delle cover

public SongAdapter(Context cntx, int resId, ArrayList<Song> objects)
{
    super(cntx, resId, objects);
    this.cntx = cntx;
    items = objects;
    resource = resId;
    inflater = LayoutInflater.from(cntx);
    covers = new Bitmap[objects.size()];
    asyncLoadImagesCover(); //lacio un thread per caricare le immagini
}


//carico le immagini nella listview in background
public void asyncLoadImagesCover()
{
    new Thread(new Runnable() {

        @Override
        public void run() {
            for(int i = 0; i < items.size(); i++)
                covers[i] = util.getAlbumArtFromSong(items.get(i).getPath(), cntx);

            //notifyDataSetChanged();
        }
    }).start();
}


//cerco di mantenere in memoria l'oggetto in modo da evitare il ricaricamento
private static class ViewHolder
{
    TextView titolo;
    TextView artista;
    ImageView cover;
}

//////////////////////////////////////////////////////////////////////////////////////

//Funzione che riscrive gli elementi della listview
@Override
public View getView(int position, View v, ViewGroup parent)
{
    //recupero l'oggetto canzone
    Song song = getItem(position);
    ViewHolder holder;

    if(v == null)
    {
        //creo la view partendo dal layout
        v = inflater.inflate(resource, parent, false);
        holder = new ViewHolder();
        holder.titolo = (TextView)v.findViewById(R.id.songTitle);
        holder.artista = (TextView)v.findViewById(R.id.artistText);
        holder.cover = (ImageView)v.findViewById(R.id.albumImage);
        v.setTag(holder);
    }
    else
        holder = (ViewHolder) v.getTag();

    //setto i valori a partire dall'holder, in questo modo non devo fare findviewbyid ogni volta
    holder.titolo.setText(song.getTitle());
    holder.artista.setText(song.getArtist());
    if(covers[position] != null)
        holder.cover.setImageBitmap(covers[position]);

    return v;
}
}

无论如何,这不是很好,50%的活动崩溃,我猜这是因为新线程。 我在网上搜索并找到了不同的解决方案,但我想先了解为什么我的代码会崩溃。另外,如何创建一个线程来更新我的 listview gui,在 getView 中传递 v 和 position?

////////////////////////////////////// 编辑:

对于任何感兴趣的人,这就是我所做的: 我创建了一个类,其中包含两个单独的对象,它使用一个处理程序来更新 UI,并使用一个 ExecutorService 来堆栈线程,这些线程通过我的函数获取图像:

public class CoverLoader  {

Utilities util = new Utilities();
Context cntx;
ExecutorService executorService;
Handler handler = new Handler(); //si occupa di aggiornare la UI

public CoverLoader(Context cntx)
{
    this.cntx = cntx;
    executorService = Executors.newFixedThreadPool(5);
}

public void DisplayImage(ImageView iv, String path)
{
    executorService.submit(new ImageLoader(iv,path));
}


//Separo la parte del caricamento dalla parte di aggiornamento della gui
//questa classe si occupa solo di recuperare la bmp
public class ImageLoader implements Runnable
{

    ImageView iv;
    String path;

    public ImageLoader(ImageView iv, String path)
    {
        this.iv = iv;
        this.path = path;
    }

    @Override
    public void run()
    {
        Bitmap bmp = util.getAlbumArtFromSong(path, iv.getContext());
        handler.post(new BitmapDisplayer(iv,bmp));
    }
}

//Si occupa di aggiornare la UI, richiamata dall'handler a termine del processo di caricamento

class BitmapDisplayer implements Runnable
{
    Bitmap bitmap;
    ImageView iv;

    public BitmapDisplayer(ImageView iv, Bitmap bitmap)
    {
        this.iv = iv;
        this.bitmap = bitmap;
    }

    @Override
    public void run()
    {
        iv.setImageBitmap(bitmap);
    }
}

}

现在在 getView() 上:

@Override
public View getView(int position, View v, ViewGroup parent)
{
    Song song = getItem(position);
    ViewHolder holder;

    if(v == null)
    {
        //creo la view partendo dal layout
        v = inflater.inflate(resource, parent, false);
        holder = new ViewHolder();
        holder.titolo = (TextView)v.findViewById(R.id.songTitle);
        holder.artista = (TextView)v.findViewById(R.id.artistText);
        holder.cover = (ImageView)v.findViewById(R.id.albumImage);
        v.setTag(holder);
    }
    else
        holder = (ViewHolder) v.getTag();

    //setto i valori a partire dall'holder, in questo modo non devo fare findviewbyid ogni volta
    holder.titolo.setText(song.getTitle());
    holder.artista.setText(song.getArtist());
    coverLoader.DisplayImage(holder.cover, song.getPath()); //Richiamo uno stack di thread

    //if(covers[position] != null)
        //holder.cover.setImageBitmap(covers[position]);

    return v;
}

到目前为止一切顺利,现在我将找到一种方法(也许是一个数组?)来保存我已经得到的图像

【问题讨论】:

  • 如果您想知道它在哪里崩溃,请发布堆栈跟踪。

标签: android multithreading image listview


【解决方案1】:

您不应该在绑定到 Activity 上下文的对象中执行线程,因为它是一个短暂的生命(内存泄漏)。

您也不应该下载所有图像,而只下载 getView 代码中请求的图像(性能)。

您还应该尽量避免多线程访问变量(同步)。

【讨论】:

  • 知道了,我犯了太多错误。按照你的说法,我已将这项工作委托给另一个班级。我正在发布我所做的事情
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-06
  • 2017-01-04
相关资源
最近更新 更多