แอป 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)
การให้ระบบใช้การดั๊กกิ้งทำให้คุณไม่ต้องใช้การดั๊กกิ้งในแอป
การลดระดับเสียงอัตโนมัติจะเกิดขึ้นเมื่อการแจ้งเตือนด้วยเสียงดึงโฟกัส จากแอปที่กำลังเล่นอยู่ด้วย โดยการเริ่มเล่นการแจ้งเตือนจะซิงค์ กับการสิ้นสุดของช่วงการลดระดับเสียง
การลดระดับเสียงอัตโนมัติจะเกิดขึ้นเมื่อตรงตามเงื่อนไขต่อไปนี้
แอปแรกที่กำลังเล่นอยู่ต้องมีคุณสมบัติตรงตามเกณฑ์ต่อไปนี้ทั้งหมด
- แอปขอโฟกัสเสียงสำเร็จแล้วโดยมีfocus gain ประเภทใดก็ได้
- แอปไม่ได้เล่นเสียงที่มีประเภทเนื้อหา
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 เนื่องจากคำขอโฟกัสต้องระบุประเภทคำขอเสมอ ประเภทจึงรวมอยู่ในตัวสร้าง
สำหรับตัวสร้าง ใช้วิธีการของเครื่องมือสร้างเพื่อตั้งค่าฟิลด์อื่นๆ ของคำขอ
ต้องระบุข้อมูลในช่อง FocusGain ส่วนช่องอื่นๆ ไม่บังคับ
| วิธีการ | หมายเหตุ |
|---|---|
setFocusGain()
|
ต้องระบุข้อมูลในช่องนี้ในทุกคำขอ โดยจะใช้ค่าเดียวกับ durationHint ที่ใช้ในการเรียกก่อน Android 8.0 ไปยัง requestAudioFocus() ดังนี้
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() ของแอป
แม้ว่าการดั๊กอัตโนมัติจะเป็นลักษณะการทำงานที่ยอมรับได้สำหรับแอปเล่นเพลงและวิดีโอ แต่จะไม่มีประโยชน์เมื่อเล่นเนื้อหาที่เป็นคำพูด เช่น ใน แอปหนังสือเสียง ในกรณีนี้ แอปควรหยุดชั่วคราวแทน
หากต้องการให้แอปหยุดชั่วคราวเมื่อได้รับแจ้งให้หลบเสียงแทนที่จะลดระดับเสียง ให้สร้าง 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() โปรดสังเกตการใช้ 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() เมื่อล้างข้อมูลทรัพยากรที่บริการใช้ด้วย