Toggle navigation
Toggle navigation
此项目
正在载入...
Sign in
xuning
/
livekitAndroidXuningTest
转到一个项目
Toggle navigation
项目
群组
代码片段
帮助
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
davidliu
2025-07-08 02:56:44 +0900
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Committed by
GitHub
2025-07-08 02:56:44 +0900
Commit
80b00a1bee0b2582fcd0d9f67e8416ba608c617e
80b00a1b
1 parent
6c239911
Move audio handling to background thread to avoid UI freezes. (#715)
隐藏空白字符变更
内嵌
并排对比
正在显示
3 个修改的文件
包含
38 行增加
和
12 行删除
.changeset/fresh-schools-chew.md
livekit-android-sdk/src/main/java/io/livekit/android/audio/AudioSwitchHandler.kt
livekit-android-sdk/src/main/java/io/livekit/android/audio/CommunicationWorkaround.kt
.changeset/fresh-schools-chew.md
0 → 100644
查看文件 @
80b00a1
---
"
client-sdk-android"
:
patch
---
Move audio handling to background thread to avoid UI freezes.
...
...
livekit-android-sdk/src/main/java/io/livekit/android/audio/AudioSwitchHandler.kt
查看文件 @
80b00a1
...
...
@@ -21,8 +21,10 @@ import android.media.AudioAttributes
import android.media.AudioManager
import android.os.Build
import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import com.twilio.audioswitch.*
import io.livekit.android.util.LKLog
import javax.inject.Inject
import javax.inject.Singleton
...
...
@@ -138,13 +140,26 @@ constructor(private val context: Context) : AudioHandler {
private var audioSwitch: AbstractAudioSwitch? = null
// AudioSwitch is not threadsafe, so all calls should be done on the main thread.
private val handler = Handler(Looper.getMainLooper())
// AudioSwitch is not threadsafe, so all calls should be done through a single thread.
private var handler: Handler? = null
private var thread: HandlerThread? = null
@Synchronized
override fun start() {
if (handler != null || thread != null) {
LKLog.i { "AudioSwitchHandler called start multiple times?" }
}
if (thread == null) {
thread = HandlerThread("AudioSwitchHandlerThread").also { it.start() }
}
if (handler == null) {
handler = Handler(thread!!.looper)
}
if (audioSwitch == null) {
handler.removeCallbacksAndMessages(null)
handler.postAtFrontOfQueue {
handler?.removeCallbacksAndMessages(null)
handler?.postAtFrontOfQueue {
val switch =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
AudioSwitch(
...
...
@@ -176,12 +191,17 @@ constructor(private val context: Context) : AudioHandler {
}
}
@Synchronized
override fun stop() {
handler.removeCallbacksAndMessages(null)
handler.postAtFrontOfQueue {
handler?.removeCallbacksAndMessages(null)
handler?.postAtFrontOfQueue {
audioSwitch?.stop()
audioSwitch = null
}
thread?.quitSafely()
handler = null
thread = null
}
/**
...
...
@@ -199,11 +219,12 @@ constructor(private val context: Context) : AudioHandler {
/**
* Select a specific audio device.
*/
@Synchronized
fun selectDevice(audioDevice: AudioDevice?) {
if (Looper.myLooper() ==
Looper.getMainLooper()
) {
if (Looper.myLooper() ==
handler?.looper
) {
audioSwitch?.selectDevice(audioDevice)
} else {
handler.post {
handler
?
.post {
audioSwitch?.selectDevice(audioDevice)
}
}
...
...
livekit-android-sdk/src/main/java/io/livekit/android/audio/CommunicationWorkaround.kt
查看文件 @
80b00a1
/*
* Copyright 2024 LiveKit, Inc.
* Copyright 2024
-2025
LiveKit, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -26,7 +26,7 @@ import androidx.annotation.RequiresApi
import io.livekit.android.dagger.InjectionNames
import io.livekit.android.util.CloseableCoroutineScope
import io.livekit.android.util.LKLog
import kotlinx.coroutines.
Main
CoroutineDispatcher
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
...
...
@@ -83,8 +83,8 @@ constructor() : CommunicationWorkaround {
internal class CommunicationWorkaroundImpl
@Inject
constructor(
@Named(InjectionNames.DISPATCHER_MAIN)
dispatcher: MainCoroutineDispatcher,
@Named(InjectionNames.DISPATCHER_IO)
dispatcher: CoroutineDispatcher,
) : CommunicationWorkaround {
private val coroutineScope = CloseableCoroutineScope(dispatcher)
...
...
请
注册
或
登录
后发表评论