简体   繁体   English

创建某些图像后内存不足

[英]Out of memory after some image creation

in my application I create some foto on button click where I take the screen view and thene I merge this image with 2 logo, the app with 5,10 image don't have problem but with more the 10 image I get an out of memory, this is the code: 在我的应用程序中,我在按钮单击上创建了一些图片,然后在其中单击屏幕视图,然后将这个图像与2个徽标合并,带有5,10个图像的应用程序没有问题,但是更多的10个图像使我的内存不足,这是代码:

    @Override
    public void onScreenshotImage(ImageStruct image) {
        //do whatever you want with the image parameter

        super.onScreenshotImage(image);

        Bitmap a = image.getBitmap();

        ResizeImage resize = new ResizeImage(a);
        resize.execute();

        Log.d("onScreenshot","get image");
    }

private class ResizeImage extends AsyncTask<String, Void, String> {
    Bitmap bottomImage;

    public ResizeImage (Bitmap image) {
        bottomImage = image;
    }

    @Override
    protected String doInBackground(String... params) {

        Bitmap output = Bitmap.createBitmap(bottomImage.getWidth(), bottomImage.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(output);
        Paint paint = new Paint();
        paint.setAntiAlias(true);

        canvas.drawBitmap(bottomImage, 0, 0, paint);

        Bitmap a = BitmapFactory.decodeResource(getResources(), R.drawable.logo);
        canvas.drawBitmap(a, 0, 0, paint);
        Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.logo_1);
        canvas.drawBitmap(b, bottomImage.getWidth()-(b.getWidth()+20), bottomImage.getHeight()-(b.getHeight()+30), paint);

        String outputString = Environment.getExternalStorageDirectory().getPath() + "/images/";

        File folder_thumb= new File(outputString);
        if (!folder_thumb.exists()) {
            folder_thumb.mkdirs();
        }

        String tmpImg = String.valueOf(System.currentTimeMillis()) + ".png";
        OutputStream os = null;
        try {
            os = new FileOutputStream(outputString + tmpImg);
            output.compress(Bitmap.CompressFormat.PNG, 100, os);
            os.close();
        }
        catch(IOException e) {
            Log.e("combineImages", "problem saving images", e);
        }

        a.recycle();
        b.recycle();
        output.recycle();
        bottomImage.recycle();

        return "Executed";
    }

    @Override
    protected void onPostExecute(String result)  {
        System.gc();
        Runtime.getRuntime().gc();
    }
}

ps. ps。 the first funciton is the function for get the image in metaio. 第一个功能是在metaio中获取图像的功能。 where is the mistake? 错误在哪里?

edit: I saw that waiting for the end of the task the memory does not exceed tot mb , while removing the block ( a simple boolean ) memory also goes to 100mb. 编辑:我看到等待任务结束的内存不会超过tot mb,同时删除块(一个简单的boolean)内存也将达到100mb。

You can do two things: 您可以做两件事:

  1. Handle this problem yourself with Android SDK 使用Android SDK自行解决此问题
  2. Let the appropriate library handle it for you 让适当的库为您处理

If you choose option 2 , just use Picasso . 如果选择选项2 ,则只需使用Picasso Check examples on their website. 在其网站上查看示例。 There's no simpler API for loading images on Android out there and it's better option. 没有更简单的API可以在Android上加载图像,这是更好的选择。

Sample API call looks like this: 示例API调用如下所示:

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

If you choose option 1 , you'll have more work and you can follow tips below: 如果您选择选项1 ,那么您将有更多工作要做,并且可以按照以下提示进行操作:

There's an article about Loading Large Bitmaps Efficiently on official Android Developers website, which is worth reading. 在Android开发者官方网站上有一篇关于有效加载大型位图的文章,值得一读。 It explains the problem and presents possible solution. 它解释了问题并提出了可能的解决方案。

We can deal with memory problem in three steps: 我们可以通过三个步骤来处理内存问题:

  1. Read image dimensions and type 阅读图像尺寸和类型
  2. Scale down image 缩小图像
  3. Load scaled down version of the image to memory 将缩小比例的图像加载到内存

You can use the following code snippet: 您可以使用以下代码段:

public static Bitmap decodeSampledBitmap(String filePath, int reqWidth, int reqHeight) {
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(filePath,options);
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(filePath,options);
}

You will also need calculateInSampleSize method borrowed from Android documentation : 您还需要从Android文档中借用calculateInSampleSize方法:

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;
}

In your AndroidManifest.xml add this line inside <application /> tag 在您的AndroidManifest.xml <application />标记内添加此行

android:largeHeap="true"

Remember the documentations warning about this though ( https://developer.android.com/training/articles/memory.html ): 请记住有关此的文档警告( https://developer.android.com/training/articles/memory.html ):

In very special situations, you can request a larger heap size by setting the largeHeap attribute to "true" in the manifest tag. 在非常特殊的情况下,可以通过在清单标记中将largeHeap属性设置为“ true”来请求更大的堆大小。 If you do so, you can call getLargeMemoryClass() to get an estimate of the large heap size. 如果这样做,则可以调用getLargeMemoryClass()以获得大堆大小的估计值。

However, the ability to request a large heap is intended only for a small set of apps that can justify the need to consume more RAM (such as a large photo editing app). 但是,请求大堆的能力仅适用于少数可以证明需要消耗更多RAM的应用程序(例如大型照片编辑应用程序)。 Never request a large heap simply because you've run out of memory and you need a quick fix—you should use it only when you know exactly where all your memory is being allocated and why it must be retained. 切勿仅仅因为内存用完并且需要快速修复而请求大堆-仅在确切知道所有内存的分配位置以及为什么必须保留它时,才应使用它。 Yet, even when you're confident your app can justify the large heap, you should avoid requesting it to whatever extent possible. 但是,即使您确信自己的应用程序可以证明大堆的合理性,也应尽可能避免请求它。 Using the extra memory will increasingly be to the detriment of the overall user experience because garbage collection will take longer and system performance may be slower when task switching or performing other common operations. 使用额外的内存将越来越不利于整体用户体验,因为在任务切换或执行其他常见操作时,垃圾回收将花费更长的时间并且系统性能可能会降低。

Additionally, the large heap size is not the same on all devices and, when running on devices that have limited RAM, the large heap size may be exactly the same as the regular heap size. 此外,大堆大小并非在所有设备上都相同,并且在具有有限RAM的设备上运行时,大堆大小可能与常规堆大小完全相同。 So even if you do request the large heap size, you should call getMemoryClass() to check the regular heap size and strive to always stay below that limit. 因此,即使您确实请求大堆大小,也应该调用getMemoryClass()来检查常规堆大小,并努力始终保持在该限制以下。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM