แอป Android ตั้งแต่ 2 แอปขึ้นไปสามารถเล่นเสียงไปยังสตรีมเอาต์พุตเดียวกันได้พร้อมกัน และระบบจะมิกซ์เสียงทั้งหมดเข้าด้วยกัน แม้ว่าจะเป็นเรื่องที่น่าประทับใจในทางเทคนิค แต่ก็อาจทำให้ผู้ใช้รู้สึกหงุดหงิดได้ Android จึงนำแนวคิดเรื่อง โฟกัสเสียง มาใช้เพื่อป้องกันไม่ให้แอปเพลงทุกแอปเล่นพร้อมกัน โดยจะมีเพียงแอปเดียวเท่านั้นที่สามารถมีโฟกัสเสียงได้ในแต่ละครั้ง
เมื่อแอปของคุณต้องการส่งออกเสียง แอปควรขอโฟกัสเสียง เมื่อมีโฟกัสแล้ว แอปจะสามารถเล่นเสียงได้ อย่างไรก็ตาม หลังจากได้รับโฟกัสเสียงแล้ว คุณอาจไม่สามารถเก็บโฟกัสเสียงไว้ได้จนกว่าจะเล่นเสร็จ แอปอื่นสามารถขอโฟกัสได้ ซึ่งจะแทนที่การถือครองโฟกัสเสียงของคุณ หากเกิดกรณีดังกล่าว แอปของคุณควรหยุดเล่นชั่วคราวหรือลดระดับเสียงลงเพื่อให้ผู้ใช้ได้ยินแหล่งเสียงใหม่ได้ง่ายขึ้น
ก่อน Android 12 (API ระดับ 31) ระบบจะไม่ได้จัดการโฟกัสเสียง ดังนั้น แม้ว่าเราจะสนับสนุนให้นักพัฒนาแอปปฏิบัติตามหลักเกณฑ์เกี่ยวกับโฟกัสเสียง แต่หากแอปยังคงเล่นเสียงดังแม้หลังจากสูญเสียโฟกัสเสียงในอุปกรณ์ที่ใช้ Android 11 (ระดับ API 30) หรือต่ำกว่า ระบบก็ไม่สามารถป้องกันได้ อย่างไรก็ตาม ลักษณะการทำงานของแอปเช่นนี้จะทำให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดีและอาจทำให้ผู้ใช้ถอนการติดตั้งแอปที่ทำงานผิดพลาด
แอปเสียงที่ออกแบบมาอย่างดีควรจัดการโฟกัสเสียงตามหลักเกณฑ์ทั่วไปต่อไปนี้
เรียกใช้
requestAudioFocus()ทันทีก่อนเริ่มเล่นและตรวจสอบว่า การเรียกใช้ส่งคืนAUDIOFOCUS_REQUEST_GRANTEDเรียกใช้requestAudioFocus()ในการเรียกกลับ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) ขึ้นไป ระบบจะบังคับใช้ลักษณะการทำงานนี้ เมื่อแอปขอโฟกัสเสียงขณะที่แอปอื่นมีโฟกัสและกำลังเล่นอยู่ ระบบจะบังคับให้แอปที่กำลังเล่นค่อยๆ ลดระดับเสียงลง การเพิ่มการค่อยๆ ลดระดับเสียงลงจะช่วยให้การเปลี่ยนจากแอปหนึ่งไปยังอีกแอปหนึ่งเป็นไปอย่างราบรื่นมากขึ้น
ลักษณะการทำงานของการค่อยๆ ลดระดับเสียงลงจะเกิดขึ้นเมื่อตรงตามเงื่อนไขต่อไปนี้
แอปแรกที่กำลังเล่นอยู่ตรงตามเกณฑ์ทั้งหมดต่อไปนี้
- แอปมีแอตทริบิวต์การใช้งาน
AudioAttributes.USAGE_MEDIAหรือAudioAttributes.USAGE_GAME - แอปขอโฟกัสเสียงด้วย
AudioManager.AUDIOFOCUS_GAINสำเร็จ - แอปไม่ได้เล่นเสียงที่มีประเภทเนื้อหา
AudioAttributes.CONTENT_TYPE_SPEECH
- แอปมีแอตทริบิวต์การใช้งาน
แอปที่ 2 ขอโฟกัสเสียงด้วย
AudioManager.AUDIOFOCUS_GAIN
เมื่อตรงตามเงื่อนไขเหล่านี้ ระบบเสียงจะค่อยๆ ลดระดับเสียงของแอปแรกลง และเมื่อสิ้นสุดการค่อยๆ ลดระดับเสียงลง ระบบจะแจ้งให้แอปแรกทราบว่าสูญเสียโฟกัส เพลเยอร์ของแอปจะยังคงปิดเสียงอยู่จนกว่าแอปจะขอโฟกัสเสียงอีกครั้ง
ลักษณะการทำงานของโฟกัสเสียงที่มีอยู่
คุณควรทราบถึงกรณีอื่นๆ ที่เกี่ยวข้องกับการเปลี่ยนโฟกัสเสียงด้วย
การลดเสียงอัตโนมัติ
การลดเสียงอัตโนมัติ (การลดระดับเสียงของแอปหนึ่งลงชั่วคราวเพื่อให้ได้ยินเสียงของอีกแอปหนึ่งอย่างชัดเจน) เริ่มใช้ใน Android 8.0 (ระดับ API 26)
การให้ระบบดำเนินการลดเสียงจะช่วยให้คุณไม่ต้องดำเนินการลดเสียงในแอป
การลดเสียงอัตโนมัติจะเกิดขึ้นเมื่อการแจ้งเตือนเสียงดึงโฟกัสจากแอปที่กำลังเล่นอยู่ โดยการเริ่มเล่นการแจ้งเตือนจะซิงค์กับการสิ้นสุดการค่อยๆ ลดระดับเสียงลง
การลดเสียงอัตโนมัติจะเกิดขึ้นเมื่อตรงตามเงื่อนไขต่อไปนี้
แอปแรกที่กำลังเล่นอยู่ตรงตามเกณฑ์ทั้งหมดต่อไปนี้
- แอปขอโฟกัสเสียงด้วยการเพิ่มโฟกัสประเภทใดก็ได้สำเร็จ
- แอปไม่ได้เล่นเสียงที่มีประเภทเนื้อหา
AudioAttributes.CONTENT_TYPE_SPEECH - แอปไม่ได้ตั้งค่า
AudioFocusRequest.Builder.setWillPauseWhenDucked(true)
แอปที่ 2 ขอโฟกัสเสียงด้วย
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
เมื่อตรงตามเงื่อนไขเหล่านี้ ระบบเสียงจะลดเสียงเพลเยอร์ที่ใช้งานอยู่ทั้งหมดของแอปแรกขณะที่แอปที่ 2 มีโฟกัส เมื่อแอปที่ 2 ละทิ้งโฟกัส ระบบจะคืนระดับเสียงเดิมให้เพลเยอร์ แอปแรกจะไม่ได้รับการแจ้งเตือนเมื่อสูญเสียโฟกัส จึงไม่จำเป็นต้องดำเนินการใดๆ
โปรดทราบว่าระบบจะไม่ลดเสียงอัตโนมัติเมื่อผู้ใช้กำลังฟังเนื้อหาเสียงพูด เนื่องจากผู้ใช้อาจพลาดเนื้อหาบางส่วนของโปรแกรม เช่น ระบบจะไม่ลดเสียงคำแนะนำด้วยเสียงสำหรับการนำทาง
ปิดเสียงการเล่นเสียงปัจจุบันเมื่อมีสายเรียกเข้า
แอปบางแอปทำงานไม่ถูกต้องและยังคงเล่นเสียงระหว่างการโทร สถานการณ์นี้บังคับให้ผู้ใช้ต้องค้นหาและปิดเสียงหรือออกจากแอปที่มีปัญหาเพื่อให้ได้ยินเสียงการโทร ระบบจึงสามารถปิดเสียงจากแอปอื่นๆ ขณะมีสายเรียกเข้าเพื่อป้องกันไม่ให้เกิดเหตุการณ์ดังกล่าว ระบบจะเรียกใช้ฟีเจอร์นี้เมื่อได้รับสายเรียกเข้าและแอปเป็นไปตามเงื่อนไขต่อไปนี้
- แอปมีแอตทริบิวต์การใช้งาน
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 ที่ใช้ในการเรียกใช้ requestAudioFocus() ก่อน Android 8.0 ได้แก่
AUDIOFOCUS_GAIN, AUDIOFOCUS_GAIN_TRANSIENT,
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK หรือ AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
|
setAudioAttributes()
|
AudioAttributes อธิบายกรณีการใช้งานสำหรับแอปของคุณ ระบบจะดูแอตทริบิวต์เหล่านี้เมื่อแอปได้รับและสูญเสียโฟกัสเสียง แอตทริบิวต์จะแทนที่แนวคิดเรื่องประเภทสตรีม ใน Android 8.0 (ระดับ API 26) ขึ้นไป ระบบจะเลิกใช้งานประเภทสตรีมสำหรับการดำเนินการอื่นๆ นอกเหนือจากการควบคุมระดับเสียง ใช้แอตทริบิวต์เดียวกันในคำขอโฟกัสกับที่ใช้ในเพลเยอร์เสียง (ตามที่แสดงในตัวอย่างหลังตารางนี้)
ใช้
หากไม่ได้ระบุไว้ |
setWillPauseWhenDucked()
|
เมื่อแอปอื่นขอโฟกัสด้วย
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK แอปที่มีโฟกัสมักจะไม่
ได้รับการเรียกกลับ
onAudioFocusChange()
เนื่องจากระบบสามารถลดเสียงได้ด้วยตัวเอง เมื่อคุณต้องการหยุดเล่นชั่วคราวแทนที่จะลดระดับเสียง ให้เรียกใช้ setWillPauseWhenDucked(true) และสร้างและตั้งค่า OnAudioFocusChangeListener ตามที่อธิบายไว้ในการลดเสียงอัตโนมัติ
|
setAcceptsDelayedFocusGain()
|
คำขอโฟกัสเสียงอาจล้มเหลวเมื่อแอปอื่นล็อกโฟกัส
เมธอดนี้จะเปิดใช้การเพิ่มโฟกัสที่ล่าช้า ซึ่งเป็นความสามารถ
ในการรับโฟกัสแบบไม่พร้อมกันเมื่อพร้อมใช้งาน
โปรดทราบว่าการเพิ่มโฟกัสที่ล่าช้าจะทำงานได้ก็ต่อเมื่อคุณระบุ |
setOnAudioFocusChangeListener()
|
OnAudioFocusChangeListener จำเป็นก็ต่อเมื่อคุณระบุ willPauseWhenDucked(true) หรือ setAcceptsDelayedFocusGain(true) ในคำขอด้วย
การตั้งค่า Listener มี 2 วิธี ได้แก่ วิธีที่มีอาร์กิวเมนต์ Handler และวิธีที่ไม่มี Handler คือเธรดที่ Listener ทำงาน หากคุณไม่ได้ระบุ Handler ระบบจะใช้ Handler ที่เชื่อมโยงกับ |
ตัวอย่างต่อไปนี้แสดงวิธีใช้ AudioFocusRequest.Builder เพื่อสร้าง
an 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 ระบบจะลดและคืนระดับเสียงได้โดยไม่ต้องเรียกใช้การเรียกกลับ onAudioFocusChange() ของแอป
แม้ว่าการลดเสียงอัตโนมัติจะเป็นลักษณะการทำงานที่ยอมรับได้สำหรับแอปเล่นเพลงและวิดีโอ แต่ก็ไม่มีประโยชน์เมื่อเล่นเนื้อหาเสียงพูด เช่น ในแอปหนังสือเสียง ในกรณีนี้ แอปควรหยุดเล่นชั่วคราวแทน
หากต้องการให้แอปหยุดเล่นชั่วคราวเมื่อได้รับคำขอให้ลดเสียงแทนที่จะลดระดับเสียง ให้สร้าง OnAudioFocusChangeListener ที่มีเมธอด Callback onAudioFocusChange() ซึ่งใช้ลักษณะการทำงานของการหยุดเล่นชั่วคราว/เล่นต่อที่ต้องการ
เรียกใช้ setOnAudioFocusChangeListener() เพื่อลงทะเบียน Listener และเรียกใช้
setWillPauseWhenDucked(true)
เพื่อบอกให้ระบบใช้การเรียกกลับของคุณแทนที่จะลดเสียงอัตโนมัติ
การเพิ่มโฟกัสที่ล่าช้า
บางครั้งระบบไม่สามารถให้คำขอโฟกัสเสียงได้เนื่องจากแอปอื่น "ล็อก" โฟกัสไว้ เช่น ระหว่างการโทร ในกรณีนี้ 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 นี้ในกิจกรรมหรือบริการเดียวกันกับที่เป็นเจ้าของเซสชันสื่อ Listener จะใช้การเรียกกลับ 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) แอปของคุณควรลดเสียง (หากคุณไม่ได้ใช้ การลดเสียงอัตโนมัติ) หรือหยุดเล่นชั่วคราว แต่ ยังคงสถานะเดิมไว้ระหว่างการสูญเสียโฟกัสเสียงชั่วคราว คุณควรตรวจสอบการเปลี่ยนแปลงโฟกัสเสียงต่อไปและเตรียมพร้อมที่จะเล่นต่อตามปกติเมื่อได้รับโฟกัสอีกครั้ง เมื่อแอปที่บล็อกละทิ้งโฟกัส คุณจะได้รับการเรียกกลับ (
AUDIOFOCUS_GAIN) ซึ่งในจุดนี้คุณสามารถคืนระดับเสียงเดิมหรือเริ่มเล่นใหม่ได้ - การสูญเสียโฟกัสถาวร
-
หากการสูญเสียโฟกัสเสียงเป็นแบบถาวร (
AUDIOFOCUS_LOSS) แสดงว่าแอปอื่นกำลังเล่นเสียงอยู่ แอปของคุณควรหยุดเล่นชั่วคราวทันที เนื่องจากจะไม่ได้รับการเรียกกลับAUDIOFOCUS_GAINหากต้องการเริ่มเล่นใหม่ ผู้ใช้ต้องดำเนินการอย่างชัดเจน เช่น กดปุ่มควบคุมการเล่นใน UI ของการแจ้งเตือนหรือแอป
ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ OnAudioFocusChangeListener และการเรียกกลับ onAudioFocusChange() โปรดสังเกตการใช้ 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 } } };
Handler ใช้ 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(), onSkipToNext() ฯลฯ ของการเรียกกลับ นอกจากนี้ คุณควรเรียกใช้เมธอดนี้ในการเรียกกลับ onDestroy() ของบริการเมื่อล้างข้อมูลทรัพยากรที่บริการใช้