java - 在 1024ram 及以下的设备上,将字符串转换为字节会导致 java.lang.OutOfMemoryError

标签 java android out-of-memory

我刚刚在一些旧手机上测试了我的设备,并收到了 java.lang.OutOfMemoryError。有人可以帮我解决这个问题吗?

我想做什么: 将一个非常大的字符串转换为html文件,该字符串由html文本组成。 这是我的代码:

    OutputStream outputStream = null;
    InputStream inputStream = null;
    try {

        inputStream = new ByteArrayInputStream(htmlContent.getBytes(Charset.forName("UTF-16")));

        outputStream = new FileOutputStream(new File(dir, appBook.getPath()));

        byte[] bufferData = new byte[512];

        int bytesRead = inputStream.read(bufferData);

        while (bytesRead != -1) {
            outputStream.write(bufferData, 0, bytesRead); //add the bufferData data to the "new file"
            bytesRead = inputStream.read(bufferData); // keep on reading and filling the dynamic byte araay until it returns -1
        }

特别是 Charset.forName("UTF-16")) 导致这些旧设备上出现错误。如果我将 htmlContentString 缩短 2,则不会发生错误。

这让我觉得数组大小对于内存来说太大了?那么我应该如何处理这个问题呢?

日志猫

03-14 16:32:39.067    5604-5604/oskaro.synesthesia.oskar.leonad.synesthesiaconverter I/dalvikvm﹕ [ 03-14 16:32:39.067  5604: 5604 D/AndroidRuntime ]
    Shutting down VM
03-14 16:32:39.067    5604-5604/oskaro.synesthesia.oskar.leonad.synesthesiaconverter W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x40fde2a0)
03-14 16:32:39.077    5604-5604/oskaro.synesthesia.oskar.leonad.synesthesiaconverter E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.OutOfMemoryError
            at java.nio.charset.CharsetEncoderICU.getArray(CharsetEncoderICU.java:235)
            at java.nio.charset.CharsetEncoderICU.encodeLoop(CharsetEncoderICU.java:169)
            at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:415)
            at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:283)
            at java.nio.charset.Charset.encode(Charset.java:451)
            at java.lang.String.getBytes(String.java:870)
            at oskaro.synesthesia.oskar.leonad.synesthesiaconverter.HtmlAddDialog.createHtmlFile(HtmlAddDialog.java:120)
            at oskaro.synesthesia.oskar.leonad.synesthesiaconverter.HtmlAddDialog.access$700(HtmlAddDialog.java:39)
            at oskaro.synesthesia.oskar.leonad.synesthesiaconverter.HtmlAddDialog$4.onClick(HtmlAddDialog.java:209)
            at android.view.View.performClick(View.java:4232)
            at android.view.View$PerformClick.run(View.java:17298)
            at android.os.Handler.handleCallback(Handler.java:615)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4921)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)

最佳答案

当旧手机需要处理 getBytes 时,该字符串使用了太多内存。因此,我决定将字符串子串为 4 个字符串,而不只是将循环中的每个字符串写入输出流。

如果有人提供更好(更短、更干净或更快)的代码 fragment 来解决这个问题,我会将其标记为答案。直到那时,我的代码就可以了!

书写方法:

    //divide the string by 4 and read it in chunks. Otherwise
    // getBytes(Charset.forName("UTF-16") can cause out.of.memory if string is too big.
    //Important, i have to call this outside the try{} otherwise it will only do 3 loops inside splitString()?????
    String[] bookPieces = splitString(htmlContent, htmlContent.lenth()/650000);

    OutputStream outputStream = null;
    InputStream inputStream = null;
    try {
        outputStream = new FileOutputStream(new File(dir, appBook.getPath()));
        for (String text : bookPieces) {
            byte[] theBytes = text.getBytes(Charset.forName("UTF-16"));
            inputStream = new ByteArrayInputStream(theBytes);
            byte[] bufferData = new byte[1024];
            int bytesRead = inputStream.read(bufferData);

            while (bytesRead != -1) {
                outputStream.write(bufferData, 0, bytesRead); //add the bufferData data to the "new file"
                bytesRead = inputStream.read(bufferData); // keep on reading and filling the dynamic byte araay until it returns -1
            }
            //need to GC the inputsteam myself!!!!
            inputStream = null;
        }

辅助方法

private String[] splitString(String str, int elements) {
    String[] splitStrings = new String[elements];
    int currentLength = 0;
    double oneFourth = str.length() / elements;
    for (int x = 1; x <= splitStrings.length; x++) {

        //for the last text chunk add "remainders" which were rounded down when (int)
        if (x == splitStrings.length) {
            splitStrings[x-1] = str.substring(currentLength, str.length());
        } else {
            splitStrings[x-1] = str.substring(currentLength, ((int) oneFourth)*x);
        }
        currentLength += (int) oneFourth;
    }
    return splitStrings;
}

关于java - 在 1024ram 及以下的设备上,将字符串转换为字节会导致 java.lang.OutOfMemoryError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29050758/

相关文章:

Java:父类(super class)构造函数需要准备

安卓: "change/intercept network settings and traffic"权限

java - 太多方法引用 - 在 Android Studio 中使用 AppEngine SDK

java.lang.OutOfMemoryError - new int[Integer.MAX_VALUE];

c# - 用于字符串连接的 StringBuilder 抛出 OutOfMemoryException

java - 如何在onActivityResult中设置不同的请求码?

java - 如何通知 TreeModel 其底层模型已更改?

java - Eclipse Indigo - JPA 验证问题

java - 如何检查其他 Activity 的单选按钮

javax.servlet.ServletException : java. lang.OutOfMemoryError:Java堆空间