تحسين تشغيل الوسائط: مقدّمة عن التحميل المُسبق باستخدام Media3 - الجزء الأول
مدّة القراءة: 8 دقائق
في التطبيقات التي تركّز على الوسائط اليوم، يُعدّ تقديم تجربة تشغيل سلسة وبدون انقطاع أمرًا أساسيًا لتوفير تجربة مستخدم ممتعة. يتوقّع المستخدمون أن تبدأ فيديوهاتهم على الفور وأن يتم تشغيلها بسلاسة بدون توقّف.
التحدي الأساسي هو وقت الاستجابة. في العادة، لا يبدأ مشغّل الفيديو عمله (الاتصال والتنزيل والتحليل والتخزين المؤقت) إلا بعد أن يختار المستخدم عنصرًا لتشغيله. هذا النهج التفاعلي بطيء بالنسبة إلى سياق الفيديوهات القصيرة اليوم. الحل هو اتخاذ خطوات استباقية. علينا توقّع ما سيشاهده المستخدم بعد ذلك وتجهيز المحتوى مسبقًا. هذا هو جوهر التحميل المُسبَق.
تشمل المزايا الرئيسية للتحميل المُسبق ما يلي:
- 🚀 بدء التشغيل بشكل أسرع: تكون الفيديوهات جاهزة للتشغيل، ما يؤدي إلى انتقال أسرع بين العناصر وبدء التشغيل بشكل فوري.
- 📉 تقليل التخزين المؤقت: من خلال تحميل البيانات بشكل استباقي، يقلّ احتمال توقّف التشغيل بسبب مشاكل في الشبكة مثلاً.
- ✨ تجربة مستخدم أكثر سلاسة: يساهم الجمع بين بدء التشغيل بشكل أسرع وتقليل التخزين المؤقت في توفير تجربة تفاعلية أكثر سلاسة للمستخدمين.
في هذه السلسلة المكوّنة من ثلاثة أجزاء، سنقدّم لك أدوات Media3 الفعّالة لتحميل المكوّنات (مسبقًا) ونتعمّق فيها.
- في الجزء الأول، سنتناول الأساسيات: فهم استراتيجيات التحميل المُسبق المختلفة المتاحة في Media3، وتفعيل PreloadConfiguration، وإعداد DefaultPreloadManager، ما يتيح لتطبيقك تحميل العناصر مسبقًا. في نهاية هذه المدوّنة، من المفترض أن تتمكّن من تحميل ملفات الوسائط مسبقًا وتشغيلها حسب الترتيب والمدة اللذين تم ضبطهما.
- في الجزء الثاني، سنتناول مواضيع أكثر تقدّمًا حول DefaultPreloadManager، مثل استخدام المستمعين للإحصاءات، واستكشاف أفضل الممارسات الجاهزة للإنتاج، مثل نمط النافذة المنزلقة والمكوّنات المشتركة المخصّصة لكل من DefaultPreloadManager وExoPlayer.
- في الجزء الثالث، سنتعمّق في ذاكرة التخزين المؤقت للقرص باستخدام DefaultPreloadManager.
التحميل المُسبق هو الحلّ 🦸♀️
الفكرة الأساسية وراء التحميل المُسبق بسيطة: تحميل محتوى الوسائط قبل الحاجة إليه. وعندما ينتقل المستخدم إلى الفيديو التالي، تكون الأجزاء الأولى من الفيديو قد تم تنزيلها وأصبحت متاحة وجاهزة للتشغيل الفوري.
فكِّر في الأمر كأنّك في مطعم. في المطبخ المزدحم، لا ينتظر الطهاة وصول الطلب لبدء تقطيع البصل. 🧅 يجهّزون المكوّنات مسبقًا. التحميل المُسبق هو عملية تحضير مشغّل الفيديو.
عند تفعيل ميزة "التحميل المُسبق"، يمكن أن تساعد في تقليل وقت الاستجابة للانضمام عندما يتخطّى المستخدم المحتوى إلى العنصر التالي قبل أن يصل التخزين المؤقت للتشغيل إلى العنصر التالي. يتم إعداد الفترة الأولى من النافذة التالية وتخزين عيّنات الفيديو والصوت والنص مؤقتًا. تتم لاحقًا إضافة الفترة التي تم تحميلها مسبقًا إلى قائمة الانتظار في المشغّل مع توفّر عيّنات مخزّنة مؤقتًا على الفور وجاهزة لإرسالها إلى برنامج الترميز لعرضها.
في Media3، تتوفّر واجهتا برمجة تطبيقات أساسيتان للتحميل المُسبَق، وكل منهما مناسبة لحالات استخدام مختلفة. اختيار واجهة برمجة التطبيقات المناسبة هو الخطوة الأولى.
1. تحميل عناصر قائمة التشغيل مسبقًا باستخدام PreloadConfiguration
هذا هو النهج البسيط، وهو مفيد للوسائط الخطية والمتسلسلة مثل قوائم التشغيل التي يكون فيها ترتيب التشغيل متوقعًا (مثل سلسلة من الحلقات). يمكنك تزويد المشغّل بالقائمة الكاملة لملفات الوسائط باستخدام واجهات برمجة تطبيقات قائمة التشغيل في ExoPlayer وضبط PreloadConfiguration للمشغّل، ثم يتم تلقائيًا تحميل الملفات التالية في التسلسل مسبقًا كما تم ضبطه. تحاول واجهة برمجة التطبيقات هذه تحسين وقت الاستجابة للانضمام عندما يتخطّى المستخدم المحتوى إلى العنصر التالي قبل أن يتداخل مخزن التشغيل المؤقت مع العنصر التالي.
لا يبدأ التحميل المُسبق إلا في حال عدم تحميل أي وسائط للتشغيل الحالي، ما يمنع التنافس على معدل نقل البيانات مع التشغيل الأساسي.
إذا لم تكن متأكدًا بعد من حاجتك إلى التحميل المُسبق، فإنّ واجهة برمجة التطبيقات هذه هي خيار رائع ومنخفض التكلفة لتجربتها.
player.preloadConfiguration =
PreloadConfiguration(/* targetPreloadDurationUs= */ 5_000_000L)باستخدام PreloadConfiguration أعلاه، يحاول المشغّل تحميل خمس ثوانٍ من الوسائط مسبقًا للعنصر التالي في قائمة التشغيل.
بعد الموافقة، يمكن إيقاف التحميل المُسبق لقوائم التشغيل مجددًا باستخدام PreloadConfiguration.DEFAULT لتعطيل التحميل المُسبق لقوائم التشغيل:
player.preloadConfiguration = PreloadConfiguration.DEFAULT
2. التحميل المُسبَق للقوائم الديناميكية باستخدام PreloadManager
بالنسبة إلى واجهات المستخدم الديناميكية، مثل الخلاصات العمودية أو لوحات العرض الدوّارة، حيث يتم تحديد العنصر "التالي" من خلال تفاعل المستخدم، تكون واجهة PreloadManager API مناسبة. هذا مكوّن جديد وفعّال ومستقل ضمن مكتبة Media3 ExoPlayer مصمّم خصيصًا للتحميل المُسبق بشكل استباقي. وتدير هذه الواجهة مجموعة من مصادر MediaSources المحتملة، وتمنحها الأولوية استنادًا إلى مدى قربها من موضع المستخدم الحالي، وتوفّر تحكّمًا دقيقًا في ما يجب تحميله مسبقًا، وهو مناسب للسيناريوهات المعقّدة، مثل الخلاصات الديناميكية للفيديوهات القصيرة.
إعداد PreloadManager
تُعدّ واجهة DefaultPreloadManager التنفيذ الأساسي لواجهة PreloadManager.
يمكن لمنشئ DefaultPreloadManager إنشاء كل من DefaultPreloadManager وأي مثيلات ExoPlayer التي ستشغّل المحتوى الذي تم تحميله مسبقًا. لإنشاء DefaultPreloadManager، عليك تمرير TargetPreloadStatusControl، والذي يمكن لمدير التحميل المُسبَق الاستعلام عنه لمعرفة مقدار التحميل لعنصر ما. سنشرح ونحدّد مثالاً على TargetPreloadStatusControl في القسم أدناه.
val preloadManagerBuilder = DefaultPreloadManager.Builder(context, targetPreloadStatusControl) val preloadManager = val preloadManagerBuilder.build() // Build ExoPlayer with DefaultPreloadManager.Builder val player = preloadManagerBuilder.buildExoPlayer()
من الضروري استخدام أداة الإنشاء نفسها لكل من ExoPlayer وDefaultPreloadManager، ما يضمن مشاركة المكوّنات الأساسية بشكل صحيح.
هذا كل ما في الأمر! لديك الآن مدير جاهز لتلقّي التعليمات.
ضبط المدة والترتيب باستخدام TargetPreloadStatusControl
ماذا لو أردت تحميل 10 ثوانٍ من الفيديو مسبقًا؟ يمكنك تقديم موضع ملفات الوسائط في لوحة العرض الدوّارة، ويمنح DefaultPreloadManager الأولوية لتحميل الملفات استنادًا إلى مدى قربها من الملف الذي يشغّله المستخدم حاليًا.
إذا أردت التحكّم في مدة تحميل العنصر مسبقًا، يمكنك تحديد ذلك من خلال DefaultPreloadManager.PreloadStatus التي تعرضها.
على سبيل المثال:
- العنصر "أ" هو الأولوية القصوى، ويتم تحميل 5 ثوانٍ من الفيديو.
- العنصر "ب" ذو أولوية متوسطة، ولكن عند الوصول إليه، يتم تحميل 3 ثوانٍ من الفيديو.
- العنصر "ج" أقل أولوية، ويتم تحميل المسارات فقط.
- العنصر "د" أقل أولوية، فقط استعد.
- أي عناصر أخرى بعيدة، لا تقم بتحميل أي شيء مسبقًا.
يمكن أن يساعدك هذا التحكّم الدقيق في تحسين استخدام الموارد، وهو أمر يُنصح به لتشغيل المحتوى بسلاسة.
import androidx.media3.exoplayer.DefaultPreloadManager.PreloadStatus
class MyTargetPreloadStatusControl(
currentPlayingIndex: Int = C.INDEX_UNSET
) : TargetPreloadStatusControl<Int,PreloadStatus> {
// The app is responsible for updating this based on UI state
override fun getTargetPreloadStatus(index: Int): PreloadStatus? {
val distance = index - currentPlayingIndex
// Adjacent items (Next): preload 5 seconds
if (distance == 1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED and suggest loading // 5000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(5000L)
}
// Adjacent items (Previous): preload 3 seconds
else if (distance == -1) {
// Return a PreloadStatus that is labelled by STAGE_SPECIFIED_RANGE_LOADED //and suggest loading 3000ms from the default start position
return PreloadStatus.specifiedRangeLoaded(3000L)
}
// Items two positions away: just select tracks
else if (distance) == 2) {
// Return a PreloadStatus that is labelled by STAGE_TRACKS_SELECTED
return PreloadStatus.TRACKS_SELECTED
}
// Items four positions away: just select prepare
else if (abs(distance) <= 4) {
// Return a PreloadStatus that is labelled by STAGE_SOURCE_PREPARED
return PreloadStatus.SOURCE_PREPARED
}
// All other items are too far away
return null
}
}ملاحظة: يمكن لـ PreloadManager الاحتفاظ بكل من العناصر السابقة والتالية محمَّلة مسبقًا، بينما سيبحث PreloadConfiguration فقط عن العناصر التالية.
إدارة عناصر التحميل المُسبَق
بعد إنشاء المدير، يمكنك البدء بإخباره بما يجب العمل عليه. عندما يتصفّح المستخدم الخلاصة، يمكنك تحديد الفيديوهات التالية وإضافتها إلى المدير. إنّ التفاعل مع PreloadManager هو محادثة مستندة إلى الحالة بين واجهة المستخدم ومحرّك التحميل المُسبَق.
1. إضافة ملفات وسائط
أثناء تعبئة خلاصتك، عليك إبلاغ المدير بالوسائط التي يجب تتبُّعها. إذا كنت في البداية، يمكنك إضافة القائمة الكاملة التي تريد تحميلها مسبقًا. بعد ذلك، يمكنك الاستمرار في إضافة سلعة واحدة إلى القائمة حسب الحاجة. يمكنك التحكّم بشكل كامل في العناصر الموجودة في قائمة التحميل المُسبق، ما يعني أنّه عليك أيضًا إدارة ما تتم إضافته وإزالته من المدير.
val initialMediaItems = pullMediaItemsFromService(/* count= */ 20)
for (index in 0 until initialMediaItems.size) {
preloadManager.add(
initialMediaItems.get(index),index)
)
}سيبدأ الحساب الإداري الآن في جلب البيانات لهذا الحساب (MediaItem) في الخلفية.
بعد الإضافة، اطلب من المدير إعادة تقييم قائمته الجديدة (مع الإشارة إلى أنّ شيئًا ما قد تغيّر، مثل إضافة عنصر أو إزالته، أو أنّ المستخدم انتقل إلى تشغيل عنصر جديد).
preloadManager.invalidate()
2. استرداد عنصر وتشغيله
في ما يلي منطق التشغيل الرئيسي. عندما يقرّر المستخدم تشغيل هذا الفيديو، لن تحتاج إلى إنشاء MediaSource جديد. بدلاً من ذلك، تطلب من PreloadManager الحصول على الردّ الذي سبق أن أعدّه. يمكنك استرداد MediaSource من Preload Manager باستخدام MediaItem.
إذا كان العنصر الذي تم استرداده من PreloadManager فارغًا، يعني ذلك أنّه لم يتم تحميل mediaItem مسبقًا أو إضافته إلى PreloadMamager، لذا يمكنك اختيار ضبط mediaItem مباشرةً.
// When a media item is about to display on the screen
val mediaSource = preloadManager.getMediaSource(mediaItem)
if (mediaSource!= null) {
player.setMediaSource(mediaSource)
} else {
// If mediaSource is null, that mediaItem hasn't been added yet.
// So, send it directly to the player.
player.setMediaItem(mediaItem)
}
player.prepare()
// When the media item is displaying at the center of the screen
player.play()من خلال إعداد MediaSource الذي تم استرداده من PreloadManager، يمكنك الانتقال بسلاسة من التحميل المُسبَق إلى التشغيل، باستخدام البيانات المتوفّرة في الذاكرة. وهذا ما يجعل وقت البدء أسرع.
3. الحفاظ على مزامنة الفهرس الحالي مع واجهة المستخدم
بما أنّ الخلاصة أو القائمة قد تكون ديناميكية، من المهم إعلام PreloadManager بالفهرس الحالي الذي يتم تشغيله لكي يتمكّن دائمًا من منح الأولوية للعناصر الأقرب إلى الفهرس الحالي للتحميل المُسبَق.
preloadManager.setCurrentPlayingIndex(currentIndex) // Need to call invalidate() to update the priorities preloadManager.invalidate()
4. إزالة عنصر
للحفاظ على كفاءة المدير، يجب إزالة العناصر التي لم يعُد بحاجة إلى تتبُّعها، مثل العناصر البعيدة عن الموقع الجغرافي الحالي للمستخدم.
// When an item is too far from the current playing index preloadManager.remove(mediaItem)
إذا أردت محو كل العناصر دفعة واحدة، يمكنك الاتصال بالرقم preloadManager.reset().
5. إصدار "المدير"
عندما لا تعود بحاجة إلى PreloadManager (على سبيل المثال، عند إتلاف واجهة المستخدم)، يجب إزالته لإخلاء موارده. ويمكنك إجراء ذلك في المكان الذي تنشر فيه حاليًا موارد "اللاعب". ننصحك بتحرير المدير قبل المشغّل لأنّ المشغّل يمكنه مواصلة التشغيل إذا لم تكن بحاجة إلى المزيد من التحميل المُسبق.
// In your Activity's onDestroy() or Composable's onDispose preloadManager.release()
تقديم العرض التوضيحي
يمكنك الاطّلاع على هذه الميزة أثناء استخدامها 👍
في العرض التوضيحي أدناه، يظهر تأثير PreloadManager على الجانب الأيمن الذي يتميز بأوقات تحميل أسرع، بينما يعرض الجانب الأيسر التجربة الحالية. يمكنك أيضًا الاطّلاع على نموذج الرمز البرمجي للعرض التوضيحي. (مكافأة: يعرض أيضًا وقت استجابة بدء التشغيل لكل فيديو)
الخطوات التالية:
وهذا ملخّص الجزء الأول. أصبحت لديك الآن الأدوات اللازمة لإنشاء نظام تحميل مسبق ديناميكي. يمكنك استخدام PreloadConfiguration لتحميل العنصر التالي في قائمة تشغيل في ExoPlayer مسبقًا أو إعداد DefaultPreloadManager وإضافة العناصر وإزالتها بسرعة وضبط حالة التحميل المُسبَق المستهدَفة واسترداد المحتوى الذي تم تحميله مسبقًا بشكل صحيح للتشغيل.
في الجزء الثاني، سنتعمّق أكثر في DefaultPreloadManager. سنستكشف كيفية الاستماع إلى أحداث التحميل المُسبق، وسنناقش أفضل الممارسات، مثل استخدام نافذة منزلقة لتجنُّب مشاكل الذاكرة، وسنلقي نظرة على المكوّنات المشتركة المخصّصة في ExoPlayer وDefaultPreloadManager.
هل لديك أي ملاحظات لمشاركتها؟ نحن في انتظار ردّك.
تابِعنا واجعل تطبيقك أسرع. 🚀
-
أخبار المنتجاتمرحبًا بك في الجزء الثاني من سلسلتنا المكوّنة من ثلاثة أجزاء حول التحميل المُسبق للوسائط باستخدام Media3. تم تصميم هذه السلسلة لإرشادك خلال عملية إنشاء تجارب وسائط عالية الاستجابة ومنخفضة وقت الاستجابة في تطبيقات Android.
Mayuri Khinvasara Khabya • مدّة القراءة: 9 دقائق -
أخبار المنتجاتبما أنّ Unity تشغّل أكثر من نصف جميع الألعاب على الأجهزة الجوّالة في جميع أنحاء العالم، فإنّ الانطباعات الأولى مهمة. بالنسبة إلى مطوّري الألعاب، تُعدّ تجربة الإعداد السلسة والموثوقة للاعبين ضرورية لتعزيز معدّل الاحتفاظ باللاعبين من اليوم الأول وزيادة تفاعلهم على المدى الطويل.
Michelle Martin • مدّة القراءة: دقيقة واحدة -
أخبار المنتجاتمن خلال تطوير التطبيقات على Android XR، يمكنك تلبية احتياجات المستخدمين في أي وقت، سواء كانوا يبحثون عن تجربة غامرة بالكامل في المنزل أو تجربة سريعة أثناء التنقل، وذلك من خلال مجموعة واسعة من التطبيقات المكانية. في مؤتمر Android Dev Summit، احتفلنا بإنجاز رائع من خلال إطلاق نظارة XREAL AURA.
Amy Zeppenfeld, Vinny DaSilva • مدّة القراءة: 3 دقائق
يمكنك الحصول على آخر الإحصاءات حول تطوير تطبيقات Android في بريدك الوارد أسبوعيًا.