معمولاً پخش رسانه در حالی که یک برنامه در پیش زمینه نیست، مطلوب است. به عنوان مثال، پخش کننده موسیقی معمولاً زمانی که کاربر دستگاه خود را قفل کرده است یا از برنامه دیگری استفاده می کند، به پخش موسیقی ادامه می دهد. کتابخانه Media3 مجموعه ای از رابط ها را ارائه می دهد که به شما امکان می دهد از پخش پس زمینه پشتیبانی کنید.
از MediaSessionService استفاده کنید
برای فعال کردن پخش پسزمینه، باید Player
و MediaSession
را در یک سرویس جداگانه قرار دهید. این به دستگاه امکان می دهد حتی زمانی که برنامه شما در پیش زمینه نیست، به پخش رسانه ادامه دهد.
هنگام میزبانی یک پخش کننده در داخل یک سرویس، باید از MediaSessionService
استفاده کنید. برای انجام این کار، یک کلاس ایجاد کنید که MediaSessionService
را گسترش دهد و جلسه رسانه خود را در داخل آن ایجاد کنید.
استفاده از MediaSessionService
این امکان را برای کلاینتهای خارجی مانند Google Assistant، کنترلهای رسانه سیستم یا دستگاههای همراه مانند Wear OS فراهم میکند تا سرویس شما را کشف کنند، به آن متصل شوند و پخش را کنترل کنند، همه اینها بدون دسترسی به فعالیت رابط کاربری برنامه شما. در واقع، میتوان چندین برنامه کلاینت را به طور همزمان به یک MediaSessionService
متصل کرد که هر برنامه MediaController
مخصوص به خود را دارد.
چرخه عمر سرویس را پیاده سازی کنید
شما باید سه روش چرخه عمر سرویس خود را پیاده سازی کنید:
-
onCreate()
زمانی فراخوانی می شود که اولین کنترلر در شرف اتصال است و سرویس نمونه سازی و راه اندازی می شود. این بهترین مکان برای ساختPlayer
وMediaSession
است. -
onTaskRemoved(Intent)
زمانی فراخوانی می شود که کاربر برنامه را از وظایف اخیر رد کند. اگر پخش ادامه دارد، برنامه میتواند انتخاب کند که سرویس در پیشزمینه اجرا شود. اگر پخش کننده متوقف شود، سرویس در پیش زمینه نیست و باید متوقف شود. -
onDestroy()
زمانی فراخوانی می شود که سرویس در حال توقف است. همه منابع از جمله پخش کننده و جلسه باید آزاد شوند.
کاتلین
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // Create your player and media session in the onCreate lifecycle event override fun onCreate() { super.onCreate() val player = ExoPlayer.Builder(this).build() mediaSession = MediaSession.Builder(this, player).build() } // The user dismissed the app from the recent tasks override fun onTaskRemoved(rootIntent: Intent?) { val player = mediaSession?.player!! if (!player.playWhenReady || player.mediaItemCount == 0 || player.playbackState == Player.STATE_ENDED) { // Stop the service if not playing, continue playing in the background // otherwise. stopSelf() } } // Remember to release the player and media session in onDestroy override fun onDestroy() { mediaSession?.run { player.release() release() mediaSession = null } super.onDestroy() } }
جاوا
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; // Create your Player and MediaSession in the onCreate lifecycle event @Override public void onCreate() { super.onCreate(); ExoPlayer player = new ExoPlayer.Builder(this).build(); mediaSession = new MediaSession.Builder(this, player).build(); } // The user dismissed the app from the recent tasks @Override public void onTaskRemoved(@Nullable Intent rootIntent) { Player player = mediaSession.getPlayer(); if (!player.getPlayWhenReady() || player.getMediaItemCount() == 0 || player.getPlaybackState() == Player.STATE_ENDED) { // Stop the service if not playing, continue playing in the background // otherwise. stopSelf(); } } // Remember to release the player and media session in onDestroy @Override public void onDestroy() { mediaSession.getPlayer().release(); mediaSession.release(); mediaSession = null; super.onDestroy(); } }
به عنوان جایگزینی برای ادامه پخش در پسزمینه، یک برنامه میتواند در هر صورت زمانی که کاربر برنامه را رد کند، سرویس را متوقف کند:
کاتلین
override fun onTaskRemoved(rootIntent: Intent?) { val player = mediaSession.player if (player.playWhenReady) { // Make sure the service is not in foreground. player.pause() } stopSelf() }
جاوا
@Override public void onTaskRemoved(@Nullable Intent rootIntent) { Player player = mediaSession.getPlayer(); if (player.getPlayWhenReady()) { // Make sure the service is not in foreground. player.pause(); } stopSelf(); }
دسترسی به جلسه رسانه را فراهم کنید
روش onGetSession()
را نادیده بگیرید تا به سایر کلاینتها به جلسه رسانه شما که در زمان ایجاد سرویس ساخته شده است دسترسی داشته باشند.
کاتلین
class PlaybackService : MediaSessionService() { private var mediaSession: MediaSession? = null // [...] lifecycle methods omitted override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? = mediaSession }
جاوا
public class PlaybackService extends MediaSessionService { private MediaSession mediaSession = null; // [...] lifecycle methods omitted @Override public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) { return mediaSession; } }
سرویس را در مانیفست اعلام کنید
یک برنامه برای اجرای یک سرویس پیش زمینه به مجوز نیاز دارد. مجوز FOREGROUND_SERVICE
را به مانیفست اضافه کنید، و اگر API 34 و بالاتر را هدف قرار می دهید FOREGROUND_SERVICE_MEDIA_PLAYBACK
نیز اضافه کنید:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
همچنین باید کلاس Service
خود را در مانیفست با فیلتر قصد MediaSessionService
اعلام کنید.
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
هنگامی که برنامه شما روی دستگاهی با Android 10 (سطح API 29) و بالاتر اجرا می شود، باید یک foregroundServiceType
که شامل mediaPlayback
باشد، تعریف کنید.
پخش را با استفاده از MediaController
کنترل کنید
در Activity یا Fragment حاوی رابط کاربری پخش کننده شما، می توانید با استفاده از MediaController
یک پیوند بین رابط کاربری و جلسه رسانه خود ایجاد کنید. رابط کاربری شما از کنترلر رسانه برای ارسال دستورات از رابط کاربری شما به پخش کننده در جلسه استفاده می کند. برای جزئیات بیشتر در مورد ایجاد و استفاده از MediaController
به راهنمای ایجاد یک MediaController
مراجعه کنید.
دستورات UI را مدیریت کنید
MediaSession
از طریق MediaSession.Callback
خود دستورات را از کنترلر دریافت می کند. راهاندازی یک MediaSession
یک پیادهسازی پیشفرض از MediaSession.Callback
ایجاد میکند که بهطور خودکار تمام دستوراتی را که MediaController
به پخشکننده شما ارسال میکند کنترل میکند.
اطلاع رسانی
یک MediaSessionService
به طور خودکار یک MediaNotification
برای شما ایجاد می کند که در بیشتر موارد باید کار کند. به طور پیش فرض، اعلان منتشر شده یک اعلان MediaStyle
است که با آخرین اطلاعات جلسه رسانه شما به روز می شود و کنترل های پخش را نمایش می دهد. MediaNotification
از جلسه شما آگاه است و می تواند برای کنترل پخش هر برنامه دیگری که به همان جلسه متصل است استفاده شود.
به عنوان مثال، یک برنامه پخش موسیقی با استفاده از MediaSessionService
یک MediaNotification
ایجاد می کند که عنوان، هنرمند و هنر آلبوم را برای آیتم رسانه فعلی در حال پخش در کنار کنترل های پخش بر اساس پیکربندی MediaSession
شما نمایش می دهد.
فراداده مورد نیاز را می توان در رسانه ارائه کرد یا به عنوان بخشی از آیتم رسانه ای در قطعه زیر اعلام کرد:
کاتلین
val mediaItem = MediaItem.Builder() .setMediaId("media-1") .setUri(mediaUri) .setMediaMetadata( MediaMetadata.Builder() .setArtist("David Bowie") .setTitle("Heroes") .setArtworkUri(artworkUri) .build() ) .build() mediaController.setMediaItem(mediaItem) mediaController.prepare() mediaController.play()
جاوا
MediaItem mediaItem = new MediaItem.Builder() .setMediaId("media-1") .setUri(mediaUri) .setMediaMetadata( new MediaMetadata.Builder() .setArtist("David Bowie") .setTitle("Heroes") .setArtworkUri(artworkUri) .build()) .build(); mediaController.setMediaItem(mediaItem); mediaController.prepare(); mediaController.play();
برنامهها میتوانند دکمههای فرمان کنترلهای Android Media را سفارشی کنند. درباره سفارشی کردن کنترلهای Android Media بیشتر بخوانید .
سفارشی سازی اعلان
برای سفارشی کردن اعلان، یک MediaNotification.Provider
با DefaultMediaNotificationProvider.Builder
یا با ایجاد یک پیاده سازی سفارشی از رابط ارائه دهنده ایجاد کنید. ارائه دهنده خود را با setMediaNotificationProvider
به MediaSessionService
خود اضافه کنید.
از سرگیری پخش
دکمههای رسانه، دکمههای سختافزاری هستند که در دستگاههای Android و سایر دستگاههای جانبی، مانند دکمه پخش یا مکث در هدست بلوتوث یافت میشوند. Media3 ورودی های دکمه رسانه را برای شما در هنگام اجرای سرویس کنترل می کند.
گیرنده دکمه رسانه Media3 را اعلام کنید
Media3 دارای یک API است که به کاربران امکان می دهد پس از پایان برنامه و حتی پس از راه اندازی مجدد دستگاه، پخش را از سر بگیرند. به طور پیش فرض، از سرگیری پخش خاموش است. این بدان معناست که کاربر نمی تواند پخش را در زمانی که سرویس شما اجرا نمی کند، از سر بگیرد. برای شرکت کردن، ابتدا MediaButtonReceiver
را در مانیفست خود اعلام کنید:
<receiver android:name="androidx.media3.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
اجرای مجدد تماس مجدد پخش
هنگامی که از سرگیری پخش توسط یک دستگاه بلوتوث یا ویژگی از سرگیری رابط کاربری سیستم اندروید درخواست می شود، روش پاسخ به تماس onPlaybackResumption()
فراخوانی می شود.
کاتلین
override fun onPlaybackResumption( mediaSession: MediaSession, controller: ControllerInfo ): ListenableFuture<MediaItemsWithStartPosition> { val settable = SettableFuture.create<MediaItemsWithStartPosition>() scope.launch { // Your app is responsible for storing the playlist and the start position // to use here val resumptionPlaylist = restorePlaylist() settable.set(resumptionPlaylist) } return settable }
جاوا
@Override public ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption( MediaSession mediaSession, ControllerInfo controller ) { SettableFuture<MediaItemsWithStartPosition> settableFuture = SettableFuture.create(); settableFuture.addListener(() -> { // Your app is responsible for storing the playlist and the start position // to use here MediaItemsWithStartPosition resumptionPlaylist = restorePlaylist(); settableFuture.set(resumptionPlaylist); }, MoreExecutors.directExecutor()); return settableFuture; }
اگر پارامترهای دیگری مانند سرعت پخش، حالت تکرار یا حالت shuffle را ذخیره کردهاید، onPlaybackResumption()
مکان خوبی برای پیکربندی پخشکننده با این پارامترها قبل از اینکه Media3 پخشکننده را آماده کند و پس از اتمام تماس مجدد شروع به پخش کند، است.
پیکربندی کنترلر پیشرفته و سازگاری با عقب
یک سناریوی رایج استفاده از MediaController
در رابط کاربری برنامه برای کنترل پخش و نمایش لیست پخش است. در همان زمان، این جلسه در معرض مشتریان خارجی مانند کنترلهای رسانه Android و Assistant در تلفن همراه یا تلویزیون، Wear OS برای ساعتها و Android Auto در اتومبیلها قرار میگیرد. برنامه نمایشی جلسه Media3 نمونه ای از برنامه هایی است که چنین سناریویی را پیاده سازی می کند.
این مشتریان خارجی ممکن است از APIهایی مانند MediaControllerCompat
کتابخانه قدیمی AndroidX یا android.media.session.MediaController
چارچوب Android استفاده کنند. Media3 کاملاً با کتابخانه قدیمی سازگار است و قابلیت همکاری با API فریمورک اندروید را فراهم می کند.
از کنترلر اعلان رسانه استفاده کنید
درک این نکته مهم است که این کنترلرهای قدیمی یا چارچوب همان مقادیر را از چارچوب PlaybackState.getActions()
و PlaybackState.getCustomActions()
می خوانند. برای تعیین اقدامات و اقدامات سفارشی جلسه چارچوب، یک برنامه می تواند از کنترلر اعلان رسانه استفاده کند و دستورات موجود و طرح بندی سفارشی خود را تنظیم کند. این سرویس کنترلکننده اعلان رسانه را به جلسه شما متصل میکند، و جلسه از ConnectionResult
بازگردانده شده توسط onConnect()
پاسخ تماس شما برای پیکربندی اقدامات و اقدامات سفارشی جلسه چارچوب استفاده میکند.
با توجه به یک سناریوی فقط برای موبایل، یک برنامه میتواند پیادهسازی MediaSession.Callback.onConnect()
را برای تنظیم دستورات موجود و طرحبندی سفارشی بهطور خاص برای جلسه چارچوب به شرح زیر ارائه دهد:
کاتلین
override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { if (session.isMediaNotificationController(controller)) { val sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build() val playerCommands = ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .build() // Custom layout and available commands to configure the legacy/framework session. return AcceptedResultBuilder(session) .setCustomLayout( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward)) ) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default custom layout for all other controllers. return AcceptedResultBuilder(session).build() }
جاوا
@Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { if (session.isMediaNotificationController(controller)) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build(); Player.Commands playerCommands = ConnectionResult.DEFAULT_PLAYER_COMMANDS .buildUpon() .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .build(); // Custom layout and available commands to configure the legacy/framework session. return new AcceptedResultBuilder(session) .setCustomLayout( ImmutableList.of( createSeekBackwardButton(customCommandSeekBackward), createSeekForwardButton(customCommandSeekForward))) .setAvailablePlayerCommands(playerCommands) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands without default custom layout for all other controllers. return new AcceptedResultBuilder(session).build(); }
مجوز Android Auto برای ارسال دستورات سفارشی
هنگام استفاده از MediaLibraryService
و برای پشتیبانی از Android Auto با برنامه تلفن همراه، کنترلکننده Android Auto به دستورات موجود نیاز دارد، در غیر این صورت Media3 دستورات سفارشی دریافتی از آن کنترلکننده را رد میکند:
کاتلین
override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { val sessionCommands = ConnectionResult.DEFAULT_SESSION_AND_LIBRARY_COMMANDS.buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build() if (session.isMediaNotificationController(controller)) { // [...] See above. } else if (session.isAutoCompanionController(controller)) { // Available session commands to accept incoming custom commands from Auto. return AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build() } // Default commands with default custom layout for all other controllers. return AcceptedResultBuilder(session).build() }
جاوا
@Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { SessionCommands sessionCommands = ConnectionResult.DEFAULT_SESSION_COMMANDS .buildUpon() .add(customCommandSeekBackward) .add(customCommandSeekForward) .build(); if (session.isMediaNotificationController(controller)) { // [...] See above. } else if (session.isAutoCompanionController(controller)) { // Available commands to accept incoming custom commands from Auto. return new AcceptedResultBuilder(session) .setAvailableSessionCommands(sessionCommands) .build(); } // Default commands without default custom layout for all other controllers. return new AcceptedResultBuilder(session).build(); }
برنامه نمایشی جلسه دارای یک ماژول خودرو است که پشتیبانی از سیستم عامل Automotive را نشان می دهد که به یک APK جداگانه نیاز دارد.