读取多个资产时出现OutOfMemoryError

时间:2014-07-27 06:57:17

标签: java android file-io out-of-memory android-assets

我有一个读取资产文件并将其内容存储在数组中的函数。这是代码。

private List<String> infinitivesDB;
private List<String> formsDB;

private void getDB(String lang, String type)
    {
        while(true)
        {
        try {
            String infinitivesDB = getStringFromAssetFile(this, "inf.txt");
            this.infinitivesDB = new LinkedList(Arrays.asList(infinitivesDB.split("\n")));
            break; //would be needed if it was reachable
        } catch (IOException e) {           
            e.printStackTrace();
            }
    }
        while(true)
        {
        try {
            String formsDB = getStringFromAssetFile(this, "forms.txt");//EXCEPTION HERE?        
            this.formsDB = new LinkedList(Arrays.asList(formsDB.split("\n")));
            break; //would be needed if it was reachable
        } catch (IOException e) {           
            e.printStackTrace();
            }
    }
    }

文件&#34; inf.txt&#34;是58kb。文件&#34; forms.txt&#34;是3.5 Mb。

每当我尝试运行此代码时,它都会引发OutOfMemoryError。这是日志

07-27 02:40:13.399: E/AndroidRuntime(2017): FATAL EXCEPTION: main
07-27 02:40:13.399: E/AndroidRuntime(2017): Process: com.highstaker.formdictionary, PID: 2017
07-27 02:40:13.399: E/AndroidRuntime(2017): java.lang.OutOfMemoryError
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.expandBuffer(Scanner.java:2067)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.readMore(Scanner.java:2031)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.findDelimiterAfter(Scanner.java:2009)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.setTokenRegion(Scanner.java:1923)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.hasNext(Scanner.java:541)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.util.Scanner.hasNext(Scanner.java:519)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.convertStreamToString(MainActivity.java:411)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.getStringFromAssetFile(MainActivity.java:404)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.getDB(MainActivity.java:389)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.highstaker.formdictionary.MainActivity.onCreate(MainActivity.java:50)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.Activity.performCreate(Activity.java:5243)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2140)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2226)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.access$700(ActivityThread.java:135)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1397)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.os.Handler.dispatchMessage(Handler.java:102)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.os.Looper.loop(Looper.java:137)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at android.app.ActivityThread.main(ActivityThread.java:4998)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.lang.reflect.Method.invokeNative(Native Method)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at java.lang.reflect.Method.invoke(Method.java:515)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
07-27 02:40:13.399: E/AndroidRuntime(2017):     at dalvik.system.NativeStart.main(Native Method)
07-27 02:40:15.179: I/Process(2017): Sending signal. PID: 2017 SIG: 9

但是,如果我尝试只读取一个文件(通过注释掉一个while循环及其内容),无论我选择哪个文件 - 它都可以正常工作。如果我尝试阅读&#34; inf.txt&#34; (较小的)在两个循环中并存储在这些不同的数组中,它也可以正常工作。

此外,如果我只是简单地切换&#34; whiles&#34;因此,在较小的文件之前读取较大的文件,它也可以工作!

我想知道为什么会这样。另外,如果两个资产文件都很大,我该怎么读?我试着在两个循环中读取&#34; forms.txt&#34;(更大)并且它引发了同样的错误(我真的想到了)。

2 个答案:

答案 0 :(得分:0)

正在引发错误因为您尝试从3.5 MB的文件中加载大量数据。为此目的的快速解决方案是

你可以使用android:largeHeap =&#34; true&#34;请求更大的堆大小,但这不适用于任何预装的Honeycomb设备。在2.3之前的设备上,您可以使用VMRuntime类,但这不适用于Gingerbread及更高版本。

尽可能增加限制的唯一方法是通过NDK执行内存密集型任务,因为NDK不会像SDK一样强加内存限制。

或者,您只能加载当前在视图中的模型部分,并根据需要加载其余部分,同时从内存中删除未使用的部分。但是,这可能无法实现,具体取决于您的应用程序。

在API Level 11+上运行的应用程序可以有android:largeHeap =&#34; true&#34;在清单中的元素上请求大于正常的堆大小,而ActivityManager上的getLargeMemoryClass()将告诉您该堆有多大。但是:

这仅适用于API级别11+(即Honeycomb及以上)

无法保证大堆的大小

用户将感知到您的大堆请求,因为它会强制其他应用程序从RAM中终止其他应用程序&#39;释放系统RAM以供大堆使用的进程

由于#3,以及我希望android:largeHeap被滥用的事实,将来可能会放弃对此的支持,或者可能会在安装时向用户发出警告(例如,您需要请求特别许可)

目前,此功能的记录很少

答案 1 :(得分:0)

我可以在这里看到几个糟糕的设计决定。最重要的是:如果你需要一个文件作为字符串列表,为什么不简单地阅读它呢?

使用扫描仪读取流,这本身并不是必需的。其他io类让你阅读没有这种复杂功能的行。

looong String被拆分成一个大数组,最终会生成列表。因此,您在内存中的文件至少一式三份。

LinedList有效增长,但你不需要这里。 ArrayList更节省空间。

必须重新设计getStringFromAssetFile。