אפשר להפעיל מדיה ברקע גם כשהאפליקציה לא מוצגת במסך, למשל, בזמן שהמשתמש מבצע פעולות באפליקציות אחרות.
כדי לעשות זאת, מטמיעים את MediaPlayer בשירות MediaBrowserServiceCompat
ומאפשרים לו לקיים אינטראקציה עם MediaBrowserCompat
בפעילות אחרת.
חשוב להיזהר בהטמעה של הגדרת הלקוח והשרת הזו. יש ציפיות לגבי האינטראקציה של נגן שפועל בשירות ברקע עם שאר המערכת. אם האפליקציה לא תעמוד בציפיות האלה, יכול להיות שהמשתמש יתקבל חוויה לא טובה. פרטים נוספים זמינים במאמר פיתוח אפליקציית אודיו.
בדף הזה מפורטות הוראות מיוחדות לניהול MediaPlayer כשמטמיעים אותו בשירות.
הפעלה אסינכרונית
בדומה לActivity
, כל העבודה בService
מתבצעת בשרשור אחד כברירת מחדל. למעשה, כשמריצים פעילות ושירות מאותה אפליקציה, הם משתמשים באותו חוט ('החוט הראשי') כברירת מחדל.
השירותים צריכים לעבד את הכוונות הנכנסות במהירות, ואף פעם לא לבצע חישובים ארוכים בתגובה אליהן. צריך לבצע משימות כבדות או קריאות חסימה באופן אסינכרוני: משרשור אחר שתטמיעו בעצמכם, או באמצעות השירותים הרבים של המסגרת לעיבוד אסינכרוני.
לדוגמה, כשמשתמשים ב-MediaPlayer
מהשרשור הראשי, צריך:
- צריך להתקשר למספר
prepareAsync()
במקום למספרprepare()
, ו - מטמיעים
MediaPlayer.OnPreparedListener
כדי לקבל התראה כשהתהליך יושלם ותוכלו להתחיל לשחק.
לדוגמה:
Kotlin
private const val ACTION_PLAY: String = "com.example.action.PLAY"
class MyService: Service(), MediaPlayer.OnPreparedListener {
private var mMediaPlayer: MediaPlayer? = null
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
...
val action: String = intent.action
when(action) {
ACTION_PLAY -> {
mMediaPlayer = ... // initialize it here
mMediaPlayer?.apply {
setOnPreparedListener(this@MyService)
prepareAsync() // prepare async to not block main thread
}
}
}
...
}
/** Called when MediaPlayer is ready */
override fun onPrepared(mediaPlayer: MediaPlayer) {
mediaPlayer.start()
}
}
Java
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mediaPlayer = null;
public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mediaPlayer = ... // initialize it here
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}
/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}
טיפול בשגיאות אסינכרוניות
בפעולות סינכרוניות, השגיאות מסומנות באמצעות חריג או קוד שגיאה. עם זאת, כשמשתמשים במשאבים אסינכרונים, צריך להודיע לאפליקציה על השגיאות באופן מתאים. במקרה של MediaPlayer
, מטמיעים את MediaPlayer.OnErrorListener
ומגדירים אותו במכונה MediaPlayer
:
Kotlin
class MyService : Service(), MediaPlayer.OnErrorListener {
private var mediaPlayer: MediaPlayer? = null
fun initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer?.setOnErrorListener(this)
}
override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
Java
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mediaPlayer;
public void initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer.setOnErrorListener(this);
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}
כשמתרחשת שגיאה, ה-MediaPlayer
עובר למצב שגיאה. צריך לאפס אותו כדי שאפשר יהיה להשתמש בו שוב. לפרטים נוספים, אפשר לעיין בתרשים המצב המלא של הכיתה MediaPlayer
.
שימוש בחסימות מצב שינה
כשאתם מפעילים מוזיקה או מפעילים סטרימינג של מוזיקה ברקע, עליכם להשתמש בחסימות של מצב שינה כדי למנוע מהמערכת להפריע להפעלה, למשל, על ידי הכנסת המכשיר למצב שינה.
נעילת ההתעוררות היא אות למערכת שהאפליקציה משתמשת בתכונות שצריכות להישאר זמינות גם כשהטלפון לא פעיל.
כדי לוודא שהמעבד ימשיך לפעול בזמן שהסרטון MediaPlayer
מופעל, צריך להפעיל את השיטה setWakeMode()
כשמאתחלים את MediaPlayer
. המשתנה MediaPlayer
שומר על הנעילה שצוינה במהלך ההפעלה, ומשחרר אותה כשהיא מושהית או מופסקת:
Kotlin
mediaPlayer = MediaPlayer().apply {
// ... other initialization here ...
setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}
Java
mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
עם זאת, נעילת ההתעוררות שנרכשה בדוגמה הזו מבטיחה רק שהמעבד יישאר במצב פעיל. אם אתם משדרים מדיה ברשת באמצעות Wi-Fi, מומלץ גם להחזיק ב-WifiLock
. צריך לקבל ולשחרר אותו באופן ידני. לכן, כשמתחילים להכין את MediaPlayer
עם כתובת ה-URL מרחוק, צריך ליצור ולקבל את נעילת ה-Wi-Fi.
לדוגמה:
Kotlin
val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")
wifiLock.acquire()
Java
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
כשמשהים או מפסיקים את המדיה, או כשלא צריכים יותר את הערוץ, צריך לבטל את הנעילה:
Kotlin
wifiLock.release()
Java
wifiLock.release();
ביצוע ניקוי
כפי שצוין קודם, אובייקט MediaPlayer
יכול לצרוך כמות משמעותית של משאבי המערכת, לכן צריך לשמור אותו רק למשך הזמן הנדרש ולקרוא ל-release()
כשמסיימים להשתמש בו. חשוב להפעיל את שיטת הניקוי הזו באופן מפורש ולא להסתמך על איסוף האשפה במערכת, כי יכול להיות שיחלוף זמן מה עד שמנגנון איסוף האשפה יחזיר לשימוש את MediaPlayer
, כי הוא רגיש רק לצורכי הזיכרון ולא למחסור במשאבים אחרים שקשורים למדיה. לכן, אם אתם משתמשים בשירות, תמיד צריך לשנות את השיטה onDestroy()
כדי לוודא שאתם משחררים את MediaPlayer
:
Kotlin
class MyService : Service() {
private var mediaPlayer: MediaPlayer? = null
// ...
override fun onDestroy() {
super.onDestroy()
mediaPlayer?.release()
}
}
Java
public class MyService extends Service {
MediaPlayer mediaPlayer;
// ...
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) mediaPlayer.release();
}
}
תמיד כדאי לחפש הזדמנויות אחרות לפרסם את MediaPlayer
, מלבד פרסום במהלך סגירה. לדוגמה, אם אתם לא מצפים להפעיל מדיה במשך זמן רב (למשל אחרי שאתם מפסיקים להתמקד באודיו), מומלץ מאוד לשחרר את MediaPlayer
הקיים וליצור אותו מחדש מאוחר יותר. לעומת זאת, אם אתם מתכוונים להפסיק את ההפעלה רק לזמן קצר מאוד, מומלץ לשמור את MediaPlayer
כדי להימנע מהעלויות הנלוות של היצירה וההכנה מחדש.
מידע נוסף
Jetpack Media3 הוא הפתרון המומלץ להפעלת מדיה באפליקציה. מידע נוסף
בדפים האלה מפורטים נושאים שקשורים להקלטה, לאחסון ולהפעלה של אודיו ווידאו: