MediasessionService के साथ बैकग्राउंड में वीडियो चलाने की सुविधा

अक्सर, किसी ऐप्लिकेशन के फ़ोरग्राउंड में न होने पर भी मीडिया चलाना होता है. उदाहरण के लिए, जब कोई उपयोगकर्ता अपने डिवाइस को लॉक करता है या किसी दूसरे ऐप्लिकेशन का इस्तेमाल करता है, तब भी संगीत प्लेयर आम तौर पर संगीत चलाता रहता है. Media3 लाइब्रेरी, इंटरफ़ेस की एक सीरीज़ उपलब्ध कराती है. इसकी मदद से, बैकग्राउंड में संगीत चलाने की सुविधा का इस्तेमाल किया जा सकता है.

MediaSessionService का इस्तेमाल करना

बैकग्राउंड में वीडियो चलाने की सुविधा चालू करने के लिए, आपको Player और MediaSession को अलग सेवा में शामिल करना होगा. इससे, डिवाइस तब भी मीडिया चलाता रहेगा, जब आपका ऐप्लिकेशन स्क्रीन पर न हो.

MediaSessionService की मदद से, मीडिया सेशन को ऐप्लिकेशन की गतिविधि से अलग चलाया जा सकता है
पहली इमेज: MediaSessionService मीडिया सेशन को ऐप्लिकेशन की गतिविधि से अलग चलने देता है

किसी सेवा में प्लेयर को होस्ट करते समय, आपको MediaSessionService का इस्तेमाल करना चाहिए. ऐसा करने के लिए, MediaSessionService को एक्सटेंड करने वाली क्लास बनाएं और उसमें अपना मीडिया सेशन बनाएं.

MediaSessionService का इस्तेमाल करने से, Google Assistant, सिस्टम मीडिया कंट्रोल या Wear OS जैसे साथी डिवाइसों जैसे बाहरी क्लाइंट, आपकी सेवा को खोज सकते हैं, उससे कनेक्ट कर सकते हैं, और वीडियो चलाने की सुविधा को कंट्रोल कर सकते हैं. इसके लिए, उन्हें आपके ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) की गतिविधि को ऐक्सेस करने की ज़रूरत नहीं होती. असल में, एक ही समय में एक ही MediaSessionService से कनेक्ट किए गए कई क्लाइंट ऐप्लिकेशन हो सकते हैं. साथ ही, हर ऐप्लिकेशन का अपना MediaController हो सकता है.

सेवा की लाइफ़साइकल लागू करना

आपको अपनी सेवा के लाइफ़साइकल के तीन तरीके लागू करने होंगे:

  • onCreate() को तब कॉल किया जाता है, जब पहला कंट्रोलर कनेक्ट होने वाला होता है और सेवा को शुरू किया जाता है. Player और MediaSession बनाने के लिए, यह सबसे अच्छी जगह है.
  • onTaskRemoved(Intent) को तब कॉल किया जाता है, जब उपयोगकर्ता हाल ही के टास्क में से ऐप्लिकेशन को खारिज करता है. अगर वीडियो चल रहा है, तो ऐप्लिकेशन के पास सेवा को फ़ोरग्राउंड में चलाए रखने का विकल्प होता है. अगर प्लेयर को रोका गया है, तो इसका मतलब है कि सेवा फ़ोरग्राउंड में नहीं है और उसे रोकना होगा.
  • सेवा को बंद किए जाने पर, onDestroy() को कॉल किया जाता है. प्लेयर और सेशन समेत सभी संसाधनों को रिलीज़ करने की ज़रूरत है.

Kotlin

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()
  }
}

Java

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();
  }
}

बैकग्राउंड में वीडियो चलाने के विकल्प के तौर पर, ऐप्लिकेशन में यह सुविधा भी हो सकती है कि जब उपयोगकर्ता ऐप्लिकेशन को बंद कर दे, तो सेवा को बंद कर दिया जाए:

Kotlin

override fun onTaskRemoved(rootIntent: Intent?) {
  val player = mediaSession.player
  if (player.playWhenReady) {
    // Make sure the service is not in foreground.
    player.pause()
  }
  stopSelf()
}

Java

@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() तरीके को बदलें जो सेवा बनाते समय बनाया गया था.

Kotlin

class PlaybackService : MediaSessionService() {
  private var mediaSession: MediaSession? = null
  // [...] lifecycle methods omitted

  override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? =
    mediaSession
}

Java

public class PlaybackService extends MediaSessionService {
  private MediaSession mediaSession = null;
  // [...] lifecycle methods omitted

  @Override
  public MediaSession onGetSession(MediaSession.ControllerInfo controllerInfo) {
    return mediaSession;
  }
}

मेनिफ़ेस्ट में सेवा की जानकारी दें

किसी ऐप्लिकेशन को फ़ोरग्राउंड सेवा चलाने के लिए अनुमति की ज़रूरत होती है. मेनिफ़ेस्ट में FOREGROUND_SERVICE अनुमति जोड़ें. अगर एपीआई 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 (एपीआई लेवल 29) और उसके बाद के वर्शन वाले डिवाइस पर चल रहा हो, तो आपको एक ऐसा foregroundServiceType तय करना होगा जिसमें mediaPlayback शामिल हो.

MediaController का इस्तेमाल करके प्लेबैक कंट्रोल करना

आपके प्लेयर यूज़र इंटरफ़ेस (यूआई) वाले ऐक्टिविटी या फ़्रैगमेंट में, MediaController का इस्तेमाल करके यूज़र इंटरफ़ेस (यूआई) और मीडिया सेशन के बीच लिंक बनाया जा सकता है. आपका यूज़र इंटरफ़ेस (यूआई), सेशन के अंदर प्लेयर को निर्देश भेजने के लिए मीडिया कंट्रोलर का इस्तेमाल करता है. MediaController बनाने और उसका इस्तेमाल करने के बारे में जानने के लिए, MediaController बनाएं गाइड देखें.

यूज़र इंटरफ़ेस (यूआई) के निर्देशों को मैनेज करना

MediaSession को कंट्रोलर से अपनी MediaSession.Callback की मदद से निर्देश मिलते हैं. MediaSession को शुरू करने पर, MediaSession.Callback डिफ़ॉल्ट रूप से लागू हो जाता है. यह उन सभी निर्देशों को अपने-आप मैनेज करता है जिन्हें MediaController आपके प्लेयर को भेजता है.

सूचना

MediaSessionService, आपके लिए अपने-आप एक MediaNotification बनाता है जो ज़्यादातर मामलों में काम करता है. डिफ़ॉल्ट रूप से, पब्लिश की गई सूचना एक MediaStyle सूचना होती है. यह आपके मीडिया सेशन की नई जानकारी के साथ अपडेट रहती है और प्लेबैक कंट्रोल दिखाती है. MediaNotification को आपके सेशन के बारे में जानकारी होती है और इसका इस्तेमाल करके, उसी सेशन से जुड़े दूसरे ऐप्लिकेशन पर वीडियो चलाने को कंट्रोल किया जा सकता है.

उदाहरण के लिए, MediaSessionService का इस्तेमाल करने वाला संगीत स्ट्रीमिंग ऐप्लिकेशन, एक ऐसा MediaNotification बनाएगा जिसमें आपके MediaSession कॉन्फ़िगरेशन के आधार पर, प्ले किए जा रहे मौजूदा मीडिया आइटम का टाइटल, कलाकार, और एल्बम आर्ट दिखेगा. साथ ही, प्लेबैक कंट्रोल भी दिखेंगे.

मीडिया में ज़रूरी मेटाडेटा दिया जा सकता है या मीडिया आइटम के हिस्से के तौर पर इसका एलान किया जा सकता है. जैसे, इस स्निपेट में:

Kotlin

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()

Java

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 मीडिया कंट्रोल के कमांड बटन को पसंद के मुताबिक बना सकते हैं. Android के मीडिया कंट्रोल को पसंद के मुताबिक बनाने के बारे में ज़्यादा पढ़ें.

सूचनाएं पसंद के मुताबिक बनाना

सूचना को पसंद के मुताबिक बनाने के लिए, DefaultMediaNotificationProvider.Builder के साथ MediaNotification.Provider बनाएं या सेवा देने वाली कंपनी के इंटरफ़ेस को पसंद के मुताबिक बनाएं. setMediaNotificationProvider का इस्तेमाल करके, सेवा देने वाली कंपनी को अपने MediaSessionService में जोड़ें.

रोके गए वीडियो को फिर से चलाना

मीडिया बटन, Android डिवाइसों और दूसरे सहायक डिवाइसों पर मिलने वाले हार्डवेयर बटन होते हैं. जैसे, ब्लूटूथ हेडसेट पर चलाएं या रोकें बटन. Media3, सेवा के चालू होने पर मीडिया बटन के इनपुट को मैनेज करता है.

Media3 मीडिया बटन रिसीवर के बारे में जानकारी देना

