epub读取优化&Bitmap解码优化

导入epub优化,复制文件到缓存区域,使用ZipFile代替ZipInputStream,使得读取能懒加载epub,减少读取epub时的内存占用。
使用BitmapFactory.decodeStream代替BitmapFactory.decodeFile,来减少加载图片时产生OOM的可能性。
pull/957/head
ag2s20150909 4 years ago
parent a4ffeea3d5
commit 77a1dbe7f4
  1. 4
      app/src/main/java/io/legado/app/help/BookHelp.kt
  2. 71
      app/src/main/java/io/legado/app/model/localBook/EpubFile.kt
  3. 4
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ImageProvider.kt
  4. 18
      app/src/main/java/io/legado/app/utils/BitmapUtils.kt
  5. 38
      app/src/main/java/io/legado/app/utils/FileUtils.kt

@ -21,9 +21,9 @@ import kotlin.math.max
import kotlin.math.min import kotlin.math.min
object BookHelp { object BookHelp {
private const val cacheFolderName = "book_cache" const val cacheFolderName = "book_cache"
private const val cacheImageFolderName = "images" private const val cacheImageFolderName = "images"
private val downloadDir: File = appCtx.externalFilesDir val downloadDir: File = appCtx.externalFilesDir
private val downloadImages = CopyOnWriteArraySet<String>() private val downloadImages = CopyOnWriteArraySet<String>()
fun clearCache() { fun clearCache() {

@ -6,12 +6,10 @@ import android.net.Uri
import android.text.TextUtils import android.text.TextUtils
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.utils.* import io.legado.app.utils.*
import me.ag2s.epublib.domain.EpubBook import me.ag2s.epublib.domain.EpubBook
import me.ag2s.epublib.domain.MediaTypes
import me.ag2s.epublib.domain.Resources
import me.ag2s.epublib.epub.EpubReader import me.ag2s.epublib.epub.EpubReader
import me.ag2s.epublib.util.ResourceUtil
import org.jsoup.Jsoup import org.jsoup.Jsoup
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
@ -20,8 +18,7 @@ import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.nio.charset.Charset import java.nio.charset.Charset
import java.util.* import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
class EpubFile(var book: Book) { class EpubFile(var book: Book) {
@ -101,32 +98,46 @@ class EpubFile(var book: Book) {
/*重写epub文件解析代码,直接读出压缩包文件生成Resources给epublib,这样的好处是可以逐一修改某些文件的格式错误*/ /*重写epub文件解析代码,直接读出压缩包文件生成Resources给epublib,这样的好处是可以逐一修改某些文件的格式错误*/
private fun readEpub(): EpubBook? { private fun readEpub(): EpubBook? {
try { try {
val input = if (book.bookUrl.isContentScheme()) { //f
val uri = Uri.parse(book.bookUrl) val file = FileUtils.getFile(
appCtx.contentResolver.openInputStream(uri) BookHelp.downloadDir,
} else { BookHelp.cacheFolderName,
File(book.bookUrl).inputStream() book.getFolderName(),
} "index.epubx"
input ?: return null )
val inZip = ZipInputStream(input) if (!file.exists()) {
var zipEntry: ZipEntry? val input = if (book.bookUrl.isContentScheme()) {
val resources = Resources() val uri = Uri.parse(book.bookUrl)
do { appCtx.contentResolver.openInputStream(uri)
zipEntry = inZip.nextEntry } else {
if ((zipEntry == null) || zipEntry.isDirectory || zipEntry == ZipEntry("<error>")) continue File(book.bookUrl).inputStream()
val resource = ResourceUtil.createResource(zipEntry, inZip)
if (resource.mediaType == MediaTypes.XHTML) resource.inputEncoding = "UTF-8"
if (zipEntry.name.endsWith(".opf")) {
/*掌上书苑有很多自制书OPF的nameSpace格式不标准,强制修复成正确的格式*/
val newS = String(resource.data).replace(
"\\smlns=\"http://www.idpf.org/2007/opf\"".toRegex(),
" xmlns=\"http://www.idpf.org/2007/opf\""
)
resource.data = newS.toByteArray()
} }
resources.add(resource) input ?: return null
} while (zipEntry != null) FileUtils.writeInputStream(file, input)
if (resources.size() > 0) return EpubReader().readEpub(resources)
}
return EpubReader().readEpubLazy(ZipFile(file),"utf-8")
// val inZip = ZipInputStream(input)
// var zipEntry: ZipEntry?
// val resources = Resources()
// do {
// zipEntry = inZip.nextEntry
// if ((zipEntry == null) || zipEntry.isDirectory || zipEntry == ZipEntry("<error>")) continue
// val resource = ResourceUtil.createResource(zipEntry, inZip)
// if (resource.mediaType == MediaTypes.XHTML) resource.inputEncoding = "UTF-8"
// if (zipEntry.name.endsWith(".opf")) {
// /*掌上书苑有很多自制书OPF的nameSpace格式不标准,强制修复成正确的格式*/
// val newS = String(resource.data).replace(
// "\\smlns=\"http://www.idpf.org/2007/opf\"".toRegex(),
// " xmlns=\"http://www.idpf.org/2007/opf\""
// )
// resource.data = newS.toByteArray()
// }
// resources.add(resource)
// } while (zipEntry != null)
// if (resources.size() > 0) return EpubReader().readEpub(resources)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }

@ -54,7 +54,9 @@ object ImageProvider {
ChapterProvider.visibleWidth, ChapterProvider.visibleWidth,
ChapterProvider.visibleHeight ChapterProvider.visibleHeight
) )
setCache(chapterIndex, src, bitmap) if (bitmap != null) {
setCache(chapterIndex, src, bitmap)
}
bitmap bitmap
} catch (e: Exception) { } catch (e: Exception) {
null null

@ -9,6 +9,7 @@ import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur import android.renderscript.ScriptIntrinsicBlur
import android.view.View import android.view.View
import splitties.init.appCtx import splitties.init.appCtx
import java.io.FileInputStream
import java.io.IOException import java.io.IOException
import kotlin.math.* import kotlin.math.*
@ -25,11 +26,12 @@ object BitmapUtils {
* @param height 想要显示的图片的高度 * @param height 想要显示的图片的高度
* @return * @return
*/ */
fun decodeBitmap(path: String, width: Int, height: Int): Bitmap { fun decodeBitmap(path: String, width: Int, height: Int): Bitmap? {
val op = BitmapFactory.Options() val op = BitmapFactory.Options()
var ips=FileInputStream(path)
// inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight; // inJustDecodeBounds如果设置为true,仅仅返回图片实际的宽和高,宽和高是赋值给opts.outWidth,opts.outHeight;
op.inJustDecodeBounds = true op.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, op) //获取尺寸信息 BitmapFactory.decodeStream(ips,null,op)//获取尺寸信息
//获取比例大小 //获取比例大小
val wRatio = ceil((op.outWidth / width).toDouble()).toInt() val wRatio = ceil((op.outWidth / width).toDouble()).toInt()
val hRatio = ceil((op.outHeight / height).toDouble()).toInt() val hRatio = ceil((op.outHeight / height).toDouble()).toInt()
@ -41,22 +43,24 @@ object BitmapUtils {
op.inSampleSize = hRatio op.inSampleSize = hRatio
} }
} }
ips=FileInputStream(path)
op.inJustDecodeBounds = false op.inJustDecodeBounds = false
return BitmapFactory.decodeFile(path, op) return BitmapFactory.decodeStream(ips,null,op)
} }
/** 从path中获取Bitmap图片 /** 从path中获取Bitmap图片
* @param path 图片路径 * @param path 图片路径
* @return * @return
*/ */
fun decodeBitmap(path: String): Bitmap { fun decodeBitmap(path: String): Bitmap? {
val opts = BitmapFactory.Options() val opts = BitmapFactory.Options()
var ips=FileInputStream(path)
opts.inJustDecodeBounds = true opts.inJustDecodeBounds = true
BitmapFactory.decodeFile(path, opts) BitmapFactory.decodeStream(ips,null,opts)
opts.inSampleSize = computeSampleSize(opts, -1, 128 * 128) opts.inSampleSize = computeSampleSize(opts, -1, 128 * 128)
opts.inJustDecodeBounds = false opts.inJustDecodeBounds = false
ips=FileInputStream(path)
return BitmapFactory.decodeFile(path, opts) return BitmapFactory.decodeStream(ips,null,opts)
} }
/** /**

@ -517,6 +517,44 @@ object FileUtils {
closeSilently(fos) closeSilently(fos)
} }
} }
/**
* 保存文件内容
*/
fun writeInputStream(filepath: String, data: InputStream): Boolean {
val file = File(filepath)
return writeInputStream(file,data)
}
/**
* 保存文件内容
*/
fun writeInputStream(file: File, data: InputStream): Boolean {
var fos: FileOutputStream? = null
return try {
if (!file.exists()) {
file.parentFile?.mkdirs()
file.createNewFile()
}
val buffer=ByteArray(1024*4)
fos = FileOutputStream(file)
while (true) {
val len = data.read(buffer, 0, buffer.size)
if (len == -1) {
break
} else {
fos.write(buffer, 0, len)
}
}
data.close()
fos.flush()
true
} catch (e: IOException) {
false
} finally {
closeSilently(fos)
}
}
/** /**
* 追加文本内容 * 追加文本内容

Loading…
Cancel
Save