我正在为 Android 编写一个 Hangman 应用程序,但遇到了一些加载问题。当我的应用程序启动时,我会生成一个从 txt 文件中挑选的随机单词。问题是这个文件很重:它几乎有 360'000 个单词(4000 kB),因此选择一个单词需要 10 到 20 秒的时间,我知道这是因为我每次读取文件并选择一个单词我点击生成。
如果我在应用程序启动后读取文件并将其内容放入字符串数组中会是一件好事吗?我真的不知道数组是否可以处理这么多。否则什么是好的模式?
谢谢阅读。
将其存储为 CSV,(逗号分隔值)。打开一个读取流,寻找一个随机位置并读取一小块字符,直到你点击','
两次。现在,从你读过的字符串中提取逗号之间的那个词。您也可以使用自定义分隔符,例如“#”或“|” 而不是逗号。
更新:使用RandomAccessFile
.
Update2:readLine()
如果您每行存储一个单词,这会更容易。即由\n
您可以(并且可能最好)创建一个单词数据库并在其中查询随机单词。您可以从阅读在 SQL 数据库中保存数据开始
将整个文件读入字符串数组不是一个好策略,因为这会消耗大量内存。一个简单的解决方案是将您的大文件拆分为几个单独的文件(使用文件名的模式,如 word1.txt、words2.txt 等)。绘制单词时,您将首先选择文件,然后从文件中选择实际单词。
文件读取是一项昂贵的操作,花费这么长时间是正常的。您可能应该创建一组文件而不是一个文件,并且每次启动应用程序时,您都可以从中选择一个随机文件和一个随机单词。这应该会减少所需的时间。
此外,您可以将单词名称作为应用程序资源存储在 XML 文件中,该资源将进入 /res/values/ 文件夹。您可以创建字符串数组资源并轻松地从代码中获取它,而不必担心打开/关闭/读取文件。这非常简单。但是在这里将所有单词拆分为不同的 xml 文件(或至少是字符串数组组)对您来说再次很重要。在这里阅读更多。http://developer.android.com/guide/topics/resources/string-resource.html#StringArray
或者另一种方法是通过从文件中读取所有内容来将其存储到您的数据库中。这样,应用程序的首次启动只需要时间。您可以在使用时显示诸如“正在准备 DB...”之类的消息或类似的内容。
执行代码以在 AsyncTask 中解析/加载 json/db 格式的内容以提高速度。我加载 5000 行,每行约 400 个字符。没有 AsyncTask 需要更长的时间。
private class YourTask extends AsyncTask<String, Void, String> {
@Override
protected String doInBackground(String... s) {
//Here you have to make the loading / parsing tasks
//Don't call any UI actions here. For example a Toast.show() this will couse Exceptions
// UI stuff you have to make in onPostExecute method
}
@Override
protected void onPreExecute() {
// This method will called during doInBackground is in process
// Here you can for example show a ProgressDialog
}
@Override
protected void onPostExecute(Long result) {
// onPostExecute is called when doInBackground finished
// Here you can for example fill your Listview with the content loaded in doInBackground method
}
}
要执行你只需要调用:
new YourTask().execute("");
在这里您可以了解有关 AsyncTasks 的更多信息:
这里需要注意的一些事项:
如果您的应用程序从 SD 卡读取,SD 卡的质量实际上是一个因素 - 根据您使用的 SD 卡的类别,有不同的读/写速度 - 查一下。
您提到每次单击“生成”时都会在文件中随机选择一行 - 因为您可以确定用户将多次单击“生成”,所以读取该文件一次并将内容存储在某处是一个不错的主意( sqlite),下次需要获取随机词时,从存储的数据中获取。这样,您将从外部存储(在移动标准中,一项繁重的任务)中读取的次数限制为每次应用程序启动一次。
您甚至可以在文件上放置一个标记,让您知道内容是否已更改。从随机字符串到当前日期到 md5 校验和的任何内容都应该让您知道您不需要再次读取该文件,因为您之前已经阅读过它并将内容存储在您的 sqlite 数据库中。你想把它放在文件的第一行。
现在其他人都在告诉您考虑写入 sqlite db 甚至复制 .db 文件本身(您可以单独生成),选择一个随机项目将是微不足道的。我宁愿警告你在你的数据库中写入 360,000 个条目。使用默认的 ContentProviderinsert(Uri, ContentValues)
方法会起作用,但如果这比实际解析文件本身花费的时间更长,我不会感到惊讶。bulkInsert(Uri, ContentValues[])
这里的技巧是使用编译语句来覆盖 ContentProvider 的实现。默认实现仅insert
多次调用该方法,因此每次插入都会打开和关闭 SqliteDatabase 对象。你想要做的是beginTransaction
在顶部调用,有一个 for 循环,它insert
是 ContentValues 中的每个项目setTransactionSuccessful
,然后调用endTransaction
.