يمكنك تشغيل الوسائط في الخلفية حتى عندما لا يكون تطبيقك معروضًا على الشاشة، مثلاً عندما يتفاعل المستخدم مع تطبيقات أخرى.
لإجراء ذلك، يمكنك تضمين 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);
ومع ذلك، لا يضمن قفل التنشيط الذي تم الحصول عليه في هذا المثال سوى إبقاء وحدة المعالجة المركزية (CPU)
في وضع التشغيل. إذا كنت تبثّ الوسائط عبر الشبكة وكنت تستخدم شبكة 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 هو الحلّ المُقترَح لتشغيل الوسائط في تطبيقك. اطّلِع على مزيد من المعلومات حوله.
تتناول هذه الصفحات مواضيع تتعلّق بتسجيل المحتوى الصوتي والفيديو وتخزينه وتشغيله: