Quản lý quyền phát âm thanh

Hai hoặc nhiều ứng dụng Android có thể phát âm thanh tới cùng một luồng đầu ra đồng thời và hệ thống trộn mọi thứ lại với nhau. Mặc dù đây là ấn tượng về mặt kỹ thuật thì nó có thể khiến người dùng thấy rất khó chịu. Để tránh mọi ứng dụng âm nhạc phát cùng lúc, Android giới thiệu ý tưởng về âm thanh tập trung. Chỉ một ứng dụng có thể giữ quyền phát âm thanh tại một thời điểm.

Khi cần xuất âm thanh, ứng dụng của bạn sẽ yêu cầu quyền phát âm thanh. Khi có tập trung, thiết bị có thể phát âm thanh. Tuy nhiên, sau khi có được quyền phát âm thanh, bạn có thể không có thể giữ cho đến khi bạn chơi xong. Một ứng dụng khác có thể yêu cầu lấy tiêu điểm, giành quyền giữ quyền phát âm thanh. Nếu điều đó xảy ra, ứng dụng của bạn sẽ tạm dừng phát hoặc giảm âm lượng để người dùng nghe được nguồn âm thanh mới dễ dàng hơn.

Trước Android 12 (API cấp 31), hệ thống không quản lý quyền phát âm thanh. Vì vậy, còn nhà phát triển ứng dụng được khuyến khích tuân thủ nguyên tắc về quyền phát âm thanh, nếu ứng dụng tiếp tục phát lớn ngay cả sau khi mất quyền phát âm thanh trên thiết bị chạy Android 11 (API cấp 30) trở xuống, thì hệ thống không ngăn chặn được điều này. Tuy nhiên, hành vi này của ứng dụng dẫn đến trải nghiệm người dùng kém và thường có thể gỡ cài đặt ứng dụng bị lỗi.

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 những nguyên tắc chung nguyên tắc:

  • Hãy gọi requestAudioFocus() ngay trước khi bắt đầu chơi và xác minh rằng cuộc gọi lại AUDIOFOCUS_REQUEST_GRANTED. Gọi điện đến requestAudioFocus() trong lệnh gọi lại onPlay() của phiên phát nội dung đa phương tiện.

  • Khi một ứng dụng khác lấy đượ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 (tức là giảm) âm lượng.

  • Khi dừng phát (ví dụ: khi ứng dụng không còn nội dung nào để phát), bỏ qua quyền phát âm thanh. Ứng dụng của bạn không phải bỏ qua 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, chỉ định CONTENT_TYPE_SPEECH.

Quyền phát âm thanh được xử lý khác nhau, tuỳ thuộc vào phiên bản Android mà đ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 phát lại âm thanh từ ứng dụng nhỏ 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 tính nă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)
Quyền phát âm thanh không do hệ thống quản lý, nhưng có một số thay đổi ra mắt trong 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 requestAudioFocus()abandonAudioFocus().

Quyền phát âm thanh trên Android 12 trở lên

Một ứng dụng nội dung đ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 bị mất tập trung. Trong Android 12 (API cấp 31) trở lên, hệ thống sẽ thực thi quy tắc này hành vi. 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 âm thanh đang phát, hệ thống sẽ buộc ứng dụng đang phát nhỏ dần. Việc bổ sung hiệu ứng làm mờ giúp quá trình chuyển đổi diễn ra suôn sẻ hơn khi chuyển từ ứng dụng này sang ứng dụng khác.

Hành vi mờ dần này xảy ra khi các điều kiện sau được đáp ứng:

  1. Ứng dụng đầu tiên, đang phát đáp ứng tất cả các tiêu chí sau:

  2. Ứng dụng thứ hai yêu cầu quyền phát âm thanh bằng AudioManager.AUDIOFOCUS_GAIN.

Khi những điều kiện này được đáp ứng, hệ thống âm thanh sẽ tắt tiếng ứng dụng đầu tiên. Tại khi kết thúc hiệu ứng mờ dần, hệ thống sẽ thông báo cho ứng dụng đầu tiên về tình trạng mất tiêu điểm. của ứng dụng trình phát sẽ ở chế độ tắt tiếng cho đến khi ứng dụng yêu cầu quyền phát âm thanh trở lại.

Chế độ tập trung vào âm thanh hiện tại

Bạn cũng nên lưu ý các trường hợp khác liên quan đến việc chuyển đổi âm thanh tập trung.

Tự động giảm âm lượng

Tự động giảm âm lượng (tạm thời giảm mức âm thanh của một ứng dụng để khác có thể nghe thấy rõ ràng) đã được giới thiệu trong Android 8.0 (API cấp 26).

Bằng cách để hệ thống triển khai tính năng giảm kích thước, bạn không phải thực hiện việc giảm hiệu suất ứng dụng của bạn.

Chế độ tự động giảm âm lượng cũng diễn ra khi thông báo âm thanh thu hút được tiêu điểm từ một ứng dụng phát. Thời điểm bắt đầu phát thông báo đã được đồng bộ hoá ở cuối đoạn đường giảm dần.

Tính năng tự động giảm âm thanh chỉ diễn ra khi đáp ứng các điều kiện sau:

  1. Ứng dụng đầu tiên đang phát đáp ứng tất cả các tiêu chí sau:

  2. Ứng dụng thứ hai yêu cầu quyền phát âm thanh bằng AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK.

Khi những điều kiện này được đáp ứng, hệ thống âm thanh sẽ tắt tất cả trình phát đang hoạt động ứng dụng đầu tiên trong khi ứng dụng thứ hai lấy tiêu điểm. Khi ứng dụng thứ hai bỏ ngang tập trung vào nó, nó sẽ giúp họ. Ứng dụng đầu tiên không được thông báo khi mất tiêu điểm, nên ứng dụng không phải làm gì cả.

Lưu ý: tính năng tự động giảm âm thanh sẽ không được thực hiện khi người dùng đang nghe nội dung bài phát biểu vì người dùng có thể bỏ lỡ một số nội dung của chương trình. Ví dụ: hướng dẫn bằng giọng nói cho chỉ đường lái xe không bị giảm độ phân giải.

Tắt tiếng phát âm thanh hiện tại cho các cuộc gọi điện thoạ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 khi 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 trong để nghe cuộc gọi của họ. Để ngăn chặn điều này, hệ thống có thể tắt âm thanh từ trong khi có cuộc gọi đến. Hệ thống gọi tính năng này khi nhận được cuộc gọi điện thoại đến và ứng dụng đáp ứng các điều kiện sau:

  • Ứng dụng có AudioAttributes.USAGE_MEDIA hoặc Thuộc tính sử dụng AudioAttributes.USAGE_GAME.
  • Ứng dụng đã yêu cầu thành công quyền phát âm thanh (bất kỳ mức lấy né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, quá trình 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 tắt tiếng dựa trên giả định rằng người dùng bắt đầu phát có chủ đích.

Quyền phát âm thanh trong Android 8.0 đến Android 11

Kể từ Android 8.0 (API cấp 26), khi bạn 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à khả năng của ứng dụng. Chiến lược phát hành đĩa đơn hệ thống sử dụng thông tin này để quản lý mức tăng và mất quyền phát âm thanh tự động. Để 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 lấy tiêu điểm phải luôn chỉ định loại yêu cầu, loại này sẽ được đưa vào hàm khởi tạo cho trình tạo. Sử dụng phương thức của trình tạo để đặt các trường khác của của bạn.

Trường FocusGain là bắt buộc; tất cả các trường khác là không bắt buộc.

Phương thứcGhi chú
setFocusGain() Trường này là bắt buộc trong mọi yêu cầu. Hàm này nhận các giá trị giống như durationHint dùng trong lệnh gọi requestAudioFocus() trước phiên bản Android 8.0: 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. Chiến lược phát hành đĩa đơn hệ thống sẽ xem xét chúng khi một ứng dụng nhận và mất quyền phát âm thanh. Thuộc tính thay thế khái niệm loại luồng. Trong Android 8.0 (API cấp 26) trở lên, Các loại luồng cho bất kỳ thao tác nào khác ngoài kiểm soát âm lượng sẽ không được dùng nữa. Sử dụng cùng các thuộc tính trong yêu cầu lấy nét mà bạn sử dụng trong trình phát âm thanh (như như trong ví dụ sau bảng này).

Sử dụng AudioAttributes.Builder để chỉ định sau đó sử dụng phương pháp này để gán các thuộc tính cho của bạn.

Nếu không được chỉ định, AudioAttributes sẽ mặc định là AudioAttributes.USAGE_MEDIA.

setWillPauseWhenDucked() Khi một ứng dụng khác lấy tiêu điểm bằng AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, ứng dụng có tiêu điểm không thường nhận được onAudioFocusChange() vì hệ thống có thể thực hiện tự thu mình. Khi bạn cần tạm dừng phát so với giảm âm lượng, hãy gọi setWillPauseWhenDucked(true) và tạo và đặt OnAudioFocusChangeListener, như được mô tả trong chế độ tự động giảm hiệu suất.
setAcceptsDelayedFocusGain() Yêu cầu cấp 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 bật tính năng lấy nét bị trễ: khả năng để lấy tiêu điểm không đồng bộ khi có sẵn.

Lưu ý rằng lấy nét bị trễ chỉ hoạt động nếu bạn cũng chỉ định một AudioManager.OnAudioFocusChangeListener trong yêu cầu âm thanh, vì ứng dụng của bạn cần nhận lệnh gọi lại để biết rằng tiêu điểm đã được cấp.

setOnAudioFocusChangeListener() Chỉ bắt buộc phải có OnAudioFocusChangeListener nếu bạn cũng chỉ định willPauseWhenDucked(true) hoặc setAcceptsDelayedFocusGain(true) trong yêu cầu.

Có hai phương pháp để thiết lập trình nghe: một phương pháp có và một phương pháp không có đối số của 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 Looper được dùng.

Ví dụ sau đây minh hoạ cách sử dụng AudioFocusRequest.Builder để tạo một AudioFocusRequest rồi yêu cầu cũng như bỏ qua quyền phát âm thanh:

Kotlin

// 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
        }
    }
}

