What happens with view references in unfinished AsyncTask after orientation change?

*爱你&永不变心* 提交于 2019-12-13 02:34:28

问题


Note this question was inspired by a comment to https://stackoverflow.com/a/33370816/519334 .

I have a holder class that belongs to a GridView-item with a reference to an ImageView

public static class Holder {
    ImageView mRowImage;
    String mImageUrl;

    // neccessary to cancel unfinished download
    BitmapLoaderAsyncTask mDownloader;
}

The corresponding GridItemAdapter uses an AsyncTask to get the image for the 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;
    }
}

A comment suggested that there might be a problem with this code after orientation change.

Szenario

  • Grid is in Landscape - mode
  • GridItemAdapter starts BitmapLoaderAsyncTask#1 for loading Image1.jpg
    • async task has mHolder.mRowImage
  • Grid orientation changes from Landscape to Portrait mode
  • BitmapLoaderAsyncTask#1 finishes and calls onPostExecute(..)
  • (!!!) In onPostExecute(..) the image mHolder.mRowImage is updated. Since mHolder.mRowImage does not exist any more because orientation change so there should be a crash.

I have code similar to the described scenario and up to now I havn-t had the (!!!) crash yet.

My Question

  • Is this just coincedence that there was no (!!!) crash yet?
  • Is there a simple solution to check in onPostExecute(..) that mHolder.mRowImage is not valid any more?
  • Or is there something in Android that protects the AsyncTask?

回答1:


Since mHolder.mRowImage does not exist any more because orientation change so there should be a crash.

That is not correct, all Threads are GC roots, and your AsyncTask is holding strong reference to View object (it's inside your Holder class) and your View has strong reference to Activity/Fragment/etc. So your Activity/Fragment/etc won't be properly garbage collected as long as your AsyncTask is running. It won't cause any crash (because View do exists) but memory leak will occur and result will be delivered to old Activity/Fragment/etc.

However if you make sure that AsyncTask is cancelled properly everything will be ok. But if you want to be 100% sure you should use WeakReference to hold you Holder class in BitmapLoaderAsyncTask

@edit

The way you do task cancelling now is incorrect. After orientation change all the view will be inflated once again (in new Activity/Fragment/etc), thus view.getTag will always result null.



来源:https://stackoverflow.com/questions/33394184/what-happens-with-view-references-in-unfinished-asynctask-after-orientation-chan

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!