จัดการโฟกัสเสียง

แอป Android อย่างน้อย 2 แอปเล่นเสียงไปยังสตรีมเอาต์พุตเดียวกันได้ แล้วระบบจะผสมทุกอย่างเข้าด้วยกัน ขณะที่ ในทางเทคนิคแล้ว ผู้ใช้อาจสร้างความเสียหายได้อย่างมาก เพื่อหลีกเลี่ยงทุก แอปเพลงที่เล่นในเวลาเดียวกัน Android ได้แนะนำแนวคิดเรื่องเสียง โฟกัส โฟกัสเสียงได้ครั้งละ 1 แอปเท่านั้น

เมื่อแอปต้องเอาต์พุตเสียง แอปควรขอการโฟกัสเสียง เมื่อมี ก็จะเล่นเสียงได้ แต่หลังจากที่คุณได้รับโฟกัสทางเสียงแล้ว คุณจะไม่สามารถ คุณจะเก็บเพลงไว้เล่นจนกว่าจะเล่นจบ แอปอื่นสามารถขอโฟกัสได้ ระงับการโฟกัสเสียงค้างไว้ หากเป็นเช่นนั้น แอปควรหยุดชั่วคราว เล่นหรือลดระดับเสียงเพื่อให้ผู้ใช้ได้ยินแหล่งที่มาของเสียงใหม่ได้ง่ายขึ้น

ก่อน Android 12 (API ระดับ 31) ระบบจะไม่จัดการการโฟกัสเสียง ดังนั้น ในขณะที่นักพัฒนาแอปควรปฏิบัติตามหลักเกณฑ์ที่เน้นเรื่องเสียง หากแอปยังคงเล่นเสียงดังต่อไปแม้จะไม่ได้โฟกัสเสียงในอุปกรณ์แล้ว ใช้ Android 11 (API ระดับ 30) หรือต่ำกว่า ระบบจะป้องกันไม่ได้ อย่างไรก็ตาม ลักษณะการทำงานของแอปเช่นนี้ทําให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดี และบ่อยครั้ง ผู้ใช้ถอนการติดตั้งแอปที่ทำงานผิดปกติได้

แอปเสียงที่ออกแบบมาอย่างดีควรจัดการโฟกัสของเสียงตามคำทั่วไปเหล่านี้ หลักเกณฑ์:

  • โทรหา requestAudioFocus() ทันทีก่อนที่จะเริ่มเล่นและยืนยันว่า การโทรกลับ AUDIOFOCUS_REQUEST_GRANTED โทรหา requestAudioFocus() ใน Callback onPlay() ของเซสชันสื่อของคุณ

  • เมื่อแอปอื่นได้โฟกัสเสียง ให้หยุดหรือหยุดเล่นชั่วคราว หรือเล่นเป็ด (กล่าวคือ ลดเสียง)

  • เมื่อการเล่นหยุด (เช่น เมื่อแอปไม่เหลือให้เล่นแล้ว) ยกเลิกโฟกัสเสียง ทำให้แอปไม่ต้องยกเลิกการโฟกัสเสียงหากผู้ใช้ หยุดการเล่นชั่วคราวแต่อาจกลับมาเล่นอีกครั้งในภายหลัง

  • ใช้ AudioAttributes เพื่ออธิบาย ประเภทเสียงที่แอปของคุณกำลังเล่น เช่น สำหรับแอปที่อ่านออกเสียง ระบุ CONTENT_TYPE_SPEECH

การโฟกัสเสียงจะได้รับการจัดการแตกต่างกันไปตามเวอร์ชันของ Android กำลังทำงาน:

Android 12 (API ระดับ 31) ขึ้นไป
การโฟกัสเสียงจัดการโดยระบบ ระบบจะบังคับให้เล่นเสียงจาก แอปที่จะค่อยๆ เบาลงเมื่ออีกแอปหนึ่งขอโฟกัสเสียง ระบบ ปิดเสียงการเล่นเสียงเมื่อมีสายเรียกเข้าได้ด้วย
Android 8.0 (API ระดับ 26) ถึง Android 11 (API ระดับ 30)
ระบบไม่ได้จัดการการโฟกัสเสียง แต่มีการเปลี่ยนแปลงบางอย่าง เปิดตัวใน Android 8.0 (API ระดับ 26)
Android 7.1 (API ระดับ 25) และต่ำกว่า
ระบบไม่ได้จัดการโฟกัสเสียง และแอปจัดการโฟกัสเสียงโดยใช้ requestAudioFocus() และ abandonAudioFocus()

โฟกัสเสียงใน Android 12 ขึ้นไป

แอปสื่อหรือแอปเกมที่ใช้การโฟกัสเสียงไม่ควรเล่นเสียงหลังจากที่แอปหายไป โฟกัส ใน Android 12 (API ระดับ 31) ขึ้นไป ระบบจะบังคับใช้ พฤติกรรมของคุณ เมื่อแอปขอโฟกัสเสียงในขณะที่อีกแอปหนึ่งมีการโฟกัสและ กำลังเล่นอยู่ ระบบจะบังคับให้แอปที่กำลังเล่นจางลง นอกจากนี้ การค่อยๆ เบาลงจะช่วยให้การเปลี่ยนภาพจากแอปหนึ่งไปยังอีกแอปหนึ่งราบรื่นขึ้น

ลักษณะการค่อยๆ เบาลงนี้จะเกิดขึ้นเมื่อเป็นไปตามเงื่อนไขต่อไปนี้

  1. แอปแรกที่กำลังเล่นอยู่ตรงตามเกณฑ์ต่อไปนี้ทั้งหมด

  2. แอปที่ 2 ขอโฟกัสเสียงด้วย AudioManager.AUDIOFOCUS_GAIN

เมื่อมีคุณสมบัติตรงตามเงื่อนไขเหล่านี้ ระบบเสียงจะค่อยๆ เบาลงในแอปแรก ที่ การค่อยๆ เลือนหายไป ระบบจะแจ้งเตือนแอปแรกที่สูญเสียโฟกัส แอป โปรแกรมเล่นจะปิดเสียงจนกว่าแอปจะขอโฟกัสเสียงอีกครั้ง

ลักษณะการทำงานของการโฟกัสเสียงที่มีอยู่

คุณควรทราบกรณีอื่นๆ ที่เกี่ยวข้องกับการเปลี่ยนเสียงด้วย โฟกัส

การลดเสียงอัตโนมัติ

การลดเสียงโดยอัตโนมัติ (ลดระดับเสียงของแอป 1 รายการชั่วคราวเพื่อให้ อีกอย่างหนึ่งอาจมีอย่างชัดเจน) ได้รับการนำมาใช้ใน Android 8.0 (API ระดับ 26)

การมีระบบที่จะนำการลดเสียงมาใช้ช่วยให้คุณไม่ต้องดำเนินการแก้ปัญหาใดๆ แอปของคุณ

การลดเสียงโดยอัตโนมัติยังเกิดขึ้นเมื่อเสียงการแจ้งเตือนจับโฟกัส จากแอปที่กำลังเล่นอยู่ ซิงค์การเริ่มเล่นการแจ้งเตือนแล้ว ด้วยการสิ้นสุดของทางลาด

การลดอัตโนมัติจะเกิดขึ้นเมื่อเป็นไปตามเงื่อนไขต่อไปนี้

  1. แอปแรกที่กำลังเล่นอยู่เป็นไปตามเกณฑ์ต่อไปนี้ทั้งหมด

  2. แอปที่ 2 ขอโฟกัสเสียงด้วย AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK

เมื่อมีคุณสมบัติตรงตามเงื่อนไขเหล่านี้ ระบบเสียงจะตัดโปรแกรมเล่นที่ใช้งานอยู่ทั้งหมดของ แอปแรกในขณะที่แอปที่สองมีสมาธิ เมื่อแอปที่สองยกเลิกการทำงาน มันจะทำให้พวกเขาไม่โฟกัส แอปแรกจะไม่ได้รับการแจ้งเตือนเมื่อโฟกัสหายไป โมเดลนี้จึงไม่ต้องดำเนินการใดๆ

โปรดทราบว่าการลดเสียงอัตโนมัติจะไม่ทำงานเมื่อผู้ใช้กำลังฟัง เนื้อหาเสียงพูด เนื่องจากผู้ใช้อาจพลาดบางโปรแกรมไป ตัวอย่างเช่น การนำทางด้วยเสียงสำหรับเส้นทางการขับขี่ไม่ได้หายไป

ปิดเสียงปัจจุบันสำหรับสายเรียกเข้า

แอปบางแอปทำงานไม่ถูกต้องและเล่นเสียงต่อในระหว่างการโทร สถานการณ์นี้จะบังคับให้ผู้ใช้ค้นหาและปิดเสียงหรือออกจากแอปที่ไม่เหมาะสมใน เพื่อฟังการสนทนา เพื่อป้องกันปัญหานี้ ระบบจะปิดเสียงจาก ขณะมีสายเข้า ระบบจะเรียกใช้ฟีเจอร์นี้เมื่อ มีการรับสายเรียกเข้าและแอปมีคุณสมบัติตรงตามเงื่อนไขต่อไปนี้

  • แอปมี AudioAttributes.USAGE_MEDIA หรือ แอตทริบิวต์การใช้งาน AudioAttributes.USAGE_GAME
  • แอปขอโฟกัสเสียง (การได้โฟกัสทั้งหมด) สำเร็จและกำลังเล่นอยู่ เสียง

หากแอปยังคงเล่นต่อในระหว่างการโทร การเล่นจะถูกปิดเสียงจนกว่า วางสาย อย่างไรก็ตาม หากแอปเริ่มเล่นระหว่างการโทร โปรแกรมเล่นจะไม่ ปิดเสียง โดยสันนิษฐานว่าผู้ใช้เริ่มเล่นโดยตั้งใจ

โฟกัสเสียงใน Android 8.0 ถึง Android 11

เริ่มตั้งแต่ Android 8.0 (API ระดับ 26) เป็นต้นไป เมื่อคุณเรียกใช้ requestAudioFocus() คุณต้องระบุพารามิเตอร์ AudioFocusRequest AudioFocusRequest มีข้อมูลเกี่ยวกับบริบทและความสามารถของเสียงของแอป ระบบใช้ข้อมูลนี้เพื่อจัดการการเพิ่มและการสูญเสียโฟกัสของเสียง โดยอัตโนมัติ หากต้องการปล่อยโฟกัสเสียง ให้เรียกใช้เมธอด abandonAudioFocusRequest() ซึ่งต้องใช้ AudioFocusRequest เป็นอาร์กิวเมนต์ด้วย ใช้ AudioFocusRequest อินสแตนซ์ทั้งเมื่อคุณขอและยกเลิกการโฟกัส

หากต้องการสร้างAudioFocusRequest ให้ใช้ AudioFocusRequest.Builder เนื่องจากคำขอโฟกัสจะต้อง ระบุประเภทของคำขอเสมอ ประเภทจะรวมอยู่ในตัวสร้าง สำหรับเครื่องมือสร้าง ใช้วิธีของเครื่องมือสร้างเพื่อตั้งค่าฟิลด์อื่นๆ ของฟิลด์ อีกครั้ง

ต้องกรอกข้อมูลในช่อง FocusGain ฟิลด์อื่นๆ ทั้งหมดเป็นตัวเลือก

วิธีการหมายเหตุ
setFocusGain() ต้องระบุฟิลด์นี้ในทุกคำขอ ซึ่งจะใช้ค่าเดียวกับ durationHint ที่ใช้ในการโทรก่อน Android 8.0 ไปยัง requestAudioFocus(): AUDIOFOCUS_GAIN AUDIOFOCUS_GAIN_TRANSIENT AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK หรือ AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
setAudioAttributes() AudioAttributes อธิบายกรณีการใช้งานแอปของคุณ ระบบจะตรวจจับเสียงเหล่านั้นเมื่อแอปเพิ่มและไม่โฟกัสเสียง ลักษณะ จะมาแทนที่หัวข้อเรื่องประเภทสตรีม ใน Android 8.0 (API ระดับ 26) ขึ้นไป ประเภทสตรีมสำหรับการดำเนินการอื่นๆ นอกเหนือจากการควบคุมระดับเสียงเลิกใช้งานแล้ว ใช้ แอตทริบิวต์เดียวกันกับในคำขอโฟกัสที่คุณใช้ในโปรแกรมเล่นเสียง (เช่น ที่แสดงในตัวอย่างถัดจากตารางนี้)

