How to solve OutOfMemoryError in Android Kotlin when reading large file?

48 views Asked by At

When attempting to convert data obtained from an XML file, stored in a list, into a byte array, I encounter an error. Could someone provide guidance on how to successfully convert this data without encountering errors?

    private fun convertFileToByteArray(file: File): ByteArray {
        val inputStream = BufferedInputStream(FileInputStream(file))
        val buffer = ByteArrayOutputStream()
        val bufferSize = 1024
        val tempBuffer = ByteArray(bufferSize)
        var bytesRead: Int
        try {
            while (inputStream.read(tempBuffer, 0, bufferSize).also { bytesRead = it } != -1) {
                buffer.write(tempBuffer, 0, bytesRead)
            }
            buffer.flush()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            inputStream.close()
        }
        return buffer.toByteArray()
    }

error:

Process: com.kader.testclient, PID: 23504
java.lang.OutOfMemoryError: Failed to allocate a 134217744 byte allocation with 25149440 free bytes and 124MB until OOM, target footprint 95582488, growth limit 201326592
at java.util.Arrays.copyOf(Arrays.java:3161)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:153)
at com.kader.testclient.activities.DataManagerActivity.convertFileToByteArray(DataManagerActivity.kt:157)
at com.kader.testclient.activities.DataManagerActivity.sendXMLToSDCard(DataManagerActivity.kt:114)
at com.kader.testclient.activities.DataManagerActivity.onCreate$lambda$1(DataManagerActivity.kt:71)
at com.kader.testclient.activities.DataManagerActivity.$r8$lambda$kG6UQAIpGVwaMzA6MHqKPJlhIVs(Unknown Source:0)
at com.kader.testclient.activities.DataManagerActivity$$ExternalSyntheticLambda1.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7448)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1218)
at android.view.View.performClickInternal(View.java:7425)
at android.view.View.access$3600(View.java:810)
at android.view.View$PerformClick.run(View.java:28305)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

I increased the bufferSize size, but it didn't helped.

1

There are 1 answers

3
Rudolf On

You can use kotlin CoroutineDispatchers, to be more specific IO Dispatcher.

In general, I/O operations do not consume a lot of CPU resources but may take a long time to finish. Therefore, having access to the IO dispatcher’s large pool of threads can boost our application’s ability to finish I/O operations faster.

suspend fun convertFileToByteArray(file: File): ByteArray {

    return withContext(Dispatchers.IO) {
        val inputStream = BufferedInputStream(FileInputStream(file))
        val buffer = ByteArrayOutputStream()
        val bufferSize = 1024
        val tempBuffer = ByteArray(bufferSize)
        var bytesRead: Int
        try {
            while (inputStream.read(tempBuffer, 0, bufferSize).also { 
            bytesRead = it } != -1) {
            buffer.write(tempBuffer, 0, bytesRead)
        }
            buffer.flush()
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
        inputStream.close()
        }
        return@withContext buffer.toByteArray()
    }
}

Something like this