Java

// 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 lượng

Trong Android 8.0 (API cấp 26), khi một ứng dụng khác yêu cầu tập trung vào AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK hệ thống có thể giảm số lượng và khôi phục âm lượng mà không cần thực hiện lệnh gọi lại onAudioFocusChange() của ứng dụng.

Mặc dù chế độ tự động giảm âm lượng là hành vi được chấp nhận khi phát nhạc và video không hữu ích khi phát nội dung nói, chẳng hạn như ứng dụng sách nói. Trong trường hợp này, ứng dụng sẽ 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 rồi gọi setWillPauseWhenDucked(true) để yêu cầu hệ thống sử dụng lệnh gọi lại của bạn thay vì tự động giảm.

Trễ lấy nét

Đôi khi, hệ thống không thể cấp yêu cầu cấp quyền phát âm thanh vì tiêu điểm là "đã khoá" bởi một ứng dụng khác, chẳng hạn như trong khi gọi điện thoại. 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 sẽ không tiếp tục phát âm thanh vì ứng dụng này không thu được tập trung.

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 thiết lập cờ này, một yêu cầu được đưa ra khi tiêu điểm bị khoá sẽ trả về AUDIOFOCUS_REQUEST_DELAYED. Khi điều kiện khoá âm thanh tiêu điểm không còn tồn tại, chẳng hạn như khi một cuộc gọi điện thoại kết thúc, hệ thống cho phép yêu cầu lấy tiêu điểm đang chờ xử lý và gọi onAudioFocusChange() để thông báo cho .

Để xử lý việc mất tiêu điểm bị trì hoãn, 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 bạn gọi requestAudioFocus() bạn phải chỉ định gợi ý về thời lượng, có thể được vinh danh bởi một ứng dụng khác hiện đang tập trung và chơi:

  • Yêu cầu quyền phát âm thanh vĩnh viễn (AUDIOFOCUS_GAIN) khi bạn định phát âm thanh cho tương lai gần (ví dụ: khi phát nhạc) và bạn mong muốn người giữ quyền phát âm thanh trước đó để dừng phát.
  • Yêu cầu tiêu điểm tạm thời (AUDIOFOCUS_GAIN_TRANSIENT) khi bạn muốn chơi âm thanh chỉ trong một thời gian ngắn và bạn muốn trình giữ trước tạm dừng đang phát.
  • Yêu cầu lấy tiêu điểm tạm thời bằng tính năng vị trí (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) để cho biết rằng bạn muốn phát âm thanh trong một khoảng thời gian ngắn và chủ sở hữu tiêu điểm trước đây có thể giữ lại chơi xem có "vịt" không (giảm) đầu ra âm thanh. Cả hai đầu ra âm thanh đều bị kết hợp vào luồng âm thanh. Hiệu ứng giảm âm thanh đặc biệt phù hợp với những ứng dụng sử dụng luồng âm thanh gián đoạn, chẳng hạn như âm thanh chỉ đường lái xe.

