题 将图像加载到Bitmap对象时出现奇怪的内存不足问题


我有一个列表视图,每行有几个图像按钮。单击列表行时,将启动新活动。由于相机布局的问题,我不得不建立自己的标签。为结果启动的活动是地图。如果我单击我的按钮启动图像预览(从SD卡加载图像),应用程序将从活动返回到 listview 结果处理程序的活动,以重新启动我的新活动,这只不过是一个图像小部件。

列表视图上的图像预览正在使用光标和 ListAdapter。这使得它非常简单,但我不确定如何放置一个已调整大小的图像(即,较小的位大小不是像素,因为 src 对于动态图像按钮。所以我只是重新调整了手机摄像头拍摄的图像。

问题是当我试图返回并重新启动第二个活动时,我收到内存不足错误。

  • 有没有办法我可以轻松地逐行构建列表适配器,我可以在运行中调整大小(有点明智)?

这是更好的,因为我还需要对每行中的小部件/元素的属性进行一些更改,因为焦点问题我无法选择带触摸屏的行。 (我可以用滚球。

  • 我知道我可以做一个带外调整大小并保存我的图像,但这不是我想要做的,但是一些示例代码会很好。

一旦我在列表视图上禁用了图像,它就再次正常工作。

仅供参考:我就是这样做的:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

哪里 R.id.imagefilename 是一个 ButtonImage

这是我的LogCat:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

显示图像时出现新错误:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

1108


起源


我把日志放在一个代码块中给它滚动条 - UnkwnTech
我通过避免使用Bitmap.decodeStream或decodeFile并使用BitmapFactory.decodeFileDescriptor方法解决了这个问题。 - Fraggle
几周前我也遇到了类似的问题,我通过缩小图像达到最佳点来解决它。我在博客中写了完整的方法 codingjunkiesforum.wordpress.com/2014/06/12/... 并上传完整的示例项目与OOM倾向代码vs OOM证明代码运行://github.com/shailendra123/BitmapHandlingDemo - Shailendra Singh Rajawat
关于这个问题的公认答案正在讨论中 元 - rene
如果您没有阅读Android开发人员指南,就会发生这种情况 - Pedro Varela


答案:


Android培训 班级,“有效显示位图“,为理解和处理异常提供了一些很好的信息 java.lang.OutOfMemoryError: bitmap size exceeds VM budget 加载位图时。


读取位图尺寸和类型

BitmapFactory class提供了几种解码方法(decodeByteArray()decodeFile()decodeResource()等)用于创建 Bitmap 来自各方面。根据图像数据源选择最合适的解码方法。这些方法尝试为构造的位图分配内存,因此很容易产生一个 OutOfMemory 例外。每种类型的解码方法都有其他签名,可让您通过指定解码选项 BitmapFactory.Options 类。设置 inJustDecodeBounds 财产 true 解码时避免内存分配,返回 null 对于位图对象但设置 outWidthoutHeight 和 outMimeType。此技术允许您在构造(和内存分配)位图之前读取图像数据的尺寸和类型。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

避免 java.lang.OutOfMemory 异常,在解码之前检查位图的尺寸,除非您完全信任源,为您提供可预测大小的图像数据,这些数据可以轻松地放入可用内存中。


将缩小版本加载到内存中

既然图像尺寸已知,它们可用于决定是否应将完整图像加载到内存中,或者是否应加载子采样版本。以下是需要考虑的因素:

  • 估计在内存中加载完整映像的内存使用情况。
  • 在给定应用程序的任何其他内存要求的情况下,您愿意承诺加载此映像的内存量。
  • 要加载图像的目标ImageView或UI组件的尺寸。
  • 屏幕尺寸和当前设备的密度。

例如,如果最终将在一个128x96像素的缩略图中显示,则不值得将1024x768像素图像加载到内存中。 ImageView

要告诉解码器对图像进行二次采样,将较小的版本加载到内存中,进行设置 inSampleSize 至 true 在你的 BitmapFactory.Options 目的。例如,分辨率为2048x1536的图像用 inSampleSize 4的位图产生大约512x384的位图。将其加载到内存中对于完整图像使用0.75MB而不是12MB(假设位图配置为 ARGB_8888)。这是一种根据目标宽度和高度计算样本大小值的方法,该值为2的幂:

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

注意:计算两个幂的幂,因为解码器使用a   最终值通过四舍五入到最接近的2的幂,按照    inSampleSize 文档。

要使用此方法,请先解码 inJustDecodeBounds 设置 true,传递选项,然后使用新的再次解码 inSampleSize 价值和 inJustDecodeBounds 设置 false

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

这种方法可以很容易地将任意大尺寸的位图加载到 ImageView 显示100x100像素的缩略图,如以下示例代码所示:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

您可以通过替换相应的方法,按照类似的过程解码来自其他来源的位图 BitmapFactory.decode* 根据需要的方法。


561



正在讨论这个答案 元 - rene
这个答案(通过链接获得的信息除外)并没有为答案提供太多解决方案。链接的重要部分应该合并到问题中。 - FallenAngel
这个答案就像问题和其他答案一样是社区维基,所以这是社区可以通过编辑修复的东西,不需要主持人介入。 - Martijn Pieters♦


要修复OutOfMemory错误,您应该执行以下操作:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

这个 inSampleSize 选项可减少内存消耗。

这是一个完整的方法。首先,它读取图像大小而不解码内容本身。然后它找到最好的 inSampleSize 值,它应该是2的幂,最后图像被解码。

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

858



请注意,10可能不是inSampleSize的最佳值,但文档建议使用2的幂。 - Mirko N.
我面临和Chrispix一样的问题,但我不认为这里的解决方案能够真正解决问题,而是回避它。更改样本大小会减少使用的内存量(以图像质量为代价,这对于图像预览来说可能没什么用),但是如果解码的图像流足够大,则不会阻止异常,如果多个图像流是解码。如果我找到一个更好的解决方案(并且可能没有),我会在这里发布一个答案。 - Flynn81
您只需要一个合适的尺寸来匹配屏幕的像素密度,以便放大,这样您就可以以更高的密度拍摄图像样本。 - stealthcopter
REQUIRED_SIZE是您要缩放到的新大小。 - Fedor
这个解决方案帮助了我,但图像质量很糟糕。我正在使用viewfilpper来显示图像的任何建议? - user1106888


我对Fedor的代码做了一点改进。它基本上是一样的,但没有(在我看来)丑陋的while循环,它总是导致2的幂。感谢Fedor制作原始解决方案,我被困住直到找到了他,然后我才能做到这一点:)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

354



是的,你是对的,而不是那么美丽。我只是想向大家说清楚。谢谢你的代码。 - Fedor
@Thomas Vervest - 这段代码存在很大问题。 ^不会提高2到一个幂,它与结果xors 2。你想要Math.pow(2.0,...)。否则,这看起来不错。 - DougW
哦,这是一个非常好的!我的不好,我会马上纠正,谢谢你的回复! - Thomas Vervest
什么,IMAGE_SIZE在这? - nicky
您正在创建两个新的FileInputStream,每个调用一次 BitmapFactory.decodeStream()。你不必保存对它们的引用,以便它们可以在一个中关闭 finally 块? - matsev


我来自iOS经验,我很沮丧地发现了一个像加载和显示图像这样基本的问题。毕竟,遇到此问题的每个人都试图显示合理大小的图像。无论如何,这里有两个修改我的问题的变化(并使我的应用程序非常敏感)。

1)每次你这样做 BitmapFactory.decodeXYZ(),一定要传入 BitmapFactory.Options 同 inPurgeable 设置 true (最好是 inInputShareable 也设置为 true)。

2)永远不要使用 Bitmap.createBitmap(width, height, Config.ARGB_8888)。我的意思是从来没有!经过几次传球后,我从未有过这样的事情。没有数量 recycle()System.gc()无论有什么帮助。它总是引发异常。另一种实际工作方式是在drawables中使用虚拟图像(或者使用上面的步骤1解码的另一个Bitmap),将其重新缩放到您想要的任何位置,然后操纵生成的Bitmap(例如将其传递给Canvas)为了更多的乐趣)。所以,你应该使用的是: Bitmap.createScaledBitmap(srcBitmap, width, height, false)。如果由于某种原因你必须使用暴力创造方法,那么至少通过 Config.ARGB_4444

这几乎可以保证,如果不是几天,你可以节省数小时。所有关于缩放图像等的讨论都不起作用(除非您考虑将错误的大小或降级的图像作为解决方案)。


221



BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true; 和 Bitmap.createScaledBitmap(srcBitmap, width, height, false); 解决了我在android 4.0.0上遇到内存不足的问题。谢了哥们! - Jan-Terje Sørensen
在Bitmap.createScaledBitmap()调用中,您应该使用true作为flag参数。否则,放大时图像的质量将不平滑。检查这个帖子 stackoverflow.com/questions/2895065/... - rOrlig
这真是一个很棒的建议。希望我能给你一个额外的+1,让谷歌为这个令人惊讶的臭丁克虫做任务。我的意思是......如果它不是一个错误,那么文档真的需要有一些严重闪烁的霓虹灯标志说“这就是你处理照片的方式”,因为我已经挣扎了2年,刚刚发现这篇文章。很棒的发现。 - Genia S.
缩小图像肯定有帮助,但这是一个重要的步骤,最终为我解决了这个问题。只是缩放图像的问题是,如果你有很多图像,或者如果源图像非常大,那么你仍然会遇到同样的问题。给你以法莲+1。 - Dave
截至棒棒糖, BitmapFactory.Options.inPurgeable 和 BitmapFactory.Options.inInputShareable 不推荐使用 developer.android.com/reference/android/graphics/... - Denis Kniazhev


