แอป 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 เนื่องจากคำขอโฟกัสต้องระบุประเภทของคำขอเสมอ ประเภทจึงรวมอยู่ในคอนสตรัคเตอร์สำหรับเครื่องมือสร้าง ใช้วิธีการของ 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 แอปที่มีโฟกัสอยู่มักจะไม่ได้รับ Callback 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 ระบบจะลดและคืนค่าระดับเสียงได้โดยไม่ต้องเรียกใช้ Callback onAudioFocusChange() ของแอป
แม้ว่าการลดระดับเสียงอัตโนมัติจะเป็นลักษณะการทำงานที่ยอมรับได้สำหรับแอปเล่นเพลงและวิดีโอ แต่ก็ไม่มีประโยชน์เมื่อเล่นเนื้อหาที่พูด เช่น ในแอปหนังสือเสียง ในกรณีนี้ แอปควรหยุดชั่วคราวแทน
หากต้องการให้แอปหยุดชั่วคราวเมื่อระบบขอให้ลดระดับเสียงแทนที่จะลดระดับเสียง ให้สร้าง OnAudioFocusChangeListener ด้วยเมธอด Callback onAudioFocusChange() ที่ใช้ลักษณะการทำงานหยุดชั่วคราว/เล่นต่อที่ต้องการ
เรียกใช้ 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) แสดงว่าแอปอื่นกำลังเล่นเสียงอยู่ แอปของคุณควรหยุดเล่นชั่วคราวทันที เนื่องจากจะไม่ได้รับ CallbackAUDIOFOCUS_GAINหากต้องการเริ่มเล่นอีกครั้ง ผู้ใช้ต้องดำเนินการอย่างชัดเจน เช่น กดตัวควบคุมการเล่นใน UI ของการแจ้งเตือนหรือแอป
ข้อมูลโค้ดต่อไปนี้แสดงวิธีใช้ OnAudioFocusChangeListener และ Callback onAudioFocusChange() โปรดสังเกตการใช้ Handler เพื่อหน่วงเวลา Callback ของการหยุดเมื่อสูญเสียโฟกัสเสียงอย่างถาวร
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(), onSkipToNext() ฯลฯ ของ Callback คุณควรเรียกใช้เมธอดนี้ใน Callback onDestroy() ของบริการด้วยเมื่อล้างทรัพยากรที่บริการใช้