对于一个Android App来说,应用卡顿是让人非常难受的,特别是在低端手机上,有一种想砸手机的冲动。动不动手机就黑屏,打开一个界面卡半天。
应用性能的重要性
应用性能优化对于软件开发和维护至关重要。包括以下几点:
1. 提升用户体验
- 用户更倾向于使用响应迅速、运行流畅的应用程序。性能差的应用可能会导致用户流失,特别是在竞争激烈的市场中。
- 如页面加载时间过长或操作延迟过高,会直接影响用户满意度。
2. 提高资源利用率
- 优化性能可以减少对计算资源(如 CPU、内存、带宽等)的消耗,从而降低服务器负载和运行成本。
- 更高效的代码和架构设计有助于更好地利用硬件资源,尤其是在云计算或多租户环境中。
3. 支持扩展性与稳定性
- 高性能应用通常具有更好的扩展能力,可以更轻松地处理用户增长或流量激增。
- 性能优化还能减少系统崩溃和宕机的风险,提高整体的稳定性和可靠性。
4. 增强竞争力
- 优化的应用在市场上更具竞争力,尤其是当用户的选择众多时。性能好的产品更容易赢得用户信任和忠诚度。
5. 降低长期维护成本
- 提前解决性能问题可以避免后期因扩展或问题积累带来的高昂维护费用。
- 通过优化代码和架构,可以使系统更易于调试和扩展。
6. 满足业务需求
- 某些业务场景(如实时数据处理、大型并发请求)对性能要求极高。优化可以确保应用满足这些业务需求。
dcache-android框架写了这么久,连个异步还要自己封装吗?
抱歉,让大家失望了,一直没有时间弄这个异步问题,之前都在修复基础功能的bug。最近板块轮动终于轮到这个项目的这个功能了,哈哈。数据ORM框架连个异步执行都不支持,难道还要别人每次调API还要自己开个线程吗?确实,对于ORM框架来说,性能优化是不可跨越的一关。传送门:github.com/dora4/dcach… 。
异步封装的主要类
dcache对异步操作的封装在dora.db.async包下。
kotlin 代码解读复制代码package dora.db.async
import android.os.Handler
import android.os.Looper
import android.os.Message
import dora.db.OrmLog
import dora.db.Transaction
import dora.db.builder.QueryBuilder
import dora.db.builder.WhereBuilder
import dora.db.exception.OrmTaskException
import dora.db.table.OrmTable
import java.util.concurrent.BlockingQueue
import java.util.concurrent.Callable
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
internal class OrmExecutor<T : OrmTable> : Runnable, Handler.Callback {
private val queue: BlockingQueue> = LinkedBlockingQueue()
private val lock = ReentrantLock()
private val condition = lock.newCondition()
@Volatile
private var executorRunning = false
@Volatile
var maxTaskCountToMerge: Int = 50
@Volatile
var listener: OrmTaskListener? = null
@Volatile
var listenerMainThread: OrmTaskListener? = null
@Volatile
var waitForMergeMillis: Int = 50
private var countTasksEnqueued = 0
private var countTasksCompleted = 0
private var handlerMainThread: Handler? = null
private var lastSequenceNumber = 0
private var stopQueue: Boolean = false
fun resetQueueStopFlag() {
stopQueue = false
}
private fun shouldStopQueue(task: OrmTask<T>): Boolean {
return (task.flags and OrmTask.FLAG_STOP_QUEUE_ON_EXCEPTION) != 0
}
fun enqueue(task: OrmTask<T>) {
synchronized(this) {
task.sequenceNumber = ++lastSequenceNumber
queue.add(task)
countTasksEnqueued++
if (!executorRunning) {
executorRunning = true
executor.execute(this)
}
}
}
@get:Synchronized
val isCompleted: Boolean
get() = lock.withLock {
countTasksEnqueued == countTasksCompleted
}
/**
* Waits until all enqueued operations are complete. If the thread gets interrupted, any
* [InterruptedException] will be rethrown as a [OrmTaskException].
* 简体中文:等待直到所有排队的操作完成。如果线程被中断,任何 [InterruptedException] 都会被重新抛出为
* [OrmTaskException]。
*/
@Synchronized
@Throws(OrmTaskException::class)
fun waitForCompletion() {
lock.lock()
try {
while (!isCompleted) {
try {
condition.await()
} catch (e: InterruptedException) {
throw OrmTaskException("Interrupted while waiting for all tasks to complete.\n$e", e)
}
}
} finally {
lock.unlock()
}
}
/**
* Waits until all enqueued operations are complete, but at most the given amount of milliseconds.
* If the thread gets interrupted, any [InterruptedException] will be rethrown as a [OrmTaskException].
* 简体中文:等待直到所有排队的操作完成,但最多等待指定的毫秒数。如果线程被中断,任何 [InterruptedException]
* 都会被重新抛出为 [OrmTaskException]。
*
* @return true if operations completed in the given time frame.
*/
@Synchronized
@Throws(OrmTaskException::class)
fun waitForCompletion(maxMillis: Int = 0): Boolean {
lock.withLock {
val deadline = System.currentTimeMillis() + maxMillis
while (!isCompleted) {
val remainingTime = deadline - System.currentTimeMillis()
if (remainingTime <= 0 || maxMillis == 0) {
break
}
try {
condition.await(remainingTime, TimeUnit.MILLISECONDS)
} catch (e: InterruptedException) {
throw OrmTaskException("Interrupted while waiting: ${e.message}", e)
}
}
return isCompleted
}
}
override fun run() {
try {
try {
while (true) {
var task = queue.poll(1, TimeUnit.SECONDS)
try {
if (task == null) {
synchronized(this) {
task = queue.poll()
if (task == null) {
executorRunning = false
return
}
}
}
if (task.isMergeTx) {
val task2 =
queue.poll(waitForMergeMillis.toLong(), TimeUnit.MILLISECONDS)
if (task2 != null) {
if (task.isMergeableWith(task2)) {
mergeTxAndExecute(task, task2)
} else {
executeTaskAndPostCompleted(task)
executeTaskAndPostCompleted(task2)
}
continue
}
}
executeTaskAndPostCompleted(task)
} catch (e: Exception) {
listener?.onFailed(task, e)
}
}
} catch (e: InterruptedException) {
OrmLog.w(Thread.currentThread().name + " was interrupted.\n" + e)
} catch (e: OrmTaskException) {
throw RuntimeException(e)
}
} finally {
executorRunning = false
}
}
@Throws(OrmTaskException::class)
private fun mergeTxAndExecute(task1: OrmTask<T>, task2: OrmTask<T>) {
val mergedTasks = ArrayList>()
mergedTasks.add(task1)
mergedTasks.add(task2)
var success = false
Transaction.execute {
for (i in mergedTasks.indices) {
val task = mergedTasks[i]
executeTask(task)
if (task.isFailed) {
break
}
if (i == mergedTasks.size - 1) {
val peekedTask = queue.peek()
if (i < maxTaskCountToMerge && task.isMergeableWith(peekedTask)) {
val removedTask = queue.remove()
if (removedTask !== peekedTask) {
throw OrmTaskException("Internal error: peeked task did not match removed task")
}
mergedTasks.add(removedTask)
} else {
success = true
break
}
}
}
}
if (success) {
val mergedCount = mergedTasks.size
for (task in mergedTasks) {
task.mergedTasksCount = mergedCount
handleTaskCompleted(task)
}
} else {
OrmLog.i(
"Reverted merged transaction because one of the tasks failed. Executing tasks one by " +
"one instead..."
)
for (task in mergedTasks) {
task.reset()
executeTaskAndPostCompleted(task)
}
}
}
private fun handleTaskCompleted(task: OrmTask<T>) {
task.setCompleted()
listener?.onCompleted(task)
listenerMainThread?.let {
getMainThreadHandler().obtainMessage(1, task).sendToTarget()
}
lock.withLock {
countTasksCompleted++
if (countTasksCompleted == countTasksEnqueued) {
condition.signalAll()
}
}
}
private fun getMainThreadHandler(): Handler {
return handlerMainThread ?: synchronized(this) {
handlerMainThread ?: Handler(Looper.getMainLooper(), this).also { handlerMainThread = it }
}
}
private fun executeTaskAndPostCompleted(task: OrmTask<T>) {
executeTask(task)
handleTaskCompleted(task)
}
private fun executeTask(task: OrmTask<T>) {
if (stopQueue) {
OrmLog.w("Task queue stopped due to previous exception.")
return
}
task.timeStarted = System.currentTimeMillis()
try {
when (task.type) {
OrmTask.Type.Insert -> task.result = task.dao.insert(task.parameter as T)
OrmTask.Type.InsertList -> task.result = task.dao.insert(task.parameter as List)
OrmTask.Type.Delete -> task.result = task.dao.delete((task.parameter as WhereBuilder))
OrmTask.Type.DeleteByKey -> task.result = task.dao.delete(task.parameter as T)
OrmTask.Type.DeleteAll -> task.result = task.dao.deleteAll()
OrmTask.Type.InsertOrReplace -> task.result = task.dao.insertOrUpdate(task.parameter as T)
OrmTask.Type.UpdateByKey -> task.result = task.dao.update(task.parameter as T)
OrmTask.Type.WhereList -> task.result =
task.dao.select((task.parameter as WhereBuilder))
OrmTask.Type.QueryList -> task.result =
task.dao.select((task.parameter as QueryBuilder))
OrmTask.Type.QueryAll -> task.result = task.dao.selectAll()
OrmTask.Type.WhereUnique -> task.result =
task.dao.selectOne((task.parameter as WhereBuilder))
OrmTask.Type.QueryUnique -> task.result =
task.dao.selectOne((task.parameter as QueryBuilder))
OrmTask.Type.IndexUnique -> task.result =
task.dao.selectOne()
OrmTask.Type.Count -> task.result = task.dao.count()
OrmTask.Type.WhereCount -> task.result =
task.dao.count((task.parameter as WhereBuilder))
OrmTask.Type.QueryCount -> task.result =
task.dao.count((task.parameter as QueryBuilder))
OrmTask.Type.AddColumn -> task.result =
task.dao.addColumn((task.parameter as String))
OrmTask.Type.RenameTable -> task.result =
task.dao.renameTable((task.parameter as String))
OrmTask.Type.Drop -> task.result =
task.dao.drop()
OrmTask.Type.WhereInsertOrReplace,
OrmTask.Type.WhereUpdate,
OrmTask.Type.RenameColumn -> {
task.result = (task.parameter as Callable<*>).call()
}
OrmTask.Type.TransactionRunnable -> executeTransactionRunnable(task)
OrmTask.Type.TransactionCallable -> executeTransactionCallable(task)
else -> throw OrmTaskException("Unsupported operation: " + task.type)
}
} catch (th: Throwable) {
task.throwable = th
if ((task.flags and OrmTask.FLAG_STOP_QUEUE_ON_EXCEPTION) != 0) {
stopQueue = true
}
task.creatorStacktrace?.let { stacktrace ->
OrmLog.e(
"Task failed: ${task.type}, Sequence: ${task.sequenceNumber}. Created at:"
+ stacktrace
)
}
} finally {
task.timeCompleted = System.currentTimeMillis()
}
}
private fun executeTransactionRunnable(task: OrmTask<T>) {
Transaction.execute {
(task.parameter as Runnable).run()
}
}
@Throws(Exception::class)
private fun executeTransactionCallable(task: OrmTask<T>) {
Transaction.execute {
task.result = (task.parameter as Callable<*>).call()
}
}
override fun handleMessage(msg: Message): Boolean {
val listenerToCall = listenerMainThread
listenerToCall?.onCompleted(msg.obj as OrmTask)
return false
}
companion object {
private val executor: ExecutorService = Executors.newCachedThreadPool()
}
}
OrmExecutor是对ORM线程池封装的核心类。里面用到了一些并发编程的东西,BlockingQueue(阻塞队列)、ExecutorService(线程池执行器)、ReentrantLock(重入锁)和Condition(条件变量)等。在run()方法中,我们可以看到有一个for循环,不断的从阻塞队列拿消息去执行。通过调用enqueue()方法,将新的ORM任务添加到线程池的阻塞队列。执行任务的过程中会不断去调用executeTask()方法,这里将所有的ORM操作都枚举出来了,如果dora.db.dao.Dao中的方法只有一个参数,则parameter就传入这个参数。如果不止一个参数,parameter就传入一个Callable,在Callable中自己去调用原来的同步ORM操作方法。最后在OrmTaskListener中的回调方法中调用task.result(clazz)方法获取返回值,可以是布尔值,也可以是查询出来的数据对象。
kotlin 代码解读复制代码package dora.db.async
import android.database.sqlite.SQLiteDatabase
import dora.db.dao.OrmDao
import dora.db.exception.OrmTaskException
import dora.db.table.OrmTable
import java.util.concurrent.TimeUnit
import java.util.concurrent.locks.ReentrantLock
open class OrmTask<T : OrmTable> internal constructor(
val type: Type, val dao: OrmDao,
val parameter: Any? = null, val flags: Int
) {
private val lock = ReentrantLock()
private val condition = lock.newCondition()
/**
* @see OrmExecutor.executeTask
*/
enum class Type {
Insert,
InsertList,
InsertOrReplace,
WhereInsertOrReplace,
Delete,
DeleteAll,
UpdateByKey,
WhereUpdate,
DeleteByKey,
WhereList,
WhereUnique,
QueryList,
QueryAll,
QueryUnique,
IndexUnique,
Count,
WhereCount,
QueryCount,
AddColumn,
RenameColumn,
RenameTable,
Drop,
TransactionRunnable,
TransactionCallable
}
@Volatile
var timeStarted: Long = 0
@Volatile
var timeCompleted: Long = 0
@Volatile
var isCompleted: Boolean = false
private set
@Volatile
var throwable: Throwable? = null
/**
* The stacktrace is captured using an exception if [.FLAG_TRACK_CREATOR_STACKTRACE] was used (null
* otherwise). 简体中文:如果使用了 [.FLAG_TRACK_CREATOR_STACKTRACE],则使用异常捕获堆栈跟踪(否则为 null)。
*/
val creatorStacktrace: Exception? = if ((flags and FLAG_TRACK_CREATOR_STACKTRACE) != 0) Exception("OrmTask was created here") else null
@Volatile
var result: Any? = null
/**
* If this operation was successfully merged with other operation into a single TX, this will give the count of
* merged operations. If the operation was not merged, it will be 0.
* 简体中文:如果此操作已成功与其他操作合并为一个事务(TX),则返回合并操作的数量。如果操作没有合并,则返回 0。
*/
@Volatile
var mergedTasksCount: Int = 0
/**
* Each operation get a unique sequence number when the operation is enqueued. Can be used for efficiently
* identifying/mapping operations. 简体中文:每个操作在排队时都会获得一个唯一的序列号。可以用于高效地识别/映射操作。
*/
var sequenceNumber: Int = 0
/**
* The operation's result after it has completed. Waits until a result is available.
* 简体中文:操作完成后返回结果。等待直到结果可用。
*
* @return The operation's result or null if the operation type does not produce any result.
* 简体中文:操作完成后的结果。等待直到结果可用。操作的结果,如果操作类型没有产生结果,则返回null。
* @throws [OrmTaskException]
* @see .waitForCompletion
*/
@Synchronized
@Throws(OrmTaskException::class)
fun result(): Any {
if (!isCompleted) {
waitForCompletion()
}
throwable?.let { throw OrmTaskException(this, it) }
// all orm operations have return value.
// 简体中文:所有ORM操作都有返回值
return result!!
}
/**
* The operation's result after it has completed. Waits until a result is available.
* 简体中文:操作完成后返回结果。等待直到结果可用。
*
* @return The operation's result or null if the operation type does not produce any result.
* 简体中文:操作完成后的结果。等待直到结果可用。操作的结果,如果操作类型没有产生结果,则返回null。
* @throws [OrmTaskException]
* @see .waitForCompletion
*/
@Synchronized
@Throws(OrmTaskException::class)
fun result(clazz: Class<R>): R {
if (!isCompleted) {
waitForCompletion()
}
if (throwable != null) {
throw OrmTaskException(this, throwable!!)
}
return result?.takeIf { clazz.isInstance(it) } as? R
?: throw OrmTaskException("The result type does not match the expected type: ${clazz.name}")
}
val isMergeTx: Boolean
get() = (flags and FLAG_MERGE_TX) != 0
fun getDatabase(): SQLiteDatabase {
return dao.getDB()
}
/**
* @return true if this operation is mergeable with the given operation. Checks for null,
* [.FLAG_MERGE_TX], and if the database instances match. 简体中文:判断此操作是否可以与指定的操作合并。
* 会检查 null、[.FLAG_MERGE_TX] 以及数据库实例是否匹配。
*/
fun isMergeableWith(other: OrmTask<T>?): Boolean {
return other != null && isMergeTx && other.isMergeTx && getDatabase() == other.getDatabase()
}
@get:Throws(OrmTaskException::class)
val duration: Long
get() {
if (timeCompleted == 0L) {
throw OrmTaskException("This operation did not yet complete")
} else {
return timeCompleted - timeStarted
}
}
val isFailed: Boolean
get() = throwable != null
/**
* Waits until the operation is complete. If the thread gets interrupted, any
* [InterruptedException] will be rethrown as a [OrmTaskException].
* 简体中文:等待操作完成。如果线程被中断,任何 [InterruptedException] 都将被重新抛出为 [OrmTaskException]。
*
* @return Result if any, see [.getResult] 简体中文:结果(如果有),请参阅 [.getResult]。
*/
@Synchronized
@Throws(OrmTaskException::class)
fun waitForCompletion(): Any? {
lock.lock()
try {
while (!isCompleted) {
try {
condition.await()
} catch (e: InterruptedException) {
throw OrmTaskException("Interrupted while waiting for operation to complete.\n${e.message}")
}
}
if (throwable != null) {
throw OrmTaskException(this, throwable!!)
}
return result
} finally {
lock.unlock()
}
}
/**
* Waits until the operation is complete, but at most the given amount of milliseconds.If the
* thread gets interrupted, any [InterruptedException] will be rethrown as a [OrmTaskException].
* 简体中文:等待操作完成,但最多等待指定的毫秒数。如果线程被中断,任何 [InterruptedException] 都会被重新抛出
* 为 [OrmTaskException]。
*
* @return true if the operation completed in the given time frame. 简体中文:如果操作在指定的时间范
* 围内完成,则返回 true。
*/
@Synchronized
@Throws(OrmTaskException::class)
fun waitForCompletion(maxMillis: Int): Boolean {
lock.lock()
try {
if (!isCompleted) {
try {
val completedInTime = condition.await(maxMillis.toLong(), TimeUnit.MILLISECONDS)
if (!completedInTime && !isCompleted) {
return false
}
} catch (e: InterruptedException) {
throw OrmTaskException("Interrupted while waiting for operation to complete.\n${e.message}")
}
}
return isCompleted
} finally {
lock.unlock()
}
}
/**
* Called when the operation is done. Notifies any threads waiting for this operation's completion.
* 简体中文:在操作完成时调用,通知所有等待该操作完成的线程。
*/
fun setCompleted() {
lock.lock() // 获取锁
try {
isCompleted = true
condition.signalAll()
} finally {
lock.unlock()
}
}
val isCompletedSuccessfully: Boolean
get() = isCompleted && throwable == null
/**
* Reset to prepare another execution run.
* 简体中文:重置以准备另一次执行运行。
*/
fun reset() {
timeStarted = 0
timeCompleted = 0
isCompleted = false
throwable = null
result = null
mergedTasksCount = 0
}
companion object {
const val FLAG_MERGE_TX: Int = 1
const val FLAG_STOP_QUEUE_ON_EXCEPTION: Int = 1 shl 1
const val FLAG_TRACK_CREATOR_STACKTRACE: Int = 1 shl 2
}
}
OrmTask重点讲一下flag,FLAG_TRACK_CREATOR_STACKTRACE为框架实现的默认标志位,即执行异步ORM操作的时候,如果抛出了异常,框架会try catch掉,并记录到OrmTaskException中,在OrmTaskListener的onFailed()回调方法中传递给调用层。OrmDao类中都是采用的这种实现方式,要不然onFailed()就没用了。FLAG_STOP_QUEUE_ON_EXCEPTION这个标志位保留,异常会直接让app崩溃。
dcache的异步ORM使用范例
kotlin 代码解读复制代码DaoFactory.getDao(Tutorial::class.java).selectOneAsync(object : OrmTaskListener {
override fun onCompleted(task: OrmTask<Tutorial>) {
runOnUiThread {
// 获取返回值
val tutorial = task.result(Tutorial::class.java)
tvTutorial.text = tutorial.content
}
}
override fun onFailed(task: OrmTask<Tutorial>, e: Exception) {
LogUtils.e(e.toString())
}
})
异步操作只要使用OrmDao中以Async结尾的方法即可。 另外github.com/dora4/DoraC… 可供参考。支持异步ORM操作的稳定版本为3.0.15,如果不需要异步功能,直接使用上一个大版本的稳定版本2.5.20就OK!
评论记录:
回复评论: