แอป Android 2 แอปขึ้นไปสามารถเล่นเสียงไปยังสตรีมเอาต์พุตเดียวกันพร้อมกันได้ และระบบจะผสมเสียงทั้งหมดเข้าด้วยกัน แม้ว่าวิธีนี้จะดูน่าประทับใจทางเทคนิค แต่ก็อาจทำให้ผู้ใช้รู้สึกหงุดหงิดอย่างมาก Android นำเสนอแนวคิดโหมดโฟกัสเสียงเพื่อหลีกเลี่ยงไม่ให้แอปเพลงทุกแอปเล่นพร้อมกัน แต่จะโฟกัสเสียงได้ทีละแอปเท่านั้น
เมื่อแอปของคุณจำเป็นต้องส่งออกเสียง ก็ควรขอโฟกัสเสียง เมื่อโฟกัสอยู่ อุปกรณ์จะเล่นเสียงได้ อย่างไรก็ตาม หลังจากได้รับโฟกัสเสียงแล้ว คุณอาจไม่สามารถรักษาโฟกัสไว้จนกว่าจะเล่นเพลงจบ แอปอื่นสามารถขอโฟกัสได้ ซึ่งจะแย่งโฟกัสเสียงที่คุณถือครองอยู่ หากเป็นเช่นนั้น แอปควรหยุดเล่นชั่วคราวหรือลดระดับเสียงเพื่อให้ผู้ใช้ได้ยินแหล่งที่มาของเสียงใหม่ได้ง่ายขึ้น
ก่อน Android 12 (API ระดับ 31) ระบบจะไม่จัดการโฟกัสเสียง ดังนั้น แม้ว่าเราจะแนะนำให้นักพัฒนาแอปปฏิบัติตามหลักเกณฑ์โฟกัสเสียง แต่หากแอปเล่นเสียงดังต่อไปแม้ว่าจะสูญเสียโฟกัสเสียงในอุปกรณ์ที่ใช้ Android 11 (API ระดับ 30) หรือต่ำกว่า ระบบจะไม่สามารถป้องกันได้ อย่างไรก็ตาม ลักษณะการทํางานของแอปนี้ทําให้ผู้ใช้ได้รับประสบการณ์การใช้งานที่ไม่ดี และมักทําให้ผู้ใช้ถอนการติดตั้งแอปที่ทำงานผิดปกติ
แอปเสียงที่ออกแบบมาอย่างดีควรจัดการโฟกัสเสียงตามหลักเกณฑ์ทั่วไปต่อไปนี้
โทรหา
requestAudioFocus()
ทันทีก่อนเริ่มเล่นและตรวจสอบว่าระบบโทรกลับAUDIOFOCUS_REQUEST_GRANTED
โทรไปที่requestAudioFocus()
ใน CallbackonPlay()
ของเซสชันสื่อเมื่อแอปอื่นได้รับโฟกัสเสียง ให้หยุดหรือหยุดเล่นชั่วคราว หรือลดระดับเสียง
เมื่อการเล่นหยุดลง (เช่น เมื่อแอปไม่มีเสียงเหลือให้เล่น) ให้ยกเลิกโฟกัสเสียง แอปของคุณไม่จำเป็นต้องยกเลิกโฟกัสเสียงหากผู้ใช้หยุดเล่นชั่วคราว แต่อาจเล่นต่อในภายหลัง
ใช้
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 อธิบาย Use Case สําหรับแอปของคุณ ระบบจะดู Use Case เหล่านี้เมื่อแอปได้รับและเสียโฟกัสเสียง แอตทริบิวต์จะแทนที่แนวคิดประเภทสตรีม ใน Android 8.0 (API ระดับ 26) ขึ้นไป ระบบจะเลิกใช้งานประเภทสตรีมสำหรับการดำเนินการอื่นๆ นอกเหนือจากการควบคุมระดับเสียง ใช้แอตทริบิวต์เดียวกันในคำขอโฟกัสกับที่ใช้ในโปรแกรมเล่นเสียง (ดังที่แสดงในตัวอย่างหลังตารางนี้)
ใช้
หากไม่ได้ระบุไว้ ระบบจะใช้ |
setWillPauseWhenDucked()
|
เมื่อแอปอื่นขอโฟกัสด้วย AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK แอปที่มีโฟกัสมักจะไม่ได้รับ onAudioFocusChange() callback เนื่องจากระบบสามารถซ่อนแอปเองได้ เมื่อคุณต้องการหยุดการเล่นชั่วคราวแทนที่จะลดระดับเสียง ให้เรียกใช้ setWillPauseWhenDucked(true) แล้วสร้างและตั้งค่า OnAudioFocusChangeListener ตามที่อธิบายไว้ในการลดระดับเสียงอัตโนมัติ
|
setAcceptsDelayedFocusGain()
|
คำขอโฟกัสเสียงอาจไม่สำเร็จเมื่อแอปอื่นล็อกโฟกัสไว้
วิธีนี้เปิดใช้โฟกัสที่ล่าช้า: ความสามารถในการรับโฟกัสแบบไม่พร้อมกันเมื่อโฟกัสพร้อมใช้งาน
โปรดทราบว่าการรับโฟกัสแบบล่าช้าจะใช้งานได้ก็ต่อเมื่อคุณระบุ |
setOnAudioFocusChangeListener()
|
คุณต้องระบุ OnAudioFocusChangeListener ก็ต่อเมื่อระบุ willPauseWhenDucked(true) หรือ setAcceptsDelayedFocusGain(true) ในคำขอด้วย
การตั้งค่า Listener มี 2 วิธี ได้แก่ 1 วิธีที่มีอาร์กิวเมนต์ตัวแฮนเดิลและอีก 1 วิธีที่ไม่มีอาร์กิวเมนต์ตัวแฮนเดิล แฮนเดิลคือเธรดที่ 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()
callback ของแอป
แม้ว่าการลดเสียงอัตโนมัติจะเป็นลักษณะการทำงานที่ยอมรับได้สำหรับแอปการเล่นเพลงและวิดีโอ แต่ก็ไม่มีประโยชน์เมื่อเล่นเนื้อหาที่มีการพูด เช่น ในแอปหนังสือเสียง ในกรณีนี้ แอปควรหยุดชั่วคราวแทน
หากต้องการให้แอปหยุดชั่วคราวเมื่อระบบขอให้ลดเสียงแทนที่จะลดระดับเสียง ให้สร้างเมธอดการเรียกคืน OnAudioFocusChangeListener
ที่มี onAudioFocusChange()
ที่ใช้ลักษณะการทำงานหยุดชั่วคราว/เล่นต่อที่ต้องการ
โทร setOnAudioFocusChangeListener()
เพื่อลงทะเบียนโปรแกรมฟัง และโทร setWillPauseWhenDucked(true)
เพื่อบอกให้ระบบใช้การเรียกกลับแทนการลดเสียงอัตโนมัติ
การเพิ่มโฟกัสที่ล่าช้า
บางครั้งระบบไม่สามารถให้สิทธิ์ตามคำขอโฟกัสเสียงได้เนื่องจากแอปอื่น "ล็อก" โฟกัสไว้ เช่น ระหว่างการโทร ในกรณีนี้ requestAudioFocus()
จะแสดงผลเป็น AUDIOFOCUS_REQUEST_FAILED
ในกรณีนี้ แอปของคุณไม่ควรเล่นเสียงต่อเนื่องจากไม่ได้โฟกัส
เมธอด setAcceptsDelayedFocusGain(true)
ที่ช่วยให้แอปจัดการคําขอโฟกัสแบบไม่พร้อมกัน เมื่อตั้งค่า Flag นี้ คำขอที่ส่งเมื่อโฟกัสถูกล็อกจะแสดงผลเป็น 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
ด้วย ควรสร้างตัวรับฟังนี้ในกิจกรรมหรือบริการเดียวกันกับที่เป็นเจ้าของเซสชันสื่อ โดยจะติดตั้งใช้งานการเรียกกลับ 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
callback หากต้องการเริ่มเล่นอีกครั้ง ผู้ใช้ต้องดำเนินการอย่างชัดแจ้ง เช่น กดตัวควบคุมการเล่นในการแจ้งเตือนหรือ 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()
ของ Callback ของบริการเมื่อล้างทรัพยากรที่บริการใช้