7.1 协程泄漏检测与预防
7.1.1 泄漏场景分析
常见泄漏模式:
泄漏代码示例:
kotlin
class LeakyActivity : AppCompatActivity() {
private val scope = CoroutineScope(Dispatchers.Main)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 泄漏:协程持有Activity引用且未取消
scope.launch {
// 长时间运行操作
delay(Long.MAX_VALUE)
// 访问Activity成员
textView.text = "Completed"
}
}
// 缺少取消逻辑
}
7.1.2 自动化检测工具
Android Studio Profiler集成:
- 启动内存分析器
- 执行操作后触发GC
- 检查Activity/Fragment实例残留
- 查看协程引用链
自定义泄漏检测器:
kotlin
object CoroutineLeakDetector {
private val trackedJobs = ConcurrentHashMap<Job, Throwable>()
private val logger: (String) -> Unit = { Log.e("LeakDetector", it) }
fun track(job: Job) {
trackedJobs[job] = Throwable("Job created here")
}
fun checkLeaks() {
trackedJobs.forEach { (job, stack) ->
if (job.isActive) {
logger("Potential leak: ${job}\n${stack.stackTraceToString()}")
}
}
trackedJobs.clear()
}
}
// 在BaseActivity中使用
abstract class BaseActivity : AppCompatActivity() {
private val jobs = mutableListOf<Job>()
protected fun launchSafely(block: suspend CoroutineScope.() -> Unit): Job {
val job = lifecycleScope.launch(block = block)
CoroutineLeakDetector.track(job)
jobs.add(job)
return job
}
override fun onDestroy() {
super.onDestroy()
jobs.forEach { it.cancel() }
CoroutineLeakDetector.checkLeaks()
}
}
7.1.3 预防策略
结构化并发最佳实践:
kotlin
// 正确做法:使用lifecycleScope
class SafeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 自动绑定生命周期
lifecycleScope.launch {
try {
val data = withContext(Dispatchers.IO) {
fetchData()
}
updateUI(data)
} catch (e: CancellationException) {
// 正常取消
}
}
}
}
// ViewModel中正确使用
class SafeViewModel : ViewModel() {
init {
viewModelScope.launch {
// 自动绑定ViewModel生命周期
loadData()
}
}
}
7.2 性能监控与分析工具
7.2.1 协程调试模式
启用调试:
kotlin
// 在Application中启用
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
System.setProperty("kotlinx.coroutines.debug", "on")
}
}
// 协程命名
viewModelScope.launch(CoroutineName("UserDataLoader")) {
// 在日志中显示协程名
Log.d("Coroutine", "Loading in ${coroutineContext[CoroutineName]?.name}")
}
日志输出示例:
perl
D/Coroutine: Loading in UserDataLoader @coroutine#1
7.2.2 Android Profiler集成
协程监控流程:
-
启动Android Profiler
-
选择应用进程
-
进入CPU分析器
-
启用"Kotlin协程跟踪"
-
记录操作并分析:
- 协程创建/销毁数量
- 线程切换频率
- 挂起时间占比
7.2.3 自定义性能监控器
kotlin
class CoroutineProfiler {
private data class TaskRecord(
val name: String,
val startTime: Long,
var endTime: Long = 0,
var thread: String = ""
)
private val records = ConcurrentHashMap<Long, TaskRecord>()
private val lock = Mutex()
suspend fun <T> measure(
name: String,
block: suspend () -> T
): T {
val start = System.nanoTime()
val threadBefore = Thread.currentThread().name
val coroutineId = coroutineContext[CoroutineId]?.id ?: 0
try {
return block().also {
withContext(lock) {
val record = records[coroutineId]
record?.endTime = System.nanoTime()
record?.thread = Thread.currentThread().name
}
}
} finally {
withContext(lock) {
records[coroutineId] = TaskRecord(
name = name,
startTime = start,
thread = threadBefore
)
}
}
}
fun generateReport() {
records.values.forEach { record ->
val durationMs = (record.endTime - record.startTime) / 1_000_000.0
Log.d("Profiler",
"${record.name}: ${"%.2f".format(durationMs)}ms " +
"[${record.thread} -> ${record.thread}]"
)
}
}
}
// 使用
val profiler = CoroutineProfiler()
viewModelScope.launch {
profiler.measure("UserDataLoad") {
repository.loadUserData()
}
profiler.measure("DataProcessing") {
processData()
}
profiler.generateReport()
}
7.3 调试技巧与实践
7.3.1 挂起函数调试
调试技巧:
-
在挂起点设置断点
-
启用"Suspend: All"选项
-
检查协程状态机:
label
字段表示当前状态result
字段存储中间结果completion
字段指向续体
7.3.2 协程堆栈分析
获取协程堆栈:
kotlin
fun printCoroutineStack() {
val stack = Thread.currentThread().stackTrace.joinToString("\n")
Log.d("CoroutineStack", "Current stack:\n$stack")
}
// 在协程中使用
viewModelScope.launch {
printCoroutineStack()
}
分析工具集成:
rust
// 自动记录未捕获异常堆栈
CoroutineExceptionHandler { _, throwable ->
Log.e("CoroutineError", "Unhandled exception", throwable)
// 保存堆栈到文件
saveStackTrace(throwable)
}
7.4 性能优化策略
7.4.1 调度器优化
自定义线程池:
scss
// 针对不同任务类型优化
val dbDispatcher = Executors.newFixedThreadPool(2).asCoroutineDispatcher()
val networkDispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()
val imageProcessingDispatcher = Dispatchers.Default.limitedParallelism(2)
// 使用
suspend fun loadUserData() {
// 数据库操作
val user = withContext(dbDispatcher) {
userDao.getUser()
}
// 网络请求
val details = withContext(networkDispatcher) {
api.getUserDetails(user.id)
}
// 图片处理
val processedAvatar = withContext(imageProcessingDispatcher) {
processAvatar(details.avatar)
}
}
7.4.2 减少上下文切换
优化前:
javascript
suspend fun processData() {
withContext(Dispatchers.IO) { /* 步骤1 */ }
withContext(Dispatchers.Main) { /* 更新UI */ }
withContext(Dispatchers.Default) { /* 步骤2 */ }
withContext(Dispatchers.Main) { /* 更新UI */ }
}
优化后:
scss
suspend fun processDataOptimized() {
// 在后台线程完成所有处理
val (result1, result2) = withContext(Dispatchers.Default) {
val res1 = step1()
val res2 = step2(res1)
res1 to res2
}
// 一次性更新UI
withContext(Dispatchers.Main) {
updateUI(result1, result2)
}
}
7.4.3 批处理优化
kotlin
// 数据库批处理
suspend fun batchInsertUsers(users: List<User>) {
withContext(dbDispatcher) {
userDao.runInTransaction {
users.chunked(50) { chunk ->
userDao.insertAll(chunk)
}
}
}
}
// 网络请求批处理
suspend fun batchFetchDetails(ids: List<Int>): Map<Int, UserDetails> {
return ids.chunked(20).flatMap { chunk ->
api.getBulkDetails(chunk)
}.associateBy { it.id }
}
7.5 内存优化
7.5.1 大对象处理
流式处理大文件:
kotlin
suspend fun processLargeFile(file: File) {
file.bufferedReader().use { reader ->
var line: String?
while (reader.readLine().also { line = it } != null) {
// 处理单行
processLine(line!!)
// 定期挂起避免阻塞
if (lineCounter % 1000 == 0) {
yield()
}
}
}
}
7.5.2 缓存管理
kotlin
// 带内存管理的缓存
class ManagedCache<T>(
private val loader: suspend (key: String) -> T,
private val maxSize: Int = 50
) {
private val cache = LinkedHashMap<String, T>(maxSize, 0.75f, true)
private val lock = Mutex()
suspend fun get(key: String): T {
return lock.withLock {
cache[key]?.let { return it }
}
// 缓存未命中,加载数据
val value = loader(key)
lock.withLock {
// 再次检查防止竞态
cache[key]?.let { return it }
// 添加新值
cache[key] = value
// 清理最旧条目
if (cache.size > maxSize) {
val oldestKey = cache.keys.first()
cache.remove(oldestKey)
}
return value
}
}
}
7.6 多线程协同优化
7.6.1 避免阻塞调用
使用BlockHound检测:
arduino
// build.gradle
dependencies {
implementation 'io.projectreactor.tools:blockhound:1.0.8.RELEASE'
}
kotlin
// Application中安装
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
BlockHound.builder()
.allowBlockingCallsInside("java.io.FileInputStream", "read")
.install()
}
}
7.6.2 并发控制优化
Semaphore替代方案:
kotlin
// 使用Mutex限制并行度
class MutexLimitedDispatcher(
private val dispatcher: CoroutineDispatcher,
private val parallelism: Int
) : CoroutineDispatcher() {
private val mutex = Semaphore(parallelism)
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatcher.dispatch(context, Runnable {
mutex.withPermit {
block.run()
}
})
}
}
// 使用
val limitedDispatcher = MutexLimitedDispatcher(Dispatchers.IO, 4)
7.7 资源清理策略
7.7.1 安全资源释放
kotlin
suspend fun processWithResources() {
val resource = openResource()
try {
useResource(resource)
} finally {
// 确保在取消时也能清理
withContext(NonCancellable) {
closeResource(resource)
}
}
}
7.7.2 协程作用域资源管理
kotlin
class ResourceScope : CoroutineScope {
private val job = SupervisorJob()
override val coroutineContext = job + Dispatchers.Default
private val resources = mutableListOf<Closeable>()
fun <T: Closeable> register(resource: T): T {
resources.add(resource)
return resource
}
fun close() {
job.cancel()
resources.forEach {
try {
it.close()
} catch (e: Exception) {
// 记录错误
}
}
resources.clear()
}
}
// 使用
val resourceScope = ResourceScope()
resourceScope.launch {
val file = resourceScope.register(FileOutputStream("data.bin"))
// 使用文件...
}
// 完成后清理
resourceScope.close()
本章小结
本章全面探讨了协程调试与性能优化的关键技术:
- 泄漏检测:自动化工具与自定义监控实现
- 性能监控:Profiler集成与自定义性能分析器
- 调试技巧:挂起函数调试与堆栈分析
- 性能优化:调度器优化、批处理与内存管理
- 多线程协同:阻塞检测与并发控制
- 资源清理:安全释放与作用域管理