แอป Android ตั้งแต่ 2 แอปขึ้นไปสามารถเล่นเสียงไปยังสตรีมเอาต์พุตเดียวกัน พร้อมกันได้ และระบบจะมิกซ์ทุกอย่างเข้าด้วยกัน แม้ว่าในทางเทคนิคแล้วการดำเนินการนี้จะน่าประทับใจ แต่ก็อาจทำให้ผู้ใช้รู้สึกหงุดหงิดอย่างมาก Android ได้นำแนวคิดเรื่องโฟกัสเสียงมาใช้เพื่อหลีกเลี่ยงไม่ให้แอปเพลงทุกแอปเล่นพร้อมกัน โดยจะมีแอปที่ถือครองโฟกัสเสียงได้ทีละแอปเท่านั้น
เมื่อแอปต้องการส่งเสียงออก ควรขอโฟกัสเสียง เมื่อมี โฟกัส จะเล่นเสียงได้ อย่างไรก็ตาม หลังจากได้รับโฟกัสเสียงแล้ว คุณอาจไม่สามารถ รักษาโฟกัสเสียงไว้ได้จนกว่าจะเล่นเสร็จ แอปอื่นสามารถขอโฟกัสได้ ซึ่งจะ ขัดจังหวะการถือครองโฟกัสเสียงของคุณ หากเกิดกรณีดังกล่าว แอปควรหยุดเล่นหรือลดระดับเสียงเพื่อให้ผู้ใช้ได้ยินแหล่งเสียงใหม่ได้ง่ายขึ้น
ก่อน 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) ขึ้นไป ระบบจะบังคับใช้ลักษณะการทำงานนี้ เมื่อแอปขอโฟกัสเสียงในขณะที่แอปอื่นมีโฟกัสและ กำลังเล่นอยู่ ระบบจะบังคับให้แอปที่เล่นอยู่ค่อยๆ ลดเสียงลง การเพิ่ม การจางออกจะช่วยให้การเปลี่ยนจากแอปหนึ่งไปยังอีกแอปหนึ่งราบรื่นยิ่งขึ้น
ลักษณะการจางหายนี้จะเกิดขึ้นเมื่อตรงตามเงื่อนไขต่อไปนี้
แอปที่เล่นอยู่แอปแรกต้องมีคุณสมบัติตรงตามเกณฑ์ต่อไปนี้
- แอปมีแอตทริบิวต์การใช้งาน
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 ไม่ได้โฟกัสแล้ว ระบบจะยกเลิกการซ่อนแอป ระบบจะไม่แจ้งเตือนแอปแรกเมื่อแอปดังกล่าวไม่ได้โฟกัส จึงไม่ต้องดำเนินการใดๆ
โปรดทราบว่าระบบจะไม่ลดระดับเสียงเพลงโดยอัตโนมัติเมื่อผู้ใช้ฟังเนื้อหาที่เป็นคำพูด เนื่องจากผู้ใช้อาจพลาดบางส่วนของรายการ เช่น ระบบจะไม่ลดระดับเสียงการนำทางด้วยเสียงสำหรับเส้นทางการขับรถ
ปิดเสียงที่กำลังเล่นอยู่เมื่อมีสายโทรเข้า
แอปบางแอปอาจทำงานไม่ถูกต้องและเล่นเสียงต่อไปในระหว่างการโทร ในกรณีนี้ ผู้ใช้จะต้องค้นหาและปิดเสียงหรือออกจากแอปที่ทำให้เกิดปัญหาเพื่อที่จะได้ยินเสียงการโทร ระบบสามารถปิดเสียงจากแอปอื่นๆ ขณะมีสายเรียกเข้าเพื่อป้องกันปัญหานี้ ระบบจะเรียกใช้ฟีเจอร์นี้เมื่อได้รับ a สายโทรศัพท์เรียกเข้าและแอปมีคุณสมบัติตรงตามเงื่อนไขต่อไปนี้
- แอปมีแอตทริบิวต์การใช้งาน
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
เนื่องจากคำขอโฟกัสต้องระบุประเภทคำขอเสมอ ประเภทจึงรวมอยู่ในตัวสร้าง
สำหรับเครื่องมือสร้าง ใช้วิธีการของ Builder เพื่อตั้งค่าฟิลด์อื่นๆ ของคำขอ
ต้องระบุข้อมูลในช่อง FocusGain
ส่วนช่องอื่นๆ ทั้งหมดไม่บังคับ
วิธีการ | หมายเหตุ |
---|---|
setFocusGain()
|
ต้องระบุข้อมูลในช่องนี้ในทุกคำขอ โดยจะใช้ค่าเดียวกับ durationHint ที่ใช้ในการเรียก requestAudioFocus() ก่อน Android 8.0 ดังนี้
AUDIOFOCUS_GAIN , AUDIOFOCUS_GAIN_TRANSIENT ,
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK หรือ AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
|
setAudioAttributes()
|
AudioAttributes อธิบาย Use Case สำหรับแอปของคุณ ระบบจะพิจารณา Use Case เหล่านี้เมื่อแอปได้รับและสูญเสียโฟกัสเสียง แอตทริบิวต์
จะแทนที่แนวคิดเรื่องประเภทสตรีม ใน Android 8.0 (API ระดับ 26) ขึ้นไป
ระบบจะเลิกใช้งานประเภทสตรีมสำหรับการดำเนินการอื่นๆ นอกเหนือจากการควบคุมระดับเสียง ใช้แอตทริบิวต์เดียวกันในคำขอโฟกัสกับที่ใช้ในเครื่องเล่นเสียง (ดังที่แสดงในตัวอย่างหลังตารางนี้)
ใช้
หากไม่ได้ระบุไว้ |
setWillPauseWhenDucked()
|
เมื่อแอปอื่นขอโฟกัสด้วย
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK โดยปกติแล้วแอปที่มีโฟกัสจะไม่ได้รับ
onAudioFocusChange()
การเรียกกลับเนื่องจากระบบลดระดับเสียงได้ด้วยตัวเอง หากต้องการหยุดเล่นชั่วคราวแทนที่จะลดระดับเสียง ให้เรียกใช้ setWillPauseWhenDucked(true) และสร้างและตั้งค่า OnAudioFocusChangeListener ตามที่อธิบายไว้ในการลดระดับเสียงอัตโนมัติ
|
setAcceptsDelayedFocusGain()
|
คำขอโฟกัสเสียงอาจไม่สำเร็จเมื่อแอปอื่นล็อกโฟกัส
เมธอดนี้ช่วยให้ได้รับโฟกัสล่าช้า ซึ่งเป็นความสามารถ
ในการรับโฟกัสแบบไม่พร้อมกันเมื่อพร้อมใช้งาน
โปรดทราบว่าการรับโฟกัสที่ล่าช้าจะทำงานได้ก็ต่อเมื่อคุณระบุ
|
setOnAudioFocusChangeListener()
|
คุณต้องระบุ OnAudioFocusChangeListener ก็ต่อเมื่อระบุ willPauseWhenDucked(true) หรือ setAcceptsDelayedFocusGain(true) ในคำขอด้วย
การตั้งค่า Listener ทำได้ 2 วิธี ได้แก่ วิธีที่มีและไม่มีอาร์กิวเมนต์ตัวแฮนเดิล แฮนเดิลคือเธรดที่ Listener ทำงานอยู่ หากคุณไม่ได้ระบุแฮนเดิล ระบบจะใช้แฮนเดิลที่เชื่อมโยงกับ |
ตัวอย่างต่อไปนี้แสดงวิธีใช้ 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
ระบบจะลดระดับเสียงและคืนค่าระดับเสียงonAudioFocusChange()
ได้โดยไม่ต้องเรียกใช้แฮนเดิล onAudioFocusChange()
ของแอป
แม้ว่าการดั๊กอัตโนมัติจะเป็นลักษณะการทำงานที่ยอมรับได้สำหรับแอปเล่นเพลงและวิดีโอ แต่จะไม่มีประโยชน์เมื่อเล่นเนื้อหาที่เป็นคำพูด เช่น ใน แอปหนังสือเสียง ในกรณีนี้ แอปควรหยุดชั่วคราวแทน
หากต้องการให้แอปหยุดชั่วคราวเมื่อได้รับแจ้งให้หลบเสียงแทนที่จะลดระดับเสียง ให้สร้าง OnAudioFocusChangeListener
ด้วยเมธอดเรียกกลับ onAudioFocusChange()
ที่ใช้ลักษณะการทำงานของการหยุดชั่วคราว/เล่นต่อที่ต้องการ
โทร setOnAudioFocusChangeListener()
เพื่อลงทะเบียนผู้ฟัง และโทร
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 นี้ในกิจกรรมหรือบริการเดียวกันกับที่เป็นเจ้าของเซสชันสื่อ โดยจะใช้การเรียกกลับ onAudioFocusChange()
ที่แอปของคุณได้รับเมื่อ
แอปอื่นได้รับหรือละทิ้งโฟกัสเสียง
ข้อมูลโค้ดต่อไปนี้ขอโฟกัสเสียงถาวรในสตรีม
STREAM_MUSIC
และลงทะเบียน OnAudioFocusChangeListener
เพื่อจัดการ
การเปลี่ยนแปลงโฟกัสเสียงในภายหลัง (เราจะพูดถึงเครื่องมือฟังการเปลี่ยนแปลงใน
การตอบสนองต่อการเปลี่ยนแปลงโฟกัสเสียง)
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()
เป็นต้น คุณควรเรียกใช้เมธอดนี้ใน Callback ของ onDestroy()
บริการด้วยเมื่อล้างข้อมูลทรัพยากรที่บริการใช้