Android Kotlin转换大XML文件为字节数组时OOM的解决咨询
解决大XML文件转字节数组时的OutOfMemoryError问题
问题背景
尝试将XML文件转换为字节数组时抛出OutOfMemoryError,已尝试增大bufferSize但无效,相关代码及报错信息如下:
转换函数代码
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() }
报错堆栈
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)
问题原因
报错显示需要分配134MB内存,但当前应用可用内存不足。你的代码本质是把整个文件内容加载到内存中生成字节数组,当文件过大时必然触发OOM,调整bufferSize无法解决——bufferSize只是读写的块大小,最终还是要把所有数据存到内存里。
解决方案
1. 避免全量加载:直接用流处理(优先推荐)
如果你的需求是将文件内容写入SD卡(从报错里的sendXMLToSDCard方法推测),可以直接用输入流对接输出流,跳过内存中转,完全避免OOM:
private fun sendFileToSDCard(sourceFile: File, destPath: String) { val destFile = File(destPath) BufferedInputStream(FileInputStream(sourceFile)).use { input -> BufferedOutputStream(FileOutputStream(destFile)).use { output -> val buffer = ByteArray(1024 * 8) // 可适当调大缓冲区提升效率 var bytesRead: Int while (input.read(buffer).also { bytesRead = it } != -1) { output.write(buffer, 0, bytesRead) } output.flush() } } }
这里用了Kotlin的use函数,会自动关闭流,无需手动处理finally。
2. 必须生成字节数组时的替代方案
如果业务逻辑确实需要完整的字节数组,可尝试以下方法:
- 提前检查文件大小:获取文件长度
file.length(),如果超过应用可用内存阈值(比如当前可用内存的80%),直接抛出提示或分块处理。 - 使用内存映射文件:通过NIO的
MappedByteBuffer将文件映射到虚拟内存,无需全量加载到堆内存:
private fun convertFileToMappedBytes(file: File): ByteBuffer { val channel = FileInputStream(file).channel return channel.map(FileChannel.MapMode.READ_ONLY, 0, file.length()) }
注意:MappedByteBuffer属于直接内存,不受堆内存限制,但使用后要确保通道关闭,避免内存泄漏。
3. 临时应急:增大应用堆内存(不推荐)
在AndroidManifest.xml的<application>标签中添加android:largeHeap="true",可让应用获得更大的堆内存,但这只是临时方案,无法从根本解决大文件内存问题,还可能掩盖其他内存泄漏问题。
内容的提问来源于stack exchange,提问作者Kader Sutlu
相关产品推荐
相关产品推荐

