将字符串转换为字节会导致1024ram及更小的设备上出现java.lang.OutOfMemoryError

时间:2015-03-14 15:38:50

标签: 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,则不会发生错误。

这让我觉得阵列的大小对于ram来说是大的?那我该怎么做呢?

logcat

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)

1 个答案:

答案 0 :(得分:0)

当旧手机的getBytes处理时,该字符串使用了大量内存。所以我决定将字符串子串到4个字符串,而不是将每个字符串写入一个循环到输出流。

如果有人提供更好(更短或更清晰或更快)的代码片段来解决这个问题,那么我会将其标记为答案。直到,我的代码才会这样做!

写作方法:

    //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;
}