4

I'm writing an app with a simple camera function. I take a picture in my main activity, and start a new thread which rotates the image, adds a transparent watermark and saves it as a .PNG.

This app runs great on my samsung galaxy S3 running a custom rom: AOKP, Android 4.4.2. The total time the execution of the code given below takes, is about 2 seconds. My app uses about 9 Mb of memory according to the task manager.

When I ran this app on my galaxy S4, running stock (rooted) Android 4.4.2, the execution time was over 27 seconds. The app uses about 124 Mb of memory according to the task manager (way too much). Occasionally the app crashes and throws an OutOfMemory error, this does not happen on the S3.

Here is the piece of code responsible for saving the image in my SaveThread:

            TimingLogger tl = new TimingLogger("MyTag", "Saving");            
            String name = new SimpleDateFormat("dd-MM-yyyy_HH-mm-ss")
                    .format(new Date());
            File myNewFolder = new File(path);
            myNewFolder.mkdir();
            File file = new File(path, name + ".JPEG");
            OutputStream imageFileOS;

            // Get EXIF rotation from byte array
            int rotation = getOrientation(data);
            tl.addSplit("Rotation calculated");
            // Convert byte array to bitmap, and watermark bitmap
            Bitmap pictureObject = BitmapFactory.decodeByteArray(data, 0,
                    data.length);
            tl.addSplit("Decoded ByteArray to Bitmap");

            // Rotate bitmap image according to EXIF data
            pictureObject = rotateBitmap(rotation, pictureObject);
            tl.addSplit("Bitmap rotated");
            // Add watermark to rotated bitmap
            Bitmap waterMarkedBitmap = addWaterMarkText(pictureObject);
            tl.addSplit("Bitmap watermarked");
            try {
                imageFileOS = new FileOutputStream(file);
                waterMarkedBitmap.compress(Bitmap.CompressFormat.PNG, 100,
                        imageFileOS);
                tl.addSplit("Bitmap compressed");
                imageFileOS.flush();
                imageFileOS.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

As you can see, I added a timing logger to find out what takes so long. The following LogCat return shows that compressing the Bitmap to .PNG takes very long. I think this may have something to do with the garbage collector. My Logcat returns the following during execution:

        02-04 13:57:08.166: I/Choreographer(30003): Skipped 73 frames!  The
        application may be doing too much work on its main thread.
        02-04 13:57:10.528: D/dalvikvm(30003): GC_FOR_ALLOC freed 552K, 19% free 24803K/30324K, paused 48ms, total 49ms
        02-04 13:57:10.628: I/dalvikvm-heap(30003): Grow heap (frag case) to 76.336MB for 51121168-byte allocation
        02-04 13:57:10.918: D/dalvikvm(30003): GC_FOR_ALLOC freed 4K, 7% free 74723K/80248K, paused 23ms, total 23ms
        02-04 13:57:11.019: I/dalvikvm-heap(30003): Grow heap (frag case) to 125.086MB for 51121168-byte allocation
        02-04 13:57:11.859: D/dalvikvm(30003): GC_FOR_ALLOC freed 49923K, 43% free 74723K/130172K, paused 25ms, total 26ms
        02-04 13:57:11.869: I/dalvikvm-heap(30003): Grow heap (frag case) to 125.086MB for 51121168-byte allocation
        02-04 13:57:39.967: D/MyTag(30003): Saving: begin
        02-04 13:57:39.967: D/MyTag(30003): Saving:      3 ms, Rotation calculated
        02-04 13:57:39.967: D/MyTag(30003): Saving:      418 ms, Decoded ByteArray to Bitmap
        02-04 13:57:39.967: D/MediaScannerConnection(30003): Scanning file 7klwibgf7fvntblfd7(7KhzCbvfib7(,6()6)(*._*+6./6*(5QHFG
        02-04 13:57:39.967: D/MyTag(30003): Saving:      939 ms, Bitmap rotated
        02-04 13:57:39.967: D/MyTag(30003): Saving:      96 ms, Bitmap watermarked
        02-04 13:57:39.967: D/MyTag(30003): Saving:      28023 ms, Bitmap compressed
        02-04 13:57:39.967: D/MyTag(30003): Saving:      0 ms, Done saving
        02-04 13:57:39.967: D/MyTag(30003): Saving:      8 ms, Media scanned
        02-04 13:57:39.967: D/MyTag(30003): Saving: end, 29487 ms

I am not sure this is related, but the LogCat line:

D/MediaScannerConnection(30003): Scanning file 7klwibgf7fvntblfd7(7KhzCbvfib7(,6()6)(*._*+6./6*(5QHFG

clearly does not show the path of the scanned file (though it does work). When I connect my S3 to Eclipse, this line does show the scanned file path.

I tried changing the compression quality but it didn't make a significant change in speed or memory usage. I can't figure out what is causing the huge difference in RAM usage and the execution time. What can cause this difference and what can I do to fix it?

EDIT: To be complete, this is how fast the compression works on the galaxy S3:

02-04 15:40:47.980: D/MyTag(25536): Saving:      6 ms, Rotation calculated
02-04 15:40:47.980: D/MyTag(25536): Saving:      53 ms, Decoded ByteArray to Bitmap
02-04 15:40:47.980: D/MyTag(25536): Saving:      94 ms, Bitmap rotated
02-04 15:40:47.980: D/MyTag(25536): Saving:      19 ms, Bitmap watermarked
02-04 15:40:47.980: D/MyTag(25536): Saving:      210 ms, Bitmap compressed
02-04 15:40:47.980: D/MyTag(25536): Saving:      0 ms, Done saving
02-04 15:40:47.980: D/MyTag(25536): Saving:      3 ms, Media scanned
02-04 15:40:47.980: D/MyTag(25536): Saving: end, 385 ms

When I take a picture with the front camera on the S3, the compression takes a lot longer (I really don't get why...):

02-04 20:24:31.245: D/MyTag(29496): Saving: begin
02-04 20:24:31.245: D/MyTag(29496): Saving:      2 ms, Rotation calculated
02-04 20:24:31.245: D/MyTag(29496): Saving:      95 ms, Decoded ByteArray to Bitmap
02-04 20:24:31.245: D/MyTag(29496): Saving:      160 ms, Bitmap rotated
02-04 20:24:31.245: D/MyTag(29496): Saving:      25 ms, Bitmap watermarked
02-04 20:24:31.245: D/MyTag(29496): Saving:      4548 ms, Bitmap compressed
02-04 20:24:31.245: D/MyTag(29496): Saving:      0 ms, Done saving
02-04 20:24:31.245: D/MyTag(29496): Saving:      3 ms, Media scanned
02-04 20:24:31.245: D/MyTag(29496): Saving: end, 4833 ms
AndroidAddict
  • 101
  • 1
  • 7
  • Have you tried running this _without_ the debugger attached, then attaching the debugger afterwards to check the logcat timestamp outputs? You may find a significant difference in performance (even 2 seconds is quite high to compress an image). That, of course, won't address the memory issues. That's a whole world of hurt - welcome! – Adam S Feb 04 '15 at 13:34
  • Note that you're telling the bitmap to compress itself at 100% quality :) You'll notice a sizable reduction in on-disk size (in-memory size stays the same regardless) even dropping down to 95%. – Adam S Feb 04 '15 at 13:35
  • Thank you for your comments. I read in the Android documentation that PNG compression is lossless, so it does not matter if I use 100% or 80% because it will always be a 100% compression. – AndroidAddict Feb 04 '15 at 14:14
  • I just tried it without debugger attached and it doesn't make a difference, still almost 30 seconds in total – AndroidAddict Feb 04 '15 at 14:15
  • Huh, I didn't realise that - guess I missed that in the docs! Sorry that the debugger thing didn't work, it's an experience I've had in the past. – Adam S Feb 04 '15 at 14:26
  • 1
    30 seconds on my Nexus 7 for an 8 mega pixel photo. Seems like a bug to me. Same photo on iOS compresses to PNG in less than a second on iPhone 6. – jjxtra Aug 24 '15 at 19:58

1 Answers1

5

I figured it out. It was a very stupid mistake I made. I am compressing to .PNG but saving as .JPEG, which apparently is a problem for some devices. The compression of a 3096x4128 image now takes 2 seconds, which I think is normal (at 100%).

Though this does not fix all the problems. The code still uses over 100 Mb on the S4, and still a OutOfMemory error is sometimes thrown. The code works perfectly on the S3.

AndroidAddict
  • 101
  • 1
  • 7