优化
This commit is contained in:
@@ -81,6 +81,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
} else {
|
||||
invalidate()
|
||||
}
|
||||
submitRenderTask()
|
||||
}
|
||||
|
||||
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
|
||||
@@ -216,7 +217,6 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
is TextColumn -> {
|
||||
if (!selectAble) return@touch
|
||||
column.selected = true
|
||||
invalidate()
|
||||
select(textPos)
|
||||
}
|
||||
}
|
||||
@@ -601,7 +601,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
|
||||
}
|
||||
}
|
||||
}
|
||||
invalidate()
|
||||
submitRenderTask()
|
||||
callBack.onCancelSelect()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ package io.legado.app.utils.canvasrecorder
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Picture
|
||||
import io.legado.app.utils.canvasrecorder.objectpool.synchronized
|
||||
import io.legado.app.utils.canvasrecorder.pools.PicturePool
|
||||
|
||||
class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
||||
|
||||
@@ -12,7 +14,7 @@ class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
||||
|
||||
private fun initPicture() {
|
||||
if (picture == null) {
|
||||
picture = Picture()
|
||||
picture = picturePool.obtain()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +35,13 @@ class CanvasRecorderApi23Impl : BaseCanvasRecorder() {
|
||||
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
if (picture == null) return
|
||||
picturePool.recycle(picture!!)
|
||||
picture = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val picturePool = PicturePool().synchronized()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,9 @@ import android.graphics.Picture
|
||||
import android.graphics.RenderNode
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import io.legado.app.utils.canvasrecorder.objectpool.synchronized
|
||||
import io.legado.app.utils.canvasrecorder.pools.PicturePool
|
||||
import io.legado.app.utils.canvasrecorder.pools.RenderNodePool
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
||||
@@ -17,10 +20,10 @@ class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
||||
|
||||
private fun init() {
|
||||
if (renderNode == null) {
|
||||
renderNode = RenderNode("CanvasRecorder")
|
||||
renderNode = renderNodePool.obtain()
|
||||
}
|
||||
if (picture == null) {
|
||||
picture = Picture()
|
||||
picture = picturePool.obtain()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,9 +59,16 @@ class CanvasRecorderApi29Impl : BaseCanvasRecorder() {
|
||||
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
renderNode?.discardDisplayList()
|
||||
if (renderNode == null || picture == null) return
|
||||
renderNodePool.recycle(renderNode!!)
|
||||
renderNode = null
|
||||
picturePool.recycle(picture!!)
|
||||
picture = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val picturePool = PicturePool().synchronized()
|
||||
private val renderNodePool = RenderNodePool().synchronized()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package io.legado.app.utils.canvasrecorder
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import io.legado.app.utils.canvasrecorder.pools.BitmapPool
|
||||
import io.legado.app.utils.canvasrecorder.pools.CanvasPool
|
||||
|
||||
class CanvasRecorderImpl : BaseCanvasRecorder() {
|
||||
|
||||
@@ -14,19 +16,15 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
||||
|
||||
private fun init(width: Int, height: Int) {
|
||||
if (bitmap == null) {
|
||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
if (canvas == null) {
|
||||
canvas = Canvas(bitmap!!)
|
||||
bitmap = bitmapPool.obtain(width, height)
|
||||
}
|
||||
if (bitmap!!.width != width || bitmap!!.height != height) {
|
||||
if (canReconfigure(width, height)) {
|
||||
bitmap!!.reconfigure(width, height, Bitmap.Config.ARGB_8888)
|
||||
} else {
|
||||
bitmap!!.recycle()
|
||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
bitmapPool.recycle(bitmap!!)
|
||||
bitmap = bitmapPool.obtain(width, height)
|
||||
}
|
||||
canvas!!.setBitmap(bitmap!!)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,12 +35,15 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
||||
override fun beginRecording(width: Int, height: Int): Canvas {
|
||||
init(width, height)
|
||||
bitmap!!.eraseColor(Color.TRANSPARENT)
|
||||
canvas = canvasPool.obtain().apply { setBitmap(bitmap!!) }
|
||||
return canvas!!
|
||||
}
|
||||
|
||||
override fun endRecording() {
|
||||
bitmap!!.prepareToDraw()
|
||||
super.endRecording()
|
||||
canvasPool.recycle(canvas!!)
|
||||
canvas = null
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
@@ -52,8 +53,14 @@ class CanvasRecorderImpl : BaseCanvasRecorder() {
|
||||
|
||||
override fun recycle() {
|
||||
super.recycle()
|
||||
bitmap?.recycle()
|
||||
bitmap = null
|
||||
val bitmap = bitmap ?: return
|
||||
bitmapPool.recycle(bitmap)
|
||||
this.bitmap = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val canvasPool = CanvasPool(2)
|
||||
private val bitmapPool = BitmapPool()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ class CanvasRecorderLocked(private val delegate: CanvasRecorder) :
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (lock == null) return
|
||||
if (!lock!!.tryLock()) return
|
||||
lock!!.lock()
|
||||
try {
|
||||
delegate.draw(canvas)
|
||||
} finally {
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.legado.app.utils.canvasrecorder.objectpool
|
||||
|
||||
import androidx.annotation.CallSuper
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.LinkedList
|
||||
|
||||
abstract class BaseObjectPool<T> : ObjectPool<T> {
|
||||
|
||||
private val pool = LinkedList<SoftReference<T>>()
|
||||
|
||||
override fun obtain(): T {
|
||||
while (true) {
|
||||
if (pool.isEmpty()) break
|
||||
return pool.poll()?.get() ?: continue
|
||||
}
|
||||
return create()
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
override fun recycle(target: T) {
|
||||
pool.add(SoftReference(target))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package io.legado.app.utils.canvasrecorder.objectpool
|
||||
|
||||
interface ObjectPool<T> {
|
||||
|
||||
fun obtain(): T
|
||||
|
||||
fun recycle(target: T)
|
||||
|
||||
fun create(): T
|
||||
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package io.legado.app.utils.canvasrecorder.objectpool
|
||||
|
||||
fun <T> ObjectPool<T>.synchronized(): ObjectPool<T> = ObjectPoolLocked(this)
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.legado.app.utils.canvasrecorder.objectpool
|
||||
|
||||
class ObjectPoolLocked<T>(private val delegate: ObjectPool<T>) : ObjectPool<T> by delegate {
|
||||
|
||||
@Synchronized
|
||||
override fun obtain(): T {
|
||||
return delegate.obtain()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun recycle(target: T) {
|
||||
return delegate.recycle(target)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package io.legado.app.utils.canvasrecorder.pools
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class BitmapPool {
|
||||
|
||||
private val reusableBitmaps: MutableSet<SoftReference<Bitmap>> = ConcurrentHashMap.newKeySet()
|
||||
|
||||
fun recycle(bitmap: Bitmap) {
|
||||
reusableBitmaps.add(SoftReference(bitmap))
|
||||
}
|
||||
|
||||
fun obtain(width: Int, height: Int): Bitmap {
|
||||
if (reusableBitmaps.isEmpty()) {
|
||||
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
val iterator = reusableBitmaps.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val item = iterator.next().get() ?: continue
|
||||
if (item.isMutable) {
|
||||
// Check to see it the item can be used for inBitmap.
|
||||
if (canReconfigure(item, width, height)) {
|
||||
// Remove from reusable set so it can't be used again.
|
||||
iterator.remove()
|
||||
item.reconfigure(width, height, Bitmap.Config.ARGB_8888)
|
||||
return item
|
||||
}
|
||||
} else {
|
||||
// Remove from the set if the reference has been cleared.
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
}
|
||||
|
||||
private fun canReconfigure(
|
||||
candidate: Bitmap,
|
||||
width: Int,
|
||||
height: Int
|
||||
): Boolean {
|
||||
// From Android 4.4 (KitKat) onward we can re-use if the byte size of
|
||||
// the new bitmap is smaller than the reusable bitmap candidate
|
||||
// allocation byte count.
|
||||
val byteCount: Int = width * height * getBytesPerPixel(candidate.config)
|
||||
return byteCount <= candidate.allocationByteCount
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A helper function to return the byte usage per pixel of a bitmap based on its configuration.
|
||||
*/
|
||||
private fun getBytesPerPixel(config: Bitmap.Config): Int {
|
||||
return when (config) {
|
||||
Bitmap.Config.ARGB_8888 -> 4
|
||||
Bitmap.Config.RGB_565, Bitmap.Config.ARGB_4444 -> 2
|
||||
Bitmap.Config.ALPHA_8 -> 1
|
||||
else -> 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.legado.app.utils.canvasrecorder.pools
|
||||
|
||||
import android.graphics.Canvas
|
||||
import androidx.core.util.Pools
|
||||
|
||||
class CanvasPool(size: Int) {
|
||||
|
||||
private val pool = Pools.SynchronizedPool<Canvas>(size)
|
||||
|
||||
fun obtain(): Canvas {
|
||||
val canvas = pool.acquire() ?: Canvas()
|
||||
return canvas
|
||||
}
|
||||
|
||||
fun recycle(canvas: Canvas) {
|
||||
canvas.setBitmap(null)
|
||||
canvas.restoreToCount(1)
|
||||
pool.release(canvas)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.legado.app.utils.canvasrecorder.pools
|
||||
|
||||
import android.graphics.Picture
|
||||
import io.legado.app.utils.canvasrecorder.objectpool.BaseObjectPool
|
||||
|
||||
class PicturePool : BaseObjectPool<Picture>() {
|
||||
|
||||
override fun create(): Picture = Picture()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.legado.app.utils.canvasrecorder.pools
|
||||
|
||||
import android.graphics.RenderNode
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import io.legado.app.utils.canvasrecorder.objectpool.BaseObjectPool
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
class RenderNodePool : BaseObjectPool<RenderNode>() {
|
||||
|
||||
override fun recycle(target: RenderNode) {
|
||||
target.discardDisplayList()
|
||||
super.recycle(target)
|
||||
}
|
||||
|
||||
override fun create(): RenderNode = RenderNode("CanvasRecorder")
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user