Media3 में एक एपीआई शामिल है, जिसकी मदद से उपयोगकर्ता किसी ऐप्लिकेशन के बंद होने के बाद भी वीडियो चलाना जारी रख सकते हैं. साथ ही, डिवाइस को फिर से चालू करने के बाद भी ऐसा किया जा सकता है. डिफ़ॉल्ट रूप से, वीडियो को फिर से शुरू करने की सुविधा बंद रहती है. इसका मतलब है कि उपयोगकर्ता आपकी सेवा के न चलने पर, वीडियो को फिर से शुरू नहीं कर सकता. ऑप्ट-इन करने के लिए, अपने मेनिफ़ेस्ट में MediaButtonReceiver का एलान करें:

<receiver android:name="androidx.media3.session.MediaButtonReceiver"
  android:exported="true">
  <intent-filter>
    <action android:name="android.intent.action.MEDIA_BUTTON" />
  </intent-filter>
</receiver>

वीडियो को फिर से शुरू करने के लिए कॉलबैक लागू करें

जब ब्लूटूथ डिवाइस या Android सिस्टम यूज़र इंटरफ़ेस (यूआई) की फिर से चलाने की सुविधा से प्लेबैक फिर से शुरू करने का अनुरोध किया जाता है, तो onPlaybackResumption() कॉलबैक का तरीका इस्तेमाल किया जाता है.

Kotlin

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
}

Java

@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;
}

अगर आपने वीडियो चलाने की स्पीड, रिपीट मोड या शफ़ल मोड जैसे अन्य पैरामीटर सेव किए हैं, तो Media3 के प्लेयर को तैयार करने और कॉलबैक पूरा होने पर वीडियो चलाने से पहले, इन पैरामीटर के हिसाब से प्लेयर को कॉन्फ़िगर करने के लिए onPlaybackResumption() सबसे सही जगह है.

बेहतर कंट्रोलर कॉन्फ़िगरेशन और पुराने सिस्टम के साथ काम करने की सुविधा

आम तौर पर, ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) में MediaController का इस्तेमाल, वीडियो चलाने और प्लेलिस्ट दिखाने के लिए किया जाता है. साथ ही, यह सेशन Android मीडिया कंट्रोल, मोबाइल या टीवी पर Assistant, स्मार्टवॉच के लिए Wear OS, और कारों में Android Auto जैसे बाहरी क्लाइंट को दिखाया जाता है. Media3 सेशन डेमो ऐप्लिकेशन एक ऐसे ऐप्लिकेशन का उदाहरण है जो इस तरह की स्थिति लागू करता है.

ये बाहरी क्लाइंट, पुरानी AndroidX लाइब्रेरी के MediaControllerCompat या Android फ़्रेमवर्क के android.media.session.MediaController जैसे एपीआई इस्तेमाल कर सकते हैं. Media3, लेगसी लाइब्रेरी के साथ पूरी तरह से काम करता है. साथ ही, यह Android फ़्रेमवर्क एपीआई के साथ इंटरऑपरेबिलिटी की सुविधा देता है.

मीडिया कंट्रोल करने से जुड़ी सूचना का इस्तेमाल करना

यह समझना ज़रूरी है कि ये लेगसी या फ़्रेमवर्क कंट्रोलर, फ़्रेमवर्क PlaybackState.getActions() और PlaybackState.getCustomActions() से एक ही वैल्यू पढ़ते हैं. फ़्रेमवर्क सेशन की कार्रवाइयों और कस्टम कार्रवाइयों का पता लगाने के लिए, कोई ऐप्लिकेशन मीडिया सूचना कंट्रोलर का इस्तेमाल कर सकता है. साथ ही, इसके उपलब्ध निर्देशों और कस्टम लेआउट को सेट कर सकता है. यह सेवा, मीडिया सूचना कंट्रोलर को आपके सेशन से कनेक्ट करती है. साथ ही, फ़्रेमवर्क सेशन की कार्रवाइयों और कस्टम कार्रवाइयों को कॉन्फ़िगर करने के लिए, आपके कॉलबैक के onConnect() से मिले ConnectionResult का इस्तेमाल करती है.

सिर्फ़ मोबाइल पर काम करने वाले ऐप्लिकेशन के लिए, MediaSession.Callback.onConnect() को लागू किया जा सकता है. इससे, फ़्रेमवर्क सेशन के लिए उपलब्ध निर्देश और पसंद के मुताबिक लेआउट सेट किए जा सकते हैं. इसके लिए, यह तरीका अपनाएं:

Kotlin

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()
}

Java

@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 उस कंट्रोलर से आने वाले कस्टम निर्देशों को स्वीकार नहीं करेगा:

Kotlin

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()
}

Java

@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 OS के साथ काम करने की सुविधा के बारे में पता चलता है. इसके लिए, एक अलग APK की ज़रूरत होती है.