1

I am trying to copy a file of about 80 megabytes from the assets folder of an Android application to the SD card.

The file is another apk. For various reasons I have to do it this way and can't simply link to an online apk or put it on the Android market.

The application works fine with smaller apks but for this large one I am getting an out of memory error.

I'm not sure exactly how this works but I am assuming that here I am trying to write the full 80 megabytes to memory.

try {
                int length = 0;
                newFile.createNewFile();

                InputStream inputStream = ctx.getAssets().open(
                        "myBigFile.apk");
                FileOutputStream fOutputStream = new FileOutputStream(
                        newFile);
                byte[] buffer = new byte[inputStream.available()];
                while ((length = inputStream.read(buffer)) > 0) {
                    fOutputStream.write(buffer, 0, length);
                }
                fOutputStream.flush();
                fOutputStream.close();
                inputStream.close();
            } catch (Exception ex) {
                if (ODP_App.getInstance().isInDebugMode())
                    Log.e(TAG, ex.toString());
            } 

I found this interesting - A question about an out of memory issue with Bitmaps

Unless I've misunderstood, in the case of Bitmaps, there appears to be some way to split the stream to reduce memory usage using BitmapFactory.Options.

Is this do-able in my scenario or is there any other possible solution?

Community
  • 1
  • 1
Ruairi O'Brien
  • 1,209
  • 5
  • 18
  • 33
  • 2
    maybe your buffer is too big (trying to read the whole file into memory)? I am guessing that `inputStream.available()` gets the whole size? Change this to 1024 (or something) – musefan Jan 11 '12 at 14:23
  • One way I see is instead of transferring entire APK as single stream, you may need to transfer each file in APK. AFAIK, apk is sort of ZIP, so you may need to do to some hacks around zip API and Stream API. – kosa Jan 11 '12 at 14:25
  • Hi musefan. You were right. inputStream.available() gets the amount in bytes of the file in the inputstream. I guess I should have read up on what that method does exactly before posting here. Thanks for your comment. Changing the buffer size worked. – Ruairi O'Brien Jan 12 '12 at 11:27

2 Answers2

7

The trick is not to try to read the whole file in one go, but rather read it in small chunks and write each chunk before reading the next one into the same memory segment. The following version will read it in 1K chunks. It's for example only - you need to determine the right chunk size.

try {
    int length = 0;
    newFile.createNewFile();

    InputStream inputStream = ctx.getAssets().open(
            "myBigFile.apk");
    FileOutputStream fOutputStream = new FileOutputStream(
            newFile);
    //note the following line
    byte[] buffer = new byte[1024];
    while ((length = inputStream.read(buffer)) > 0) {
        fOutputStream.write(buffer, 0, length);
    }
    fOutputStream.flush();
    fOutputStream.close();
    inputStream.close();
} catch (Exception ex) {
    if (ODP_App.getInstance().isInDebugMode())
        Log.e(TAG, ex.toString());
} 
Aleks G
  • 56,435
  • 29
  • 168
  • 265
2

Do not read the whole file into memory; read 64k at a time, then write them, repeat until you reach the end of file. Or use IOUtils from Apache Commons IO.

Tassos Bassoukos
  • 16,017
  • 2
  • 36
  • 40
  • Thank you for your answer. What you said is correct but I accepted Aleks G's answer because he provided a more detailed example. Thanks again. – Ruairi O'Brien Jan 11 '12 at 15:01