feat: 优化代码

pull/198/head
kunfei 5 years ago
parent 15c87eb6fa
commit d830dab1d2
  1. 161
      app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt
  2. 2
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  3. 2
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt

@ -13,34 +13,43 @@ import java.nio.charset.Charset
import java.util.regex.Matcher import java.util.regex.Matcher
import java.util.regex.Pattern import java.util.regex.Pattern
object AnalyzeTxtFile { class AnalyzeTxtFile {
private const val folderName = "bookTxt"
private const val BLANK: Byte = 0x0a private val tocRules = arrayListOf<TxtTocRule>()
private lateinit var charset: Charset
//默认从文件中获取数据的长度
private const val BUFFER_SIZE = 512 * 1024
//没有标题的时候,每个章节的最大长度
private const val MAX_LENGTH_WITH_NO_CHAPTER = 10 * 1024
val cacheFolder: File by lazy {
val rootFile = App.INSTANCE.getExternalFilesDir(null)
?: App.INSTANCE.externalCacheDir
?: App.INSTANCE.cacheDir
FileUtils.createFolderIfNotExist(rootFile, subDirs = *arrayOf(folderName))
}
fun analyze(context: Context, book: Book): ArrayList<BookChapter> { fun analyze(context: Context, book: Book): ArrayList<BookChapter> {
val bookFile = getBookFile(context, book) val bookFile = getBookFile(context, book)
book.charset = EncodingDetect.getEncode(bookFile) book.charset = EncodingDetect.getEncode(bookFile)
val charset = book.fileCharset() charset = book.fileCharset()
val toc = arrayListOf<BookChapter>() val rulePattern = if (book.tocUrl.isNotEmpty()) {
Pattern.compile(book.tocUrl, Pattern.MULTILINE)
} else {
tocRules.addAll(getTocRules())
null
}
//获取文件流 //获取文件流
val bookStream = RandomAccessFile(bookFile, "r") val bookStream = RandomAccessFile(bookFile, "r")
val rulePattern = getTocRule(book, bookStream, charset) return analyze(bookStream, book, rulePattern)
}
private fun analyze(
bookStream: RandomAccessFile,
book: Book,
pattern: Pattern?
): ArrayList<BookChapter> {
val toc = arrayListOf<BookChapter>()
var tocRule: TxtTocRule? = null
val rulePattern = pattern ?: let {
tocRule = getTocRule(bookStream)
tocRule?.let {
Pattern.compile(it.rule, Pattern.MULTILINE)
}
}
//加载章节 //加载章节
val buffer = ByteArray(BUFFER_SIZE) val buffer = ByteArray(BUFFER_SIZE)
//获取到的块起始点,在文件中的位置 //获取到的块起始点,在文件中的位置
bookStream.seek(0)
var curOffset: Long = 0 var curOffset: Long = 0
//block的个数 //block的个数
var blockPos = 0 var blockPos = 0
@ -70,6 +79,10 @@ object AnalyzeTxtFile {
val chapterStart = matcher.start() val chapterStart = matcher.start()
//获取章节内容 //获取章节内容
val chapterContent = blockContent.substring(seekPos, chapterStart) val chapterContent = blockContent.substring(seekPos, chapterStart)
if (chapterContent.length > 30000 && pattern == null) {
tocRules.remove(tocRule)
return analyze(bookStream, book, null)
}
//如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容 //如果 seekPos == 0 && nextChapterPos != 0 表示当前block处前面有一段内容
//第一种情况一定是序章 第二种情况可能是上一个章节的内容 //第一种情况一定是序章 第二种情况可能是上一个章节的内容
if (seekPos == 0 && chapterStart != 0) { //获取当前章节的内容 if (seekPos == 0 && chapterStart != 0) { //获取当前章节的内容
@ -161,41 +174,14 @@ object AnalyzeTxtFile {
System.gc() System.gc()
System.runFinalization() System.runFinalization()
return toc tocRule?.let {
} book.tocUrl = it.rule
fun getContent(book: Book, bookChapter: BookChapter): String {
val bookFile = getBookFile(App.INSTANCE, book)
//获取文件流
val bookStream = RandomAccessFile(bookFile, "r")
bookStream.seek(bookChapter.start ?: 0)
val extent = (bookChapter.end!! - bookChapter.start!!).toInt()
val content = ByteArray(extent)
bookStream.read(content, 0, extent)
return String(content, book.fileCharset())
}
private fun getBookFile(context: Context, book: Book): File {
if (book.bookUrl.isContentPath()) {
val uri = Uri.parse(book.bookUrl)
val bookFile = FileUtils.getFile(cacheFolder, book.originName, subDirs = *arrayOf())
if (!bookFile.exists()) {
bookFile.createNewFile()
DocumentUtils.readBytes(context, uri)?.let {
bookFile.writeBytes(it)
}
}
return bookFile
} }
return File(book.bookUrl) return toc
} }
private fun getTocRule(book: Book, bookStream: RandomAccessFile, charset: Charset): Pattern? { private fun getTocRule(bookStream: RandomAccessFile): TxtTocRule? {
if (book.tocUrl.isNotEmpty()) { var txtTocRule: TxtTocRule? = null
return Pattern.compile(book.tocUrl, Pattern.MULTILINE)
}
val tocRules = getTocRules()
var rulePattern: Pattern? = null
//首先获取128k的数据 //首先获取128k的数据
val buffer = ByteArray(BUFFER_SIZE / 4) val buffer = ByteArray(BUFFER_SIZE / 4)
val length = bookStream.read(buffer, 0, buffer.size) val length = bookStream.read(buffer, 0, buffer.size)
@ -204,30 +190,73 @@ object AnalyzeTxtFile {
val pattern = Pattern.compile(tocRule.rule, Pattern.MULTILINE) val pattern = Pattern.compile(tocRule.rule, Pattern.MULTILINE)
val matcher = pattern.matcher(content) val matcher = pattern.matcher(content)
if (matcher.find()) { if (matcher.find()) {
book.tocUrl = tocRule.rule txtTocRule = tocRule
rulePattern = pattern
break break
} }
} }
bookStream.seek(0)
return rulePattern return txtTocRule
} }
private fun getTocRules(): List<TxtTocRule> { companion object {
val rules = App.db.txtTocRule().all private const val folderName = "bookTxt"
if (rules.isEmpty()) { private const val BLANK: Byte = 0x0a
return getDefaultRules()
//默认从文件中获取数据的长度
private const val BUFFER_SIZE = 512 * 1024
//没有标题的时候,每个章节的最大长度
private const val MAX_LENGTH_WITH_NO_CHAPTER = 10 * 1024
val cacheFolder: File by lazy {
val rootFile = App.INSTANCE.getExternalFilesDir(null)
?: App.INSTANCE.externalCacheDir
?: App.INSTANCE.cacheDir
FileUtils.createFolderIfNotExist(rootFile, subDirs = *arrayOf(folderName))
} }
return rules
}
fun getDefaultRules(): List<TxtTocRule> { fun getContent(book: Book, bookChapter: BookChapter): String {
App.INSTANCE.assets.open("txtTocRule.json").readBytes().let { byteArray -> val bookFile = getBookFile(App.INSTANCE, book)
GSON.fromJsonArray<TxtTocRule>(String(byteArray))?.let { //获取文件流
App.db.txtTocRule().insert(*it.toTypedArray()) val bookStream = RandomAccessFile(bookFile, "r")
return it bookStream.seek(bookChapter.start ?: 0)
val extent = (bookChapter.end!! - bookChapter.start!!).toInt()
val content = ByteArray(extent)
bookStream.read(content, 0, extent)
return String(content, book.fileCharset())
}
private fun getBookFile(context: Context, book: Book): File {
if (book.bookUrl.isContentPath()) {
val uri = Uri.parse(book.bookUrl)
val bookFile = FileUtils.getFile(cacheFolder, book.originName, subDirs = *arrayOf())
if (!bookFile.exists()) {
bookFile.createNewFile()
DocumentUtils.readBytes(context, uri)?.let {
bookFile.writeBytes(it)
}
}
return bookFile
} }
return File(book.bookUrl)
}
private fun getTocRules(): List<TxtTocRule> {
val rules = App.db.txtTocRule().all
if (rules.isEmpty()) {
return getDefaultRules()
}
return rules
}
fun getDefaultRules(): List<TxtTocRule> {
App.INSTANCE.assets.open("txtTocRule.json").readBytes().let { byteArray ->
GSON.fromJsonArray<TxtTocRule>(String(byteArray))?.let {
App.db.txtTocRule().insert(*it.toTypedArray())
return it
}
}
return emptyList()
} }
return emptyList()
} }
} }

@ -81,7 +81,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
) { ) {
execute { execute {
if (book.isLocalBook()) { if (book.isLocalBook()) {
AnalyzeTxtFile.analyze(context, book).let { AnalyzeTxtFile().analyze(context, book).let {
App.db.bookDao().update(book) App.db.bookDao().update(book)
App.db.bookChapterDao().insert(*it.toTypedArray()) App.db.bookChapterDao().insert(*it.toTypedArray())
chapterListData.postValue(it) chapterListData.postValue(it)

@ -102,7 +102,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
) { ) {
execute { execute {
if (book.isLocalBook()) { if (book.isLocalBook()) {
AnalyzeTxtFile.analyze(context, book).let { AnalyzeTxtFile().analyze(context, book).let {
App.db.bookChapterDao().delByBook(book.bookUrl) App.db.bookChapterDao().delByBook(book.bookUrl)
App.db.bookChapterDao().insert(*it.toTypedArray()) App.db.bookChapterDao().insert(*it.toTypedArray())
App.db.bookDao().update(book) App.db.bookDao().update(book)

Loading…
Cancel
Save