我刚刚在一些旧手机上测试了我的设备并获得了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)
答案 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;
}