Phương thức requestAudioFocus() cũng yêu cầu một AudioManager.OnAudioFocusChangeListener. Trình nghe này phải là được tạo trong cùng một hoạt động hoặc dịch vụ sở hữu phiên phát nội dung đa phương tiện của bạn. Nó triển khai lệnh gọi lại onAudioFocusChange() mà ứng dụng nhận được khi một số ứng dụng khác thu được hoặc bỏ qua quyền phát â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 rồi đăng ký một OnAudioFocusChangeListener để xử lý những 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 hồi khi có thay đổi về quyền phát âm thanh.)

Kotlin

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
}

Java

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().

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

// 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 lấy tiêu điểm nữa và huỷ đăng ký OnAudioFocusChangeListener được liên kết. Nếu bạn đã yêu cầu lấy tiêu điểm tạm thời, Thao tác này sẽ thông báo cho một ứng dụng đã tạm dừng hoặc đã giảm kích thước rằng ứng dụng đó có thể tiếp tục phát hoặc khôi phục âm lượng của bộ nhớ đệm.

Phản hồi khi thay đổi quyền phát âm thanh

Khi một ứng dụng có được quyền phát âm thanh, ứng dụng đó phải huỷ được quyền đó khi một ứng dụng khác yêu cầu quyền phát âm thanh cho chính nó. Khi điều này xảy ra, ứng dụng của bạn nhận được một cuộc gọi đến onAudioFocusChange() trong AudioFocusChangeListener mà bạn đã chỉ định khi ứng dụng gọi requestAudioFocus().

Tham số focusChange được chuyển đến onAudioFocusChange() cho biết loại thay đổi đang diễn ra. Tương ứng với thành gợi ý về thời lượng mà ứng dụng đang lấy tiêu điểm. Ứng dụng của bạn sẽ đưa ra câu trả lời thích hợp.

Mất tập trung tạm thời
Nếu tiêu điểm thay đổi là tạm thời (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK hoặc AUDIOFOCUS_LOSS_TRANSIENT), ứng dụng của bạn sẽ giảm tốc độ (nếu bạn không dựa vào bật tự động giảm âm thanh) hoặc tạm dừng phát nhưng nếu không thì hãy giữ nguyên trạng thái.

Trong khi 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 tập trung vào âm thanh và sẵn sàng tiếp tục phát lại bình thường khi bạn lấy lại tập trung. Khi ứng dụng chặn bỏ qua tiêu điểm, bạn sẽ nhận được một lệnh gọi lại (AUDIOFOCUS_GAIN). Lúc 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 khả năng tập trung vĩnh viễn
Nếu tình trạng mất quyền phát âm thanh vĩnh viễn (AUDIOFOCUS_LOSS), thì một ứng dụng khác đang đ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ì ứng dụng sẽ không bao giờ bị tạm dừng nhận lệnh gọi lại AUDIOFOCUS_GAIN. Để bắt đầu lại quá trình phát, người dùng phải thực hiện một hành động rõ ràng, chẳng hạn như nhấn vào nút phát điều khiển truyền tải 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 ý sử dụng Handler để trì hoãn lệnh gọi lại dừng khi mất âm thanh vĩnh viễn tập trung.

Kotlin

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
        }
    }
}

Java

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:

Kotlin

private var delayedStopRunnable = Runnable {
    mediaController.transportControls.stop()
}

Java

private Runnable delayedStopRunnable = new Runnable() {
    @Override
    public void run() {
        getMediaController().getTransportControls().stop();
    }
};

Để đảm bảo quá trình dừng bị trì hoãn không kích hoạt nếu người dùng phát lại lần nữa, hãy gọi mHandler.removeCallbacks(mDelayedStopRunnable) để phản hồi bất kỳ tiểu bang nào thay đổi. Ví dụ: gọi removeCallbacks() trong onPlay() của Lệnh gọi lại, onSkipToNext(), v.v. Bạn cũng nên gọi phương thức này trong lệnh gọi Lệnh gọi lại onDestroy() khi dọn dẹp tài nguyên mà dịch vụ của bạn sử dụng.