它是 已知的bug,这不是因为大文件。由于Android缓存了Drawables,因此在使用少量图像后会出现内存不足的情况。但是我通过跳过android默认缓存系统找到了另一种方法。

: 将图像移动到“assets”文件夹并使用以下函数获取BitmapDrawable:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}

85





我遇到了同样的问题并通过避免使用BitmapFactory.decodeStream或decodeFile函数来解决它 BitmapFactory.decodeFileDescriptor

decodeFileDescriptor 看起来它比decodeStream / decodeFile调用不同的本机方法。

无论如何,这是有效的(请注意,我添加了一些选项,如上所述,但这不是产生差异的原因。重要的是调用 BitmapFactory.decodeFileDescriptor 代替 decodeStream 要么 decodeFile):

private void showImage(String path)   {
    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
    bfOptions.inTempStorage=new byte[32 * 1024]; 


    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;



}

我认为decodeStream / decodeFile中使用的本机函数存在问题。我已经确认在使用decodeFileDescriptor时会调用不同的本机方法。我还读到的是“图像(位图)不是以标准Java方式分配,而是通过本机调用分配;分配是在虚拟堆之外完成的,但是 算了吧!


70



从记忆中得出相同的结果,实际上,使用它的方法并不重要,这取决于你所持有的字节数来读取内存不足的数据。 - PiyushMishra
如果路径是网址怎么办? - Jesse


我认为最好的方法是避免 OutOfMemoryError 是面对它并理解它。

我做了一个 应用 故意造成的 OutOfMemoryError,并监控内存使用情况。

在我使用此应用程序完成了大量实验后,我得出以下结论:

我将在Honey Comb之前讨论SDK版本。

  1. 位图存储在本机堆中,但它会自动收集垃圾,调用recycle()是不必要的。

  2. 如果{VM堆大小} + {已分配的本机堆内存}> = {设备的VM堆大小限制},并且您尝试创建位图,则将抛出OOM。

    注意:计算VM HEAP SIZE而不是VM ALLOCATED MEMORY。

  3. 即使分配的VM内存缩小,VM堆大小也不会在增长后缩小。

  4. 因此,您必须尽可能降低峰值VM内存,以防止VM堆大小过大而无法为Bitmaps节省可用内存。

  5. 手动调用System.gc()是没有意义的,系统会在尝试增加堆大小之前先调用它。

  6. 原生堆大小永远不会缩小,但它不计入OOM,因此无需担心它。

然后,让我们来谈谈Honey Comb中的SDK Starts。

  1. 位图存储在VM堆中,本机内存不计入OOM。

  2. OOM的条件要简单得多:{VM heap size}> = {设备的VM堆大小限制}。

  3. 因此,您有更多可用内存来创建具有相同堆大小限制的位图,因此不太可能抛出OOM。

以下是我对垃圾收集和内存泄漏的一些观察。

您可以在App中自己查看。如果一个Activity执行了一个在销毁Activity之后仍在运行的AsyncTask,那么在AsyncTask完成之前,Activity不会被垃圾收集。

这是因为AsyncTask是匿名内部类的一个实例,它包含Activity的引用。

如果在后台线程中的IO操作中阻止任务,则调用AsyncTask.cancel(true)将不会停止执行。

回调也是匿名的内部类,因此如果项目中的静态实例保留它们并且不释放它们,则内存将被泄露。

如果您安排了重复或延迟的任务,例如Timer,并且您没有在onPause()中调用cancel()和purge(),则内存将被泄露。


64



AsyncTask不一定必须是“匿名内部类的实例”,Callbackks也是如此。您可以在它自己的文件中创建一个新的公共类,该文件扩展了AsyncTask,甚至是 private static class 在同一个班。他们不会对活动有任何提及(除非你给他们一个当然) - Simon Forsberg