ใช้ AudioAttributes.Builder เพื่อระบุ ก่อนแล้วจึงใช้วิธีนี้ในการกำหนดแอตทริบิวต์ให้กับ อีกครั้ง

หากไม่ได้ระบุไว้ AudioAttributes จะมีค่าเริ่มต้นเป็น AudioAttributes.USAGE_MEDIA

setWillPauseWhenDucked() เมื่อแอปอื่นขอให้โฟกัสด้วย AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK แอปที่มีโฟกัสไม่ มักจะได้รับ วันที่ onAudioFocusChange() Callback เนื่องจากระบบสามารถดำเนินการ ลดตัวเองได้ เมื่อคุณต้องการหยุดเล่นชั่วคราว ลดเสียงลง โทรหา setWillPauseWhenDucked(true) เพื่อสร้างและตั้งค่า OnAudioFocusChangeListener ตามที่อธิบายไว้ในอัตโนมัติ การลดการใช้งาน
setAcceptsDelayedFocusGain() คำขอโฟกัสเสียงอาจล้มเหลวเมื่อโฟกัสถูกล็อกโดยแอปอื่น วิธีนี้เปิดใช้การได้โฟกัสแบบหน่วงเวลา: ความสามารถ เพื่อให้ได้รับโฟกัสแบบไม่พร้อมกันเมื่อพร้อมใช้งาน

โปรดทราบว่าการรับโฟกัสแบบหน่วงเวลาจะใช้ได้ต่อเมื่อคุณระบุ AudioManager.OnAudioFocusChangeListener ในคำขอเสียง เนื่องจากแอปของคุณต้อง รับ Callback เพื่อให้ทราบว่าได้โฟกัสแล้ว

setOnAudioFocusChangeListener() คุณต้องระบุ OnAudioFocusChangeListener ในกรณีที่คุณระบุด้วยเท่านั้น willPauseWhenDucked(true) หรือ setAcceptsDelayedFocusGain(true) ในคำขอ

การตั้งค่าผู้ฟังมี 2 วิธี ได้แก่ วิธีหนึ่งแบบมีและไม่มี อาร์กิวเมนต์ของเครื่องจัดการ เครื่องจัดการคือเทรดที่ Listener ทำงาน หากคุณ ไม่ระบุเครื่องจัดการ แต่เครื่องจัดการที่เชื่อมโยงกับ ใช้งาน Looper

ตัวอย่างต่อไปนี้จะแสดงวิธีใช้ AudioFocusRequest.Builder เพื่อสร้าง AudioFocusRequest แล้วส่งคำขอและยกเลิกการโฟกัสเสียง:

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

การลดเสียงอัตโนมัติ

ใน Android 8.0 (API ระดับ 26) เมื่อแอปอื่นขอโฟกัสด้วย AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK ระบบจะลดและคืนค่าระดับเสียงได้ โดยไม่ต้องเรียกใช้ Callback onAudioFocusChange() ของแอป

ขณะที่การลดเสียงอัตโนมัติเป็นลักษณะการทำงานที่ยอมรับได้สำหรับการเล่นเพลงและวิดีโอ แอป ฟีเจอร์นี้จะไม่เป็นประโยชน์เมื่อเล่นเนื้อหาที่พูด เช่น แอปหนังสือเสียง ในกรณีนี้ แอปควรหยุดชั่วคราวแทน

หากคุณต้องการให้แอปหยุดชั่วคราวเมื่อระบบขอให้ลดเสียงแทนที่จะลดระดับเสียง ให้สร้าง OnAudioFocusChangeListener ด้วย เมธอด onAudioFocusChange() ของ Callback ที่ใช้ลักษณะการหยุดชั่วคราว/กลับมาทำงานอีกครั้งที่ต้องการ โทร setOnAudioFocusChangeListener() เพื่อลงทะเบียน Listener และโทร setWillPauseWhenDucked(true) เพื่อบอกระบบให้ใช้ Callback แทนการลดโดยอัตโนมัติ

เพิ่มโฟกัสแบบหน่วงเวลา

บางครั้งระบบไม่สามารถให้สิทธิ์กับคำขอโฟกัสเสียงได้เนื่องจากโฟกัสอยู่ที่ "ล็อก" แอปอื่น เช่น ระหว่างการโทร ในกรณีนี้ requestAudioFocus() แสดงผล AUDIOFOCUS_REQUEST_FAILED เมื่อเกิดเหตุการณ์นี้ขึ้น แอปของคุณไม่ควรเล่นเสียงต่อเนื่องจากไม่ได้รับ โฟกัส

เมธอด setAcceptsDelayedFocusGain(true) ซึ่งช่วยให้แอปของคุณจัดการคำขอโฟกัสได้ แบบไม่พร้อมกัน เมื่อตั้งค่าธงนี้แล้ว ระบบจะส่งคำขอเมื่อโฟกัสล็อกอยู่ แสดงผล AUDIOFOCUS_REQUEST_DELAYED เมื่อเงื่อนไขที่ล็อกเสียง โฟกัสไม่ได้อีกต่อไป เช่น เมื่อการโทรศัพท์สิ้นสุดลง อนุมัติคำขอโฟกัสที่รออนุมัติ และเรียกใช้ onAudioFocusChange() เพื่อแจ้ง แอป

ในการจัดการการโฟกัสที่ล่าช้า คุณต้องสร้าง OnAudioFocusChangeListener ที่มีเมธอด Callback onAudioFocusChange() ที่ นำพฤติกรรมที่ต้องการไปใช้และลงทะเบียน Listener โดยการเรียกใช้ setOnAudioFocusChangeListener()

โฟกัสเสียงใน Android 7.1 และต่ำกว่า

เมื่อคุณโทร requestAudioFocus() คุณต้องระบุคำแนะนำระยะเวลา ซึ่งอาจ ได้รับการยกย่องจากแอปอื่นที่กำลังมีสมาธิอยู่กับการเล่น:

  • ขอโฟกัสเสียงถาวร (AUDIOFOCUS_GAIN) เมื่อคุณวางแผนที่จะเล่นเสียง สำหรับอนาคตอันใกล้ (เช่น เมื่อเล่นเพลง) และคาดหวังว่า ที่ยึดโฟกัสเสียงก่อนหน้าเพื่อหยุดเล่น
  • ขอโฟกัสชั่วคราว (AUDIOFOCUS_GAIN_TRANSIENT) เมื่อคุณคาดว่าจะเล่น เสียงในช่วงเวลาสั้นๆ และคุณคาดว่าเจ้าของคนก่อนหน้าจะหยุดชั่วคราว กำลังเล่น
  • ขอโฟกัสชั่วคราวด้วยการลด (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) เพื่อระบุว่าคุณคาดว่าจะเล่นเสียง ในช่วงเวลาสั้นๆ และเจ้าของโฟกัสคนก่อนหน้าเก็บเอาไว้ได้ กำลังเล่นหากเป็น "เป็ด" (ลด) เอาต์พุตเสียง เอาต์พุตเสียงทั้ง 2 แบบผสมกัน ไปยังสตรีมเสียง การลดเสียงเหมาะอย่างยิ่งสำหรับแอปที่ใช้ เสียงขาดๆ หายๆ เช่น เมื่อมีเสียงเส้นทางการขับขี่

เมธอด requestAudioFocus() ต้องใช้ AudioManager.OnAudioFocusChangeListener ด้วย Listener นี้ควร ที่สร้างในกิจกรรมหรือบริการเดียวกับที่เป็นเจ้าของเซสชันสื่อของคุณ ทั้งนี้ ใช้ Callback onAudioFocusChange() ที่แอปของคุณได้รับเมื่อ บางแอปได้ผู้ใช้ใหม่หรือเลิกใช้การโฟกัสเสียง

ข้อมูลโค้ดต่อไปนี้ขอให้สตรีมมีเสียงแบบถาวร STREAM_MUSIC และลงทะเบียน OnAudioFocusChangeListener เพื่อจัดการ การเปลี่ยนแปลงด้านโฟกัสของเสียงที่ตามมา (จะมีการพูดคุยกันเกี่ยวกับ Listener การเปลี่ยนแปลง การตอบสนองต่อการเปลี่ยนโฟกัสเสียง)

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
}

เมื่อเล่นเสร็จ ให้โทร abandonAudioFocus()

Kotlin

audioManager.abandonAudioFocus(afChangeListener)

Java

// Abandon audio focus when playback complete
audioManager.abandonAudioFocus(afChangeListener);

