生产者消费者模式及一个实例

本文详细阐述了在线音乐播放器中如何运用生产者消费者模式解决图片加载问题,通过优化ListView实现图片实时显示,避免卡顿现象。重点介绍了模式的具体应用、线程管理与消息传递机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

生产者消费者模式及实例

  • 生产者消费者模式简介:在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象地称为生产者;而处理数据的模块,就称为消费者。 单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要有一个缓冲区处于生产者和消费者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区取出数据。

- 生产者消费者具体的应用在操作系统里面用的很多,本身详细来说也很复杂,所以直接上代码吧

`public class MusicAdapter extends BaseAdapter{
private Context context;
private List<Music> musics;
private LayoutInflater inflater;
//生成任务集合
private List<ImageLoadTask> tasks=new ArrayList<ImageLoadTask>();
//声明工作线程
private Thread workThread;
private boolean isLoop=true;
private ListView listView;
//声明handler
private Handler handler=new  Handler(){
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {
        case HANDLER_IMAGE_LOAD_SUCCESS:
            //更新ImageView
            ImageLoadTask task=(ImageLoadTask)msg.obj;
            //通过position做的tag 找到相应的ImageView
            ImageView ivAlbum=(ImageView)listView.findViewWithTag(task.position);
            if(ivAlbum!=null){
                Bitmap bitmap=task.bitmap;
                if(bitmap!=null){
                    ivAlbum.setImageBitmap(bitmap);
                }else{
                    ivAlbum.setImageResource(R.drawable.ic_launcher);
                }
            }
            break;
        }
    }
};

public static final int HANDLER_IMAGE_LOAD_SUCCESS=0;

public MusicAdapter(Context context, List<Music> musics, ListView listView) {
    this.listView=listView;
    this.context=context;
    this.musics=musics;
    this.inflater=LayoutInflater.from(context);
    //初始化workThread
    workThread=new Thread(){
        public void run() {
            //不断的查看集合中是否有数据
            while(isLoop){
                //如果集合中有图片下载任务
                if(!tasks.isEmpty()){
                    //把第一个图片下载任务获取并执行
                    ImageLoadTask task=tasks.remove(0);
                    //直接下载图片
                    Bitmap bitmap=loadBitmap(task.path);
                    //把bitmap设置到相应的ImageView中
                    //需要在主线程中  (发消息给handler)
                    task.bitmap=bitmap;
                    Message msg=new Message();
                    msg.what=HANDLER_IMAGE_LOAD_SUCCESS;
                    msg.obj=task;
                    handler.sendMessage(msg);

                }else{
                    //如果任务集合中已经没有任务了 
                    //那么线程等待
                    synchronized (workThread) {
                        try {
                            workThread.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };
    workThread.start();
}

/**
 * 通过path 发送http请求 下载图片
 * @param path 
 * images/junshengjinshi.jpg
 * @return
 * @throws IOException 
 * @throws ClientProtocolException 
 */
public Bitmap loadBitmap(String path) {
    try {
        String url=GlobalConsts.BASEURL+path;
        HttpEntity entity=HttpUtils.send(HttpUtils.METHOD_GET, url, null);
        //把entity转成Bitmap
        byte[] bytes=EntityUtils.toByteArray(entity);
        Bitmap bitmap=BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        return bitmap;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}


@Override
public int getCount() {
    return musics.size();
}

@Override
public Music getItem(int position) {
    return musics.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder=null;
    if(convertView==null){
        convertView=inflater.inflate(R.layout.item_lv_music, null);
        holder=new ViewHolder();
        holder.ivAlbum=(ImageView)convertView.findViewById(R.id.ivAlbum);
        holder.tvName=(TextView)convertView.findViewById(R.id.tvName);
        holder.tvAuthor=(TextView)convertView.findViewById(R.id.tvAuthor);
        holder.tvSinger=(TextView)convertView.findViewById(R.id.tvSinger);
        holder.tvDuration=(TextView)convertView.findViewById(R.id.tvDuration);
        convertView.setTag(holder);
    }
    holder=(ViewHolder)convertView.getTag();
    //给holder中持有的控件  赋值
    Music m=getItem(position);
    holder.tvName.setText(m.getName());
    holder.tvAuthor.setText(m.getAuthor());
    holder.tvDuration.setText(m.getDurationtime());
    holder.tvSinger.setText(m.getSinger());

    holder.ivAlbum.setTag(position);

    //向任务集合中添加任务
    ImageLoadTask task=new ImageLoadTask();
    task.path=m.getAlbumpic();//保存图片路径
    task.position=position;
    tasks.add(task);
    //唤醒工作线程workThread  起来干活
    synchronized (workThread) {
        workThread.notify();
    }
    return convertView;
}

class ImageLoadTask{
    String path;
    Bitmap bitmap;
    int position;
}

class ViewHolder{
    ImageView ivAlbum;
    TextView tvName;
    TextView tvAuthor;
    TextView tvSinger;
    TextView tvDuration;
}

- 这个在写在线音乐播放器的时候遇到的一个问题,本身是一个listview,进行了优化会在每一栏view显示图片,图片从网络获取实时显示(也可以下载到本地,但是在显示的时候也可以使用这种方法,特别是图片很大的时候),由于listview的特性,滑动时会启动当前显示的view的图片下载任务,这个任务包括了链接服务器,发送请求和获取返回的数据。任务消耗的时间很长,listview的滑动又很快,这样会导致不能实时显示(并且会卡爆,因为开了很多线程),所以采用了生产者消费者模式(其实也可以采用异步任务,但是这个以后后面博客会说到)。
- 生产者是getview函数,在定位到当前的view时,将图片的路径和相应参数position存入缓冲区tasks,然后唤醒线程(线程在缓冲区无数据时是等待状态)。
- 下面是仔细分析,首先workthread线程,这个线程是消费者,


        workThread=new Thread(){
        public void run() {
            //不断的查看集合中是否有数据
            while(isLoop){
                //如果集合中有图片下载任务
                if(!tasks.isEmpty()){
                    //把第一个图片下载任务获取并执行
                    ImageLoadTask task=tasks.remove(0);
                    //直接下载图片
                    Bitmap bitmap=loadBitmap(task.path);
                    //把bitmap设置到相应的ImageView中
                    //需要在主线程中  (发消息给handler)
                    task.bitmap=bitmap;
                    Message msg=new Message();
                    msg.what=HANDLER_IMAGE_LOAD_SUCCESS;
                    msg.obj=task;
                    handler.sendMessage(msg);

                }else{
                    //如果任务集合中已经没有任务了 
                    //那么线程等待
                    synchronized (workThread) {
                        try {
                            workThread.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    };

线程开始的时候就查看缓冲区,如果非空,则去除,并且获得图片,作为通过message传递给主线程,如果没有任务,则等待


        //向任务集合中添加任务
    ImageLoadTask task=new ImageLoadTask();
    task.path=m.getAlbumpic();//保存图片路径
    task.position=position;
    tasks.add(task);
    //唤醒工作线程workThread  起来干活
    synchronized (workThread) {
        workThread.notify();

这是针对缓冲区的操作,很好理解,存入缓冲区,唤醒线程。ImageLoadTask类型中存储了图片的路径,当前view的position和最后获得的数据,(有另外的方式,可以把position换为imageview,这样可以不用再次定位)。
- 补充部分:MainActivity activity =(MainActivity)context;activity.updateListView(musics);更新view的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值