Hai hoặc nhiều ứng dụng Android có thể phát âm thanh cùng lúc vào cùng một luồng đầu ra và hệ thống sẽ kết hợp tất cả lại với nhau. Mặc dù điều này rất ấn tượng về mặt kỹ thuật, nhưng có thể gây khó chịu cho người dùng. Để tránh việc mọi ứng dụng phát nhạc cùng lúc, Android giới thiệu ý tưởng về tiêu điểm âm thanh. Mỗi lần, chỉ một ứng dụng có thể giữ quyền phát âm thanh.
Khi cần phát âm thanh, ứng dụng của bạn phải yêu cầu quyền phát âm thanh. Khi có tâm điểm, thành phần hiển thị có thể phát âm thanh. Tuy nhiên, sau khi có quyền phát âm thanh, bạn có thể không giữ được quyền đó cho đến khi phát xong. Một ứng dụng khác có thể yêu cầu tiêu điểm, làm gián đoạn quyền phát âm thanh của bạn. Nếu điều đó xảy ra, ứng dụng của bạn nên tạm dừng phát hoặc giảm âm lượng để người dùng dễ nghe nguồn âm thanh mới hơn.
Trước Android 12 (API cấp 31), hệ thống không quản lý tiêu điểm âm thanh. Vì vậy, mặc dù nhà phát triển ứng dụng nên tuân thủ các nguyên tắc về quyền phát âm thanh, nhưng nếu một ứng dụng tiếp tục phát âm thanh lớn ngay cả sau khi mất quyền phát âm thanh trên một thiết bị chạy Android 11 (API cấp 30) trở xuống, thì hệ thống không thể ngăn chặn việc này. Tuy nhiên, hành vi này của ứng dụng sẽ khiến người dùng có trải nghiệm kém và thường khiến người dùng gỡ cài đặt ứng dụng có hành vi không đúng cách.
Một ứng dụng âm thanh được thiết kế tốt phải quản lý quyền phát âm thanh theo các nguyên tắc chung sau:
Gọi
requestAudioFocus()
ngay trước khi bắt đầu phát và xác minh rằng lệnh gọi trả vềAUDIOFOCUS_REQUEST_GRANTED
. Gọi đếnrequestAudioFocus()
trong lệnh gọi lạionPlay()
của phiên phát nội dung đa phương tiện.Khi một ứng dụng khác có quyền phát âm thanh, hãy dừng hoặc tạm dừng phát hoặc giảm âm lượng.
Khi quá trình phát dừng (ví dụ: khi ứng dụng không còn gì để phát), hãy từ bỏ quyền phát âm thanh. Ứng dụng của bạn không nhất thiết phải từ bỏ quyền phát âm thanh nếu người dùng tạm dừng phát nhưng có thể tiếp tục phát sau.
Sử dụng
AudioAttributes
để mô tả loại âm thanh mà ứng dụng của bạn đang phát. Ví dụ: đối với các ứng dụng phát lời nói, hãy chỉ địnhCONTENT_TYPE_SPEECH
.
Tiêu điểm âm thanh được xử lý theo cách khác nhau tuỳ thuộc vào phiên bản Android đang chạy:
- Android 12 (API cấp 31) trở lên
- Quyền phát âm thanh do hệ thống quản lý. Hệ thống buộc chế độ phát âm thanh của một ứng dụng phải tắt dần khi một ứng dụng khác yêu cầu quyền phát âm thanh. Hệ thống cũng tắt tiếng phát âm thanh khi nhận được cuộc gọi đến.
- Android 8.0 (API cấp 26) đến Android 11 (API cấp 30)
- Tiêu điểm âm thanh không do hệ thống quản lý, nhưng bao gồm một số thay đổi được giới thiệu kể từ Android 8.0 (API cấp 26).
- Android 7.1 (API cấp 25) trở xuống
- Hệ thống không quản lý quyền phát âm thanh và các ứng dụng quản lý quyền phát âm thanh bằng cách sử dụng
requestAudioFocus()
vàabandonAudioFocus()
.
Tiêu điểm âm thanh trong Android 12 trở lên
Ứng dụng đa phương tiện hoặc trò chơi sử dụng quyền phát âm thanh không được phát âm thanh sau khi mất quyền phát. Trong Android 12 (API cấp 31) trở lên, hệ thống sẽ thực thi hành vi này. Khi một ứng dụng yêu cầu quyền phát âm thanh trong khi một ứng dụng khác có quyền phát và đang phát, hệ thống sẽ buộc ứng dụng đang phát giảm dần âm lượng. Việc thêm hiệu ứng mờ sẽ giúp quá trình chuyển đổi từ ứng dụng này sang ứng dụng khác mượt mà hơn.
Hành vi mờ dần này xảy ra khi đáp ứng các điều kiện sau:
Ứng dụng đầu tiên đang phát đáp ứng tất cả các tiêu chí sau:
- Ứng dụng có thuộc tính sử dụng
AudioAttributes.USAGE_MEDIA
hoặcAudioAttributes.USAGE_GAME
. - Ứng dụng đã yêu cầu quyền phát âm thanh thành công bằng
AudioManager.AUDIOFOCUS_GAIN
. - Ứng dụng không phát âm thanh có loại nội dung
AudioAttributes.CONTENT_TYPE_SPEECH
.
- Ứng dụng có thuộc tính sử dụng
Ứng dụng thứ hai yêu cầu quyền phát âm thanh bằng
AudioManager.AUDIOFOCUS_GAIN
.
Khi các điều kiện này được đáp ứng, hệ thống âm thanh sẽ làm mờ ứng dụng đầu tiên. Khi quá trình làm mờ kết thúc, hệ thống sẽ thông báo cho ứng dụng đầu tiên về việc mất tiêu điểm. Trình phát của ứng dụng sẽ vẫn bị tắt tiếng cho đến khi ứng dụng yêu cầu quyền phát âm thanh lại.
Hành vi hiện có của quyền phát âm thanh
Bạn cũng nên lưu ý những trường hợp khác liên quan đến việc chuyển đổi tiêu điểm âm thanh.
Tự động giảm âm thanh
Tính năng tự động giảm âm lượng (tạm thời giảm âm lượng của một ứng dụng để có thể nghe rõ một ứng dụng khác) được giới thiệu trong Android 8.0 (API cấp 26).
Khi hệ thống triển khai tính năng giảm âm lượng, bạn không cần phải triển khai tính năng này trong ứng dụng.
Tính năng tự động giảm âm cũng xảy ra khi một thông báo âm thanh thu hút tiêu điểm từ một ứng dụng đang phát. Điểm bắt đầu phát thông báo được đồng bộ hoá với điểm kết thúc của dải giảm âm.
Tính năng tự động giảm âm thanh sẽ xảy ra khi đáp ứng các điều kiện sau:
Ứng dụng đầu tiên đang phát đáp ứng tất cả các tiêu chí sau:
- Ứng dụng đã yêu cầu thành công quyền phát âm thanh với bất kỳ loại tăng quyền phát nào.
- Ứng dụng không phát âm thanh có loại nội dung
AudioAttributes.CONTENT_TYPE_SPEECH
. - Ứng dụng không đặt
AudioFocusRequest.Builder.setWillPauseWhenDucked(true)
.
Ứng dụng thứ hai yêu cầu quyền phát âm thanh bằng
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
Khi đáp ứng các điều kiện này, hệ thống âm thanh sẽ tắt tất cả trình phát đang hoạt động của ứng dụng đầu tiên trong khi ứng dụng thứ hai có tiêu điểm. Khi ứng dụng thứ hai bỏ focus, ứng dụng đó sẽ huỷ bỏ các ứng dụng đó. Ứng dụng đầu tiên không được thông báo khi mất tiêu điểm, vì vậy, ứng dụng này không cần làm gì cả.
Xin lưu ý rằng tính năng tự động giảm âm không được thực hiện khi người dùng đang nghe nội dung lời nói, vì người dùng có thể bỏ lỡ một số chương trình. Ví dụ: hướng dẫn bằng giọng nói cho đường đi sẽ không bị tắt tiếng.
Tắt tiếng chế độ phát âm thanh hiện tại khi có cuộc gọi đến
Một số ứng dụng hoạt động không đúng cách và tiếp tục phát âm thanh trong cuộc gọi điện thoại. Tình huống này buộc người dùng phải tìm và tắt tiếng hoặc thoát khỏi ứng dụng vi phạm để nghe cuộc gọi. Để ngăn chặn điều này, hệ thống có thể tắt tiếng âm thanh của các ứng dụng khác trong khi có cuộc gọi đến. Hệ thống gọi tính năng này khi nhận được một cuộc gọi điện thoại đến và một ứng dụng đáp ứng các điều kiện sau:
- Ứng dụng có thuộc tính sử dụng
AudioAttributes.USAGE_MEDIA
hoặcAudioAttributes.USAGE_GAME
. - Ứng dụng đã yêu cầu thành công quyền phát âm thanh (bất kỳ quyền phát nào) và đang phát âm thanh.
Nếu một ứng dụng tiếp tục phát trong cuộc gọi, thì chế độ phát của ứng dụng đó sẽ bị tắt tiếng cho đến khi cuộc gọi kết thúc. Tuy nhiên, nếu một ứng dụng bắt đầu phát trong cuộc gọi, thì trình phát đó sẽ không bị tắt tiếng với giả định rằng người dùng đã chủ ý bắt đầu phát.
Tiêu điểm âm thanh trong Android 8.0 đến Android 11
Kể từ Android 8.0 (API cấp 26), khi gọi requestAudioFocus()
, bạn phải cung cấp tham số AudioFocusRequest
. AudioFocusRequest
chứa thông tin về ngữ cảnh âm thanh và chức năng của ứng dụng. Hệ thống sử dụng thông tin này để tự động quản lý việc tăng và mất tiêu điểm âm thanh. Để nhả quyền phát âm thanh, hãy gọi phương thức abandonAudioFocusRequest()
. Phương thức này cũng lấy AudioFocusRequest
làm đối số. Sử dụng cùng một thực thể AudioFocusRequest
cả khi bạn yêu cầu và bỏ qua tiêu điểm.
Để tạo AudioFocusRequest
, hãy sử dụng AudioFocusRequest.Builder
. Vì yêu cầu tiêu điểm phải luôn chỉ định loại yêu cầu, nên loại này được đưa vào hàm khởi tạo cho trình tạo. Sử dụng các phương thức của trình tạo để đặt các trường khác của yêu cầu.
Bạn phải điền trường FocusGain
; tất cả các trường khác đều không bắt buộc.
Phương thức | Ghi chú |
---|---|
setFocusGain()
|
Đây là trường bắt buộc trong mọi yêu cầu. Phương thức này có các giá trị giống như durationHint được dùng trong lệnh gọi trước Android 8.0 đến requestAudioFocus() : AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT , AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK hoặc AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE .
|
setAudioAttributes()
|
AudioAttributes mô tả trường hợp sử dụng cho ứng dụng của bạn. Hệ thống sẽ xem xét các trường hợp này khi một ứng dụng có được và mất quyền phát âm thanh. Thuộc tính thay thế khái niệm về loại luồng. Trong Android 8.0 (API cấp 26) trở lên, các loại luồng cho mọi thao tác khác ngoài việc điều khiển âm lượng không còn được dùng nữa. Sử dụng các thuộc tính giống nhau trong yêu cầu tiêu điểm mà bạn sử dụng trong trình phát âm thanh (như trong ví dụ sau bảng này).
Trước tiên, hãy sử dụng
Nếu không được chỉ định, |
setWillPauseWhenDucked()
|
Khi một ứng dụng khác yêu cầu tiêu điểm bằng AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK , ứng dụng có tiêu điểm thường không nhận được lệnh gọi lại onAudioFocusChange() vì hệ thống có thể tự ẩn tiêu điểm. Khi bạn cần tạm dừng phát thay vì giảm âm lượng, hãy gọi setWillPauseWhenDucked(true) rồi tạo và đặt OnAudioFocusChangeListener , như mô tả trong phần tự động giảm âm lượng.
|
setAcceptsDelayedFocusGain()
|
Yêu cầu quyền phát âm thanh có thể không thành công khi tiêu điểm bị một ứng dụng khác khoá. Phương thức này cho phép lấy tiêu điểm bị trì hoãn: khả năng lấy tiêu điểm không đồng bộ khi có tiêu điểm.
Xin lưu ý rằng tính năng lấy tiêu điểm bị trì hoãn chỉ hoạt động nếu bạn cũng chỉ định |
setOnAudioFocusChangeListener()
|
Bạn chỉ cần OnAudioFocusChangeListener nếu cũng chỉ định willPauseWhenDucked(true) hoặc setAcceptsDelayedFocusGain(true) trong yêu cầu.
Có hai phương thức để thiết lập trình nghe: một phương thức có và một phương thức không có đối số trình xử lý. Trình xử lý là luồng mà trình nghe chạy trên đó. Nếu bạn không chỉ định trình xử lý, trình xử lý liên kết với |
Ví dụ sau đây cho biết cách sử dụng AudioFocusRequest.Builder
để tạo AudioFocusRequest
, đồng thời yêu cầu và từ bỏ quyền phát âm thanh:
// initializing variables for audio focus and playback management audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN).run { setAudioAttributes(AudioAttributes.Builder().run { setUsage(AudioAttributes.USAGE_GAME) setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) build() }) setAcceptsDelayedFocusGain(true) setOnAudioFocusChangeListener(afChangeListener, handler) build() } val focusLock = Any() var playbackDelayed = false var playbackNowAuthorized = false // requesting audio focus and processing the response val res = audioManager.requestAudioFocus(focusRequest) synchronized(focusLock) { playbackNowAuthorized = when (res) { AudioManager.AUDIOFOCUS_REQUEST_FAILED -> false AudioManager.AUDIOFOCUS_REQUEST_GRANTED -> { playbackNow() true } AudioManager.AUDIOFOCUS_REQUEST_DELAYED -> { playbackDelayed = true false } else -> false } } // implementing OnAudioFocusChangeListener to react to focus changes override fun onAudioFocusChange(focusChange: Int) { when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> if (playbackDelayed || resumeOnFocusGain) { synchronized(focusLock) { playbackDelayed = false resumeOnFocusGain = false } playbackNow() } AudioManager.AUDIOFOCUS_LOSS -> { synchronized(focusLock) { resumeOnFocusGain = false playbackDelayed = false } pausePlayback() } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { synchronized(focusLock) { // only resume if playback is being interrupted resumeOnFocusGain = isPlaying() playbackDelayed = false } pausePlayback() } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { // ... pausing or ducking depends on your app } } }
// initializing variables for audio focus and playback management audioManager = (AudioManager) Context.getSystemService(Context.AUDIO_SERVICE); playbackAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(playbackAttributes) .setAcceptsDelayedFocusGain(true) .setOnAudioFocusChangeListener(afChangeListener, handler) .build(); final Object focusLock = new Object(); boolean playbackDelayed = false; boolean playbackNowAuthorized = false; // requesting audio focus and processing the response int res = audioManager.requestAudioFocus(focusRequest); synchronized(focusLock) { if (res == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { playbackNowAuthorized = false; } else if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { playbackNowAuthorized = true; playbackNow(); } else if (res == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { playbackDelayed = true; playbackNowAuthorized = false; } } // implementing OnAudioFocusChangeListener to react to focus changes @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: if (playbackDelayed || resumeOnFocusGain) { synchronized(focusLock) { playbackDelayed = false; resumeOnFocusGain = false; } playbackNow(); } break; case AudioManager.AUDIOFOCUS_LOSS: synchronized(focusLock) { resumeOnFocusGain = false; playbackDelayed = false; } pausePlayback(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: synchronized(focusLock) { // only resume if playback is being interrupted resumeOnFocusGain = isPlaying(); playbackDelayed = false; } pausePlayback(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // ... pausing or ducking depends on your app break; } } }
Tự động giảm âm thanh
Trong Android 8.0 (API cấp 26), khi một ứng dụng khác yêu cầu tiêu điểm bằng AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
, hệ thống có thể giảm và khôi phục âm lượng mà không cần gọi lại onAudioFocusChange()
của ứng dụng.
Mặc dù tính năng tự động giảm âm là hành vi chấp nhận được đối với các ứng dụng phát nhạc và video, nhưng tính năng này không hữu ích khi phát nội dung nói, chẳng hạn như trong ứng dụng sách nói. Trong trường hợp này, ứng dụng nên tạm dừng.
Nếu bạn muốn ứng dụng tạm dừng khi được yêu cầu giảm âm lượng thay vì giảm âm lượng, hãy tạo một OnAudioFocusChangeListener
có phương thức gọi lại onAudioFocusChange()
triển khai hành vi tạm dừng/tiếp tục mong muốn.
Gọi setOnAudioFocusChangeListener()
để đăng ký trình nghe và gọi setWillPauseWhenDucked(true)
để yêu cầu hệ thống sử dụng lệnh gọi lại thay vì tự động giảm âm lượng.
Chậm lấy nét
Đôi khi, hệ thống không thể cấp yêu cầu về quyền phát âm thanh vì quyền phát âm thanh bị một ứng dụng khác "khoá", chẳng hạn như trong khi gọi điện. Trong trường hợp này, requestAudioFocus()
trả về AUDIOFOCUS_REQUEST_FAILED
. Khi điều này xảy ra, ứng dụng của bạn không nên tiếp tục phát âm thanh vì ứng dụng không nhận được tiêu điểm.
Phương thức setAcceptsDelayedFocusGain(true)
cho phép ứng dụng của bạn xử lý yêu cầu lấy tiêu điểm một cách không đồng bộ. Khi đặt cờ này, yêu cầu được thực hiện khi tiêu điểm bị khoá sẽ trả về AUDIOFOCUS_REQUEST_DELAYED
. Khi điều kiện đã khoá tiêu điểm âm thanh không còn tồn tại, chẳng hạn như khi cuộc gọi điện thoại kết thúc, hệ thống sẽ cấp yêu cầu tiêu điểm đang chờ xử lý và gọi onAudioFocusChange()
để thông báo cho ứng dụng của bạn.
Để xử lý việc lấy tiêu điểm bị trễ, bạn phải tạo một OnAudioFocusChangeListener
bằng phương thức gọi lại onAudioFocusChange()
triển khai hành vi mong muốn và đăng ký trình nghe bằng cách gọi setOnAudioFocusChangeListener()
.
Quyền phát âm thanh trong Android 7.1 trở xuống
Khi gọi requestAudioFocus()
, bạn phải chỉ định một gợi ý về thời lượng. Ứng dụng khác đang giữ tiêu điểm và phát có thể tuân theo gợi ý này:
- Yêu cầu quyền phát âm thanh vĩnh viễn (
AUDIOFOCUS_GAIN
) khi bạn dự định phát âm thanh trong tương lai gần (ví dụ: khi phát nhạc) và bạn muốn chủ sở hữu quyền phát âm thanh trước đó dừng phát. - Yêu cầu quyền phát tạm thời (
AUDIOFOCUS_GAIN_TRANSIENT
) khi bạn chỉ dự kiến phát âm thanh trong một thời gian ngắn và bạn muốn phần tử giữ trước đó tạm dừng phát. - Yêu cầu quyền phát tạm thời bằng tính năng điều chỉnh âm lượng (
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
) để cho biết rằng bạn chỉ dự kiến phát âm thanh trong một thời gian ngắn và chủ sở hữu quyền phát trước đó có thể tiếp tục phát nếu "điều chỉnh âm lượng" (giảm) đầu ra âm thanh. Cả hai đầu ra âm thanh đều được trộn vào luồng âm thanh. Tính năng giảm âm lượng đặc biệt phù hợp với các ứng dụng sử dụng luồng âm thanh không liên tục, chẳng hạn như hướng dẫn lái xe bằng âm thanh.
Phương thức requestAudioFocus()
cũng yêu cầu AudioManager.OnAudioFocusChangeListener
. Bạn nên tạo trình nghe này trong cùng một hoạt động hoặc dịch vụ sở hữu phiên phát nội dung nghe nhìn. Phương thức này triển khai lệnh gọi lại onAudioFocusChange()
mà ứng dụng của bạn nhận được khi một số ứng dụng khác thu nạp hoặc từ bỏ tiêu điểm âm thanh.
Đoạn mã sau đây yêu cầu quyền phát âm thanh vĩnh viễn trên luồng STREAM_MUSIC
và đăng ký OnAudioFocusChangeListener
để xử lý các thay đổi tiếp theo về quyền phát âm thanh. (Trình nghe thay đổi được thảo luận trong phần Phản hồi thay đổi tiêu điểm âm thanh.)
audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager lateinit var afChangeListener AudioManager.OnAudioFocusChangeListener ... // Request audio focus for playback val result: Int = audioManager.requestAudioFocus( afChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN ) if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // Start playback }
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); AudioManager.OnAudioFocusChangeListener afChangeListener; ... // Request audio focus for playback int result = audioManager.requestAudioFocus(afChangeListener, // Use the music stream. AudioManager.STREAM_MUSIC, // Request permanent focus. AudioManager.AUDIOFOCUS_GAIN); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // Start playback }
Khi bạn phát xong, hãy gọi abandonAudioFocus()
.
audioManager.abandonAudioFocus(afChangeListener)
// Abandon audio focus when playback complete audioManager.abandonAudioFocus(afChangeListener);
Thao tác này sẽ thông báo cho hệ thống rằng bạn không còn yêu cầu tiêu điểm và huỷ đăng ký OnAudioFocusChangeListener
được liên kết. Nếu bạn yêu cầu tiêu điểm tạm thời, thì thao tác này sẽ thông báo cho một ứng dụng đã tạm dừng hoặc bị ẩn rằng ứng dụng đó có thể tiếp tục phát hoặc khôi phục âm lượng.
Phản hồi thay đổi về quyền phát âm thanh
Khi một ứng dụng có quyền phát âm thanh, ứng dụng đó phải có thể giải phóng quyền phát âm thanh khi một ứng dụng khác yêu cầu quyền phát âm thanh cho chính ứng dụng đó. Khi điều này xảy ra, ứng dụng của bạn sẽ nhận được lệnh gọi đến phương thức onAudioFocusChange()
trong AudioFocusChangeListener
mà bạn đã chỉ định khi ứng dụng gọi requestAudioFocus()
.
Tham số focusChange
được truyền đến onAudioFocusChange()
cho biết loại thay đổi đang diễn ra. Giá trị này tương ứng với gợi ý về thời lượng mà ứng dụng đang lấy tiêu điểm sử dụng. Ứng dụng của bạn sẽ phản hồi một cách thích hợp.
- Mất tiêu điểm tạm thời
- Nếu thay đổi tiêu điểm là tạm thời (
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
hoặcAUDIOFOCUS_LOSS_TRANSIENT
), thì ứng dụng của bạn sẽ ẩn (nếu bạn không dựa vào tính năng tự động ẩn) hoặc tạm dừng phát nhưng vẫn duy trì trạng thái như cũ.Trong thời gian tạm thời mất quyền phát âm thanh, bạn nên tiếp tục theo dõi các thay đổi về quyền phát âm thanh và chuẩn bị tiếp tục phát bình thường khi lấy lại quyền phát. Khi ứng dụng chặn bỏ tiêu điểm, bạn sẽ nhận được lệnh gọi lại (
AUDIOFOCUS_GAIN
). Tại thời điểm này, bạn có thể khôi phục âm lượng về mức bình thường hoặc bắt đầu phát lại. - Mất tiêu điểm vĩnh viễn
- Nếu quyền phát âm thanh bị mất vĩnh viễn (
AUDIOFOCUS_LOSS
), thì một ứng dụng khác đang phát âm thanh. Ứng dụng của bạn sẽ tạm dừng phát ngay lập tức vì sẽ không bao giờ nhận được lệnh gọi lạiAUDIOFOCUS_GAIN
. Để bắt đầu lại quá trình phát, người dùng phải thực hiện một thao tác rõ ràng, chẳng hạn như nhấn nút điều khiển phát trong thông báo hoặc giao diện người dùng của ứng dụng.
Đoạn mã sau đây minh hoạ cách triển khai OnAudioFocusChangeListener
và lệnh gọi lại onAudioFocusChange()
. Lưu ý việc sử dụng Handler
để trì hoãn lệnh gọi lại dừng khi mất quyền phát âm thanh vĩnh viễn.
private val handler = Handler() private val afChangeListener = AudioManager.OnAudioFocusChangeListener { focusChange -> when (focusChange) { AudioManager.AUDIOFOCUS_LOSS -> { // Permanent loss of audio focus // Pause playback immediately mediaController.transportControls.pause() // Wait 30 seconds before stopping playback handler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30)) } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { // Pause playback } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> { // Lower the volume, keep playing } AudioManager.AUDIOFOCUS_GAIN -> { // Your app has been granted audio focus again // Raise volume to normal, restart playback if necessary } } }
private Handler handler = new Handler(); AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() { public void onAudioFocusChange(int focusChange) { if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { // Permanent loss of audio focus // Pause playback immediately mediaController.getTransportControls().pause(); // Wait 30 seconds before stopping playback handler.postDelayed(delayedStopRunnable, TimeUnit.SECONDS.toMillis(30)); } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { // Pause playback } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) { // Lower the volume, keep playing } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { // Your app has been granted audio focus again // Raise volume to normal, restart playback if necessary } } };
Trình xử lý sử dụng Runnable
có dạng như sau:
private var delayedStopRunnable = Runnable { mediaController.transportControls.stop() }
private Runnable delayedStopRunnable = new Runnable() { @Override public void run() { getMediaController().getTransportControls().stop(); } };
Để đảm bảo tính năng tạm dừng trễ không kích hoạt nếu người dùng bắt đầu phát lại, hãy gọi mHandler.removeCallbacks(mDelayedStopRunnable)
để phản hồi mọi thay đổi về trạng thái. Ví dụ: gọi removeCallbacks()
trong onPlay()
, onSkipToNext()
của Lệnh gọi lại, v.v. Bạn cũng nên gọi phương thức này trong lệnh gọi lại onDestroy()
của dịch vụ khi dọn dẹp các tài nguyên mà dịch vụ của bạn sử dụng.