การดำเนินการนี้จะแจ้งให้ระบบทราบว่าคุณไม่ต้องโฟกัสอีกต่อไปและยกเลิกการลงทะเบียน OnAudioFocusChangeListener ที่เกี่ยวข้อง ถ้าคุณขอโฟกัสชั่วคราว การดำเนินการนี้จะแจ้งเตือนแอปที่หยุดชั่วคราวหรือปิดไปแล้วว่าแอปอาจเล่นต่อไปหรือ เพื่อคืนค่าระดับเสียง

การตอบสนองต่อการเปลี่ยนการโฟกัสเสียง

เมื่อแอปได้รับโฟกัสเสียง แอปจะต้องปล่อยได้เมื่อแอปอื่น ขอโฟกัสเสียงเอง ในกรณีนี้ แอปของคุณ รับสายที่โทรไปยัง onAudioFocusChange() ใน AudioFocusChangeListener ที่คุณระบุเมื่อแอปชื่อว่า requestAudioFocus()

พารามิเตอร์ focusChange ที่ส่งผ่านไปยัง onAudioFocusChange() จะระบุชนิด การเปลี่ยนแปลงที่กำลังเกิดขึ้น ซึ่งตรงกับ กับคำแนะนำระยะเวลาที่ใช้โดยแอปที่กำลังโฟกัส แอปของคุณควร ตอบสนองอย่างเหมาะสม

สูญเสียโฟกัสชั่วคราว
หากการเปลี่ยนโฟกัสเป็นแบบชั่วคราว (AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK หรือ AUDIOFOCUS_LOSS_TRANSIENT) แอปของคุณก็ควรเป็นช่องโหว่ (หากคุณไม่ได้พึ่งพา ในการลดเสียงอัตโนมัติ) หรือหยุดเล่นชั่วคราวแต่ ไม่เช่นนั้น ให้คงสถานะเดิมไว้

ในระหว่างที่สูญเสียโฟกัสเสียงชั่วคราว คุณควรตรวจสอบการเปลี่ยนแปลงต่อไป โฟกัสเสียงและเตรียมตัวกลับมาเล่นแบบปกติเมื่อคุณกลับมาเล่น โฟกัส เมื่อแอปที่บล็อกเลิกโฟกัส คุณจะได้รับ Callback (AUDIOFOCUS_GAIN) ณ จุดนี้ คุณสามารถคืนค่าระดับเสียงเป็นระดับปกติได้ หรือเริ่มเล่นใหม่

สูญเสียโฟกัสถาวร
หากการสูญเสียโฟกัสเสียงเป็นแบบถาวร (AUDIOFOCUS_LOSS) แอปอื่น กำลังเล่นเสียง แอปของคุณควรหยุดเล่นชั่วคราวทันที เนื่องจากจะไม่เกิดขึ้น ได้รับการติดต่อกลับจาก AUDIOFOCUS_GAIN หากต้องการเริ่มเล่นอีกครั้ง ผู้ใช้ ต้องดำเนินการอย่างชัดเจน เช่น กดปุ่มควบคุมการส่งเล่น ในการแจ้งเตือนหรือ UI ของแอป

ข้อมูลโค้ดต่อไปนี้แสดงวิธีนำแท็ก OnAudioFocusChangeListener และ onAudioFocusChange() ของ Callback โปรดสังเกต ใช้ Handler เพื่อชะลอการเรียกกลับเมื่อสูญเสียเสียงอย่างถาวร โฟกัส

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

เครื่องจัดการจะใช้ Runnable ซึ่งมีลักษณะดังนี้

Kotlin

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

Java

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

เพื่อให้มั่นใจว่าการหยุดแบบล่าช้าจะไม่เริ่มขึ้นหากผู้ใช้เริ่มเล่นอีกครั้ง โปรดโทร mHandler.removeCallbacks(mDelayedStopRunnable)เพื่อตอบสนองต่อสถานะใดๆ การเปลี่ยนแปลง ตัวอย่างเช่น โทรหา removeCallbacks() ใน onPlay() ของ Callback onSkipToNext() ฯลฯ คุณควรเรียกใช้เมธอดนี้ในบริการของ onDestroy() Callback เมื่อล้างทรัพยากรที่บริการของคุณใช้