שתי אפליקציות ל-Android או יותר יכולות להפעיל אודיו לאותו זרם פלט בו-זמנית, והמערכת משלבת את הכול יחד. למרות שזה מרשים מבחינה טכנית, זה יכול להיות מאוד מעצבן למשתמש. כדי למנוע מצב שבו כל אפליקציית מוזיקה פועלת בו-זמנית, מערכת 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
.
- באפליקציה מוגדר מאפיין השימוש
אפליקציה שנייה מבקשת את הרשאת האודיו עם
AudioManager.AUDIOFOCUS_GAIN
.
כשמתקיימים התנאים האלה, מערכת האודיו מעמעמת את האפליקציה הראשונה. בסוף ההנמכה, המערכת מודיעה לאפליקציה הראשונה שהיא איבדה את המיקוד. הנגנים של האפליקציה יישארו מושתקים עד שהאפליקציה תבקש שוב הרשאת אודיו.
התנהגויות קיימות של מיקוד אודיו
חשוב גם לשים לב למקרים אחרים שבהם יש שינוי במיקוד השמע.
הנמכה אוטומטית של עוצמת הקול
הנמכה אוטומטית (הפחתה זמנית של עוצמת השמע של אפליקציה אחת כדי שאפשר יהיה לשמוע בבירור אפליקציה אחרת) הוצגה ב-Android 8.0 (רמת API 26).
אם המערכת מטמיעה את ההנמכה, אתם לא צריכים להטמיע אותה באפליקציה שלכם.
הנמכה אוטומטית מתרחשת גם כשמתקבלת התראה קולית שמושכת את המיקוד מאפליקציה שפועלת. תחילת ההשמעה של ההתראה מסונכרנת עם סוף ההנמכה.
הנמכת עוצמה אוטומטית מתרחשת כשהתנאים הבאים מתקיימים:
האפליקציה הראשונה שמופעלת כרגע עומדת בכל הקריטריונים האלה:
- האפליקציה ביקשה בהצלחה התמקדות באודיו עם כל סוג של התמקדות באודיו.
- האפליקציה לא מפעילה אודיו עם סוג התוכן
AudioAttributes.CONTENT_TYPE_SPEECH
. - האפליקציה לא הגדירה את
AudioFocusRequest.Builder.setWillPauseWhenDucked(true)
.
אפליקציה שנייה מבקשת הרשאת אודיו עם
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
.
כשהתנאים האלה מתקיימים, מערכת האודיו מנמיכה את כל הנגנים הפעילים של האפליקציה הראשונה בזמן שהאפליקציה השנייה נמצאת במוקד. כשהאפליקציה השנייה מפסיקה להיות במרכז, היא מתנתקת מהמסך. האפליקציה הראשונה לא מקבלת הודעה כשהיא מאבדת את המיקוד, ולכן היא לא צריכה לעשות כלום.
שימו לב: השתקה אוטומטית לא מתבצעת כשהמשתמש מאזין לתוכן דיבור, כי יכול להיות שהוא יפספס חלק מהתוכנית. לדוגמה, הנחיות קוליות למסלולי נסיעה לא מושתקות.
השתקת האודיו שמופעל כרגע בשיחות טלפון נכנסות
חלק מהאפליקציות לא מתנהגות כמו שצריך וממשיכות להפעיל אודיו במהלך שיחות טלפון. במצב כזה, המשתמש נאלץ למצוא את האפליקציה הבעייתית, להשתיק אותה או לצאת ממנה כדי לשמוע את השיחה. כדי למנוע זאת, המערכת יכולה להשתיק את האודיו מאפליקציות אחרות בזמן שיש שיחה נכנסת. המערכת מפעילה את התכונה הזו כשמתקבלת שיחה נכנסת בטלפון ואפליקציה עומדת בתנאים הבאים:
- באפליקציה מוגדר מאפיין השימוש
AudioAttributes.USAGE_MEDIA
אוAudioAttributes.USAGE_GAME
. - האפליקציה ביקשה בהצלחה מיקוד שמע (כל השגת מיקוד) והיא משמיעה שמע.
אם ההפעלה של אפליקציה מסוימת נמשכת במהלך השיחה, היא מושתקת עד לסיום השיחה. עם זאת, אם אפליקציה מתחילה לפעול במהלך השיחה, ההפעלה שלה לא מושתקת מתוך הנחה שהמשתמש התחיל את ההפעלה בכוונה.
התמקדות באודיו ב-Android מגרסה 8.0 עד גרסה 11
החל מ-Android 8.0 (רמת API 26), כשמפעילים את הפונקציה
requestAudioFocus()
צריך לספק פרמטר AudioFocusRequest
. AudioFocusRequest
כולל מידע על ההקשר של האודיו ועל היכולות של האפליקציה. המערכת משתמשת במידע הזה כדי לנהל את העלייה והירידה של פוקוס האודיו באופן אוטומטי. כדי לשחרר את הרשאת האודיו, צריך להפעיל את ה-method 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) בבקשה.
יש שתי שיטות להגדרת מאזין: אחת עם ארגומנט של handler ואחת בלי. ה-handler הוא ה-thread שבו ה-listener פועל. אם לא מציינים handler, נעשה שימוש ב-handler שמשויך ל- |
בדוגמה הבאה מוצג שימוש ב-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
עם שיטת קריאה חוזרת onAudioFocusChange()
שמטמיעה את ההתנהגות הרצויה של השהיה והפעלה מחדש.
מתקשרים למספר setOnAudioFocusChangeListener()
כדי לרשום את המאזין, ומתקשרים למספר setWillPauseWhenDucked(true)
כדי להגדיר למערכת להשתמש בהתקשרות חזרה במקום בהנמכה אוטומטית של עוצמת השמע.
השהיית המיקוד
לפעמים המערכת לא יכולה לאשר בקשה למיקוד אודיו כי המיקוד 'נעול' על ידי אפליקציה אחרת, למשל במהלך שיחת טלפון. במקרה הזה, requestAudioFocus()
מחזירה AUDIOFOCUS_REQUEST_FAILED
. במקרה כזה, האפליקציה לא אמורה להמשיך בהפעלת האודיו כי היא לא קיבלה פוקוס.
השיטה, setAcceptsDelayedFocusGain(true)
, שמאפשרת לאפליקציה לטפל בבקשה להעברת המיקוד באופן אסינכרוני. אם הדגל הזה מוגדר, בקשה שנשלחת כשהמיקוד נעול מחזירה AUDIOFOCUS_REQUEST_DELAYED
. כשהתנאי שגרם לנעילת המיקוד באודיו כבר לא מתקיים, למשל כששיחת טלפון מסתיימת, המערכת מאשרת את בקשת המיקוד בהמתנה וקוראת ל-onAudioFocusChange()
כדי להודיע לאפליקציה.
כדי לטפל בהשהיה בהעברת המיקוד, צריך ליצור OnAudioFocusChangeListener
עם שיטת קריאה חוזרת onAudioFocusChange()
שמטמיעה את ההתנהגות הרצויה, ולרשום את ה-listener על ידי שליחת קריאה אל setOnAudioFocusChangeListener()
.
מיקוד אודיו ב-Android מגרסה 7.1 ומטה
כשמתקשרים אל
requestAudioFocus()
צריך לציין רמז לגבי משך הזמן, שאפליקציה אחרת שמחזיקה כרגע במיקוד ומפעילה תוכן עשויה להתחשב בו:
- שליחת בקשה למיקוד אודיו קבוע (
AUDIOFOCUS_GAIN
) כשמתכננים להפעיל אודיו בעתיד הנראה לעין (לדוגמה, כשמפעילים מוזיקה) ומצפים שהאפליקציה הקודמת שהיה לה מיקוד אודיו תפסיק להפעיל אודיו. - שליחת בקשה להתמקדות זמנית (
AUDIOFOCUS_GAIN_TRANSIENT
) כשמצפים להשמעת אודיו לזמן קצר בלבד, וכשמצפים שהבעלים הקודמים של המיקוד ישהה את ההפעלה. - שליחת בקשה להעברת המיקוד באופן זמני באמצעות הנמכה
(
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
) כדי לציין שאתם רוצים להפעיל אודיו לזמן קצר בלבד, ושהבעלים הקודם של המיקוד יכול להמשיך להפעיל אודיו אם הוא מנמיך את פלט האודיו שלו. שני פלטי האודיו מעורבבים בזרם האודיו. הנמכת עוצמת הקול מתאימה במיוחד לאפליקציות שמשתמשות בזרם האודיו לסירוגין, למשל להנחיות קוליות לנהיגה.
השיטה requestAudioFocus()
דורשת גם AudioManager.OnAudioFocusChangeListener
. צריך ליצור את מאזין האירועים באותה פעילות או באותו שירות שמוגדרים כבעלים של סשן המדיה. הוא מיישם את הקריאה החוזרת (callback) 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
המשויך. אם ביקשתם מיקוד זמני, האפליקציה שהושהתה או שההנמכה שלה הופעלה תקבל הודעה שאפשר להמשיך להפעיל אותה או להחזיר את עוצמת הקול שלה.
תגובה לשינוי במיקוד האודיו
כשאפליקציה מקבלת את פוקוס האודיו, היא צריכה להיות מסוגלת לשחרר אותו כשמגיעה בקשה מאפליקציה אחרת לקבל את פוקוס האודיו. במקרה כזה, האפליקציה מקבלת קריאה ל-method onAudioFocusChange()
ב-AudioFocusChangeListener
שציינתם כשהאפליקציה קראה ל-requestAudioFocus()
.
הפרמטר focusChange
שמועבר אל onAudioFocusChange()
מציין את סוג השינוי שמתרחש. הוא תואם לרמז משך הזמן שבו נעשה שימוש באפליקציה שמקבלת את המיקוד. האפליקציה צריכה להגיב בצורה הולמת.
- אובדן מיקוד זמני
-
אם שינוי המיקוד הוא זמני (
AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
אוAUDIOFOCUS_LOSS_TRANSIENT
), האפליקציה צריכה להנמיך את עוצמת הקול (אם אתם לא מסתמכים על הנמכה אוטומטית של עוצמת הקול) או להשהות את ההפעלה, אבל לשמור על אותו מצב.במהלך אובדן זמני של הרשאת האודיו, צריך להמשיך לעקוב אחרי שינויים בהרשאת האודיו ולהיות מוכנים להמשיך בהפעלה הרגילה כשמקבלים שוב את ההרשאה. כשאפליקציית החסימה מפסיקה להתמקד, מתקבלת קריאה חוזרת (callback) (
AUDIOFOCUS_GAIN
). בשלב הזה, אפשר להחזיר את עוצמת הקול לרמה הרגילה או להפעיל מחדש את ההשמעה. - אובדן מיקוד קבוע
-
אם איבוד המיקוד באודיו הוא קבוע (
AUDIOFOCUS_LOSS
), אפליקציה אחרת משמיעה אודיו. האפליקציה צריכה להשהות את ההפעלה באופן מיידי, כי היא לא תקבל אף פעם קריאה חוזרת (callback) שלAUDIOFOCUS_GAIN
. כדי להפעיל מחדש את ההשמעה, המשתמש צריך לבצע פעולה מפורשת, כמו ללחוץ על לחצן ההפעלה של אמצעי הבקרה להפעלת מדיה בהתראה או בממשק המשתמש של האפליקציה.
קטע הקוד הבא מדגים איך להטמיע את 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)
בתגובה לכל שינוי בסטטוס. לדוגמה, אפשר לקרוא ל-method removeCallbacks()
ב-callback onPlay()
, onSkipToNext()
וכו'. כדאי גם לקרוא ל-method הזה ב-callback onDestroy()
של השירות כשמנקים את המשאבים שבהם השירות משתמש.