Android 方向更改后,未完成的AsyncTask中的视图引用会发生什么情况?

Android 方向更改后,未完成的AsyncTask中的视图引用会发生什么情况?,android,android-asynctask,android-adapter,android-orientation,Android,Android Asynctask,Android Adapter,Android Orientation,注意:这个问题的灵感来自对的评论 我有一个holder类,它属于GridView项,引用了ImageView public static class Holder { ImageView mRowImage; String mImageUrl; // neccessary to cancel unfinished download BitmapLoaderAsyncTask mDownloader; } 相应的GridItemAdapter使用AsyncTas

注意:这个问题的灵感来自对的评论

我有一个holder类,它属于GridView项,引用了ImageView

public static class Holder {
    ImageView mRowImage;
    String mImageUrl;

    // neccessary to cancel unfinished download
    BitmapLoaderAsyncTask mDownloader;
}
相应的GridItemAdapter使用AsyncTask获取GridItem的映像

public class GridItemAdapter extends BaseAdapter {
    @Override
    public View getView(int position, ...) {
        ...
        if (view == null) {
            holder = ...
            ...
        } else {
            holder = (Holder) view.getTag();
        }
        ...
        // cancel unfinished mDownloader
        if (holder.mDownloader != null) {
            holder.mDownloader.cancel(false);
            holder.mDownloader = null;
        }
        holder.mImageUrl = mImageUrls.get(position);
        holder.mDownloader = new BitmapLoaderAsyncTask()
        holder.mDownloader.execute(holder); 
    }
}

static class BitmapLoaderAsyncTask extends AsyncTask<Holder, Void, Bitmap> {
    Holder mHolder;
    protected Bitmap doInBackground(Holder... holders) {
        mHolder = holders[0];
        ...
    }
    protected void onPostExecute(...) {
        mHolder.mDownloader = null; 
        if (!isCancelled()) {
            this.mHolder.mRowImage.setImageBitmap(image);
        }
        this.mHolder = null;
    }
}
公共类GridItemAdapter扩展了BaseAdapter{
@凌驾
公共视图getView(内部位置,…){
...
如果(视图==null){
持有人=。。。
...
}否则{
holder=(holder)view.getTag();
}
...
//取消未完成的mDownloader
if(holder.mDownloader!=null){
holder.mDownloader.cancel(false);
holder.mDownloader=null;
}
holder.mImageUrl=mImageUrls.get(位置);
holder.mDownloader=新的BitmapLoaderAsyncTask()
holder.mDownloader.execute(holder);
}
}
静态类BitmapLoaderAsyncTask扩展了AsyncTask{
持有人mHolder;
受保护位图背景(支架…支架){
mHolder=持有人[0];
...
}
受保护的void onPostExecute(…){
mHolder.mDownloader=null;
如果(!isCancelled()){
this.mHolder.mRowImage.setImageBitmap(图像);
}
this.mHolder=null;
}
}
一条评论指出,在方向更改后,此代码可能存在问题

塞纳里奥

  • 网格处于横向模式
  • GridItemAdapter启动BitmapLoaderAsyncTask#1以加载Image1.jpg
    • 异步任务具有mHolder.mRowImage
  • 栅格方向从横向模式更改为纵向模式
  • BitmapLoaderAsyncTask#1完成并调用
    onPostExecute(..)
  • (!!!)在
    onPostExecute(…)
    中,图像
    mHolder.mRowImage
    被更新。因为
    mHolder.mRowImage
    不再存在,因为方向改变了,所以应该会发生崩溃
我有 到目前为止,我还没有发生(!!!)车祸

我的问题

  • 这仅仅是没有(!!!)崩溃的巧合吗
  • 是否有一个简单的解决方案来签入
    onPostExecute(..)
    使
    mHolder.mRowImage
    不再有效
  • 或者安卓系统中有什么东西可以保护
    异步任务
因为mHolder.mRowImage不再存在,因为 改变,所以应该有一个崩溃

这是不正确的,所有线程都是GC根,并且您的
AsyncTask
持有对
View
对象的强引用(它在
Holder
类中)而且您的
视图
强烈引用了活动/片段/etc。因此,只要您的
异步任务
正在运行,您的活动/片段/etc就不会被正确地垃圾收集。它不会导致任何崩溃(因为视图确实存在),但会发生内存泄漏,结果将传递给旧的Activity/Fragment/etc

但是,如果您确保正确取消了
AsyncTask
,一切都会正常。但是如果你想100%确定你应该使用
WeakReference
BitmapLoaderAsyncTask

@编辑


您现在执行任务取消的方式不正确。更改方向后,所有视图将再次膨胀(在新活动/片段/etc中),因此view.getTag将始终为空。

当前任务取消代码用于取消网格视图外图像的图像下载。方向更改后,将重新创建带支架的项目。我同意这些引用可能会导致内存泄漏
GridItemView.tag
->
Holder.mRowImage
->
GridItemView
。如果我在
Holder
中使用
WeakReference
,那么
mHolder.mRowImage
onPostExecute(…)
中旋转后可能不再有效,这是真的
mRowImage
在旋转后将无效,这就是为什么有更好的方法在后台线程中工作的原因(类似于在屏幕方向改变期间幸存下来并将结果交付给适当活动/片段的装载机)。