图像加载过程中多线程问题 [英] Multi-threading issues during image loading

查看:172
本文介绍了图像加载过程中多线程问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的应用程序,有一个的ListView 它使用一个自定义的的CursorAdapter 将数据加载到它基本上是一个的ImageView 的TextView 。我加载在图像的的AsyncTask ,现在的问题是,最初所有图像被分配到正确的文本,但是当我向上和向下滚动很快,它结合每个随机图像文字。由于我使用的CursorAdapter,我不能使用 ViewHolder 在这里,所以我应该做些什么来解决这个问题?

In my app , there is a ListView which uses a custom CursorAdapter to load data into it which is basically an ImageView and a TextView . I am loading images in an AsyncTask , now the problem is , initially all images are assigned to correct text , but when i scroll up and down quickly , it binds random image for each text . Since I am using CursorAdapter , I can't use ViewHolder here , so what shall i do to eliminate this problem?

下面是我的示例code:

Here is my sample code :

public class MyAdapter extends CursorAdapter {

    private LayoutInflater mLayoutInflater;
    @SuppressWarnings("unused")
    private Context mContext;

    public MyAdapter(Context context, Cursor c, int flags) {
        super(context, c, flags);
        mContext = context;
        mLayoutInflater = LayoutInflater.from(context);
    }
    public void bindView(View view, Context context, Cursor cursor) {
        String title = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.TITLE));
        String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
        TextView text = (TextView)view.findViewById(R.id.txtTitle);
        text.setText(title);
        Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
        Uri uri = ContentUris.withAppendedId(sArtworkUri, Integer.valueOf(album_id));
        new MyImageLoader(context,view).execute(uri);

    }
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        return mLayoutInflater.inflate(R.layout.row, parent, false);
    }
    private class MyImageLoader extends AsyncTask<Uri, Void, Bitmap>{
        Context context;
        View view;


        MyImageLoader(Context context,View view){
            this.context = context;
            this.view = view;
        }
        @Override
        protected Bitmap doInBackground(Uri... uri) {
            ContentResolver res = context.getContentResolver();
            InputStream in = null;
            try {
                in = res.openInputStream(uri[0]);
            } 
            catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Bitmap artwork = BitmapFactory.decodeStream(in);
            return artwork;
        }
        protected void onPostExecute(Bitmap bmp){
            ImageView iv = (ImageView)view.findViewById(R.id.imgIcon);
            if(bmp!=null)
                //iv.setImageBitmap(bmp);
                iv.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false));
        }
    }
}

更新:我采用的方法setTag,现在洗牌较少,但是当我快速滚动,直到正确的图像加载的第二个旧图像仍然存在。这是新的code:

UPDATED : I applied the setTag method , now the shuffling is less , however when I scroll fast the old image persists for a second until the correct image is loaded. Here is the new code :

public void bindView(View view, Context context, Cursor cursor) {
        String title = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.TITLE));
        String album_id = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));
        ImageView iv = (ImageView)view.findViewById(R.id.imgIcon);
        TextView text = (TextView)view.findViewById(R.id.txtTitle);
        text.setText(title);
        Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");
        Uri uri = ContentUris.withAppendedId(sArtworkUri, Integer.valueOf(album_id));
// *****TAG SET*****
        iv.setTag(uri);
//***PASSING BOTH URI AND IMAGEVIEW TO CONSTRUCTOR***
        new MyImageLoader(context,view,iv,uri).execute(uri);

    }
    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        View v = mLayoutInflater.inflate(R.layout.row, parent, false);
        //v.setTag(R.id.imgIcon, v.findViewById(R.id.imgIcon));
        return v;
    }
    private class MyImageLoader extends AsyncTask<Object, Void, Bitmap>{
        Context context;
        View v;
        ImageView iv;
        Uri u;

        MyImageLoader(Context context,View v,ImageView iv,Uri u){
            this.context = context;
            this.v = v;
            this.iv = iv;   
            this.u = u;
        }
        @Override

        protected synchronized Bitmap doInBackground(Object... param) {
            ContentResolver res = context.getContentResolver();
            InputStream in = null;
            try {
                Uri uri= (Uri)param[0];
                in = res.openInputStream(uri);
            } 
            catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            Bitmap artwork = BitmapFactory.decodeStream(in);

            return artwork;
        }
        protected void onPostExecute(Bitmap bmp){

            if(bmp!=null)
            {   ImageView iv = (ImageView)v.findViewById(R.id.imgIcon);
                if(iv.getTag().toString().equals(u.toString()))
                    iv.setImageBitmap(Bitmap.createScaledBitmap(bmp, 100, 100, false));
            }
        }
    }

更新:设置一个占位符图像之前调用后台任务使得它好得多。

UPDATE : Setting a placeholder image before calling background task makes it much better.

推荐答案

这是我用一个解决方案是附加的乌里的ImageView 通过 setTag()。然后,当你去更新的ImageView ,请检查您是否即将图像的乌里申请比赛从 getTag值()。如果是这样,请继续更新的ImageView 。如果没有,则的ImageView 被回收,并且可以跳过更新。

One solution that I have used is to attach the Uri to the ImageView via setTag(). Then, when you go to to update the ImageView, check to see whether the Uri of the image you are about to apply matches the value from getTag(). If it does, go ahead and update the ImageView. If it does not, the ImageView was recycled, and you can skip the update.

另一种方法是<一个href=\"http://developer.android.com/reference/android/view/View.html#setHasTransientState%28boolean%29\"相对=nofollow>使用 setHasTransientState(真) 上的的ImageView (或者在该行 - 当你决定你有一个高速缓存未命中,需要揭开序幕的的AsyncTask 没试过)。这将导致适配器视图,以避免回收这一行,直到匹配的 setHasTransientState(假)已经调用。切特哈泽具有的一个的DevBytes视频,在施加动画一个的上下文的ListView 行,并尽量避免回收问题。然而, setHasTransientState()是一个新的API级别16,所以如果你想支持Android 4.0以上的设备,这不会是一个可行的选择。

Another approach is to use setHasTransientState(true) on the ImageView (or perhaps on the row -- haven't tried this) when you decide you have a cache miss and need to kick off an AsyncTask. This will cause AdapterView to avoid recycling that row, until a matching setHasTransientState(false) call has been made. Chet Haase has a DevBytes video on this, in the context of applying an animation to a ListView row and trying to avoid recycling problems. However, setHasTransientState() is new to API Level 16, so if you are trying to support Android 4.0 or older devices, this will not be an available option.

这篇关于图像加载过程中多线程问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