Créez un modèle pour un lecteur de musique très courant.
De plus, nous prendrons une méthode qui peut prendre en charge Wear and Auto sans implémenter de traitement dédié.
Il n'y a pas d'articles en japonais sur les lecteurs de musique Android. J'ai fait un article parce que j'ai réussi à le mettre en œuvre avec divers efforts. Ce sera plus long, mais restez en contact si vous le souhaitez.
Si vous souhaitez voir uniquement l'implémentation, passez à l'implémentation.
Lorsque vous créez un lecteur de musique qui fonctionne sur un PC, utilisez diverses méthodes d'instances telles que MediaPlayer. Je pense que le principal moyen de le faire était de l'appeler en cliquant sur le bouton collé sur le formulaire.
Cependant, les plates-formes mobiles comme Android et iOS de ces dernières années ne se limitent pas aux smartphones. Il a été intégré à divers appareils tels que les montres, les audios de voiture et les téléviseurs. Ensuite, il est devenu nécessaire de se connecter à un smartphone, d'utiliser le lecteur à partir d'une montre intelligente ou d'un autoradio et d'afficher les informations sur la chanson. Si un développeur écrit du code pour divers appareils (pour Android Wear, Auto, TV, etc.), ce sera un lourd fardeau. Par conséquent, il y avait un mouvement pour standardiser ce traitement dans la série Android, et il a été implémenté dans Android 5.0. C'est "MediaSession".
La figure ci-dessus montre le mécanisme autour de la musique sur Android. Celles-ci sont similaires aux relations serveur-client.
La Media Session
agit comme un serveur.
Il fournit des fonctions pour faire fonctionner les chansons, l'état du lecteur, les informations sur les chansons, etc. aux clients connectés.
Le client demande une opération à partir de la session multimédia en utilisant Media Controller
, Media Button Intent
, etc.
Affiche l'état du lecteur et les informations sur la chanson à notifier.
référence: Understanding MediaSession (Part 1/4) Understanding MediaSession (Part 2/4)
Les deux sont utilisés pour demander à la Media Session
de faire fonctionner le morceau (lecture, arrêt, saut, etc.).
La différence est que «Media Button Intent» ne peut demander que des opérations, tandis que «Media Controller» peut également obtenir le statut du lecteur et des informations sur la chanson.
À propos, Media Session se voit attribuer un identifiant unique appelé «jeton de session». Si vous utilisez l'analogie du serveur ci-dessus, c'est comme une adresse IP. En d'autres termes, si vous connaissez le Token, vous pouvez vous connecter de l'extérieur.
Lors de la connexion, passez Token au constructeur de Media Controller
.
Je pense qu'il est difficile d'imaginer comment utiliser Media Session à partir de l'explication ci-dessus, j'ai donc fait un diagramme.
En regardant la figure, il semble que vous faites quelque chose d'assez ennuyeux. Cependant, il est très logique de prendre la forme ci-dessus lors de la création d'un lecteur de musique sur la plate-forme Android.
Je pense que le lecteur de musique que vous utilisez habituellement continuera à jouer même si vous supprimez l'activité des tâches récentes. Ou même si vous ne le supprimez pas de la tâche, le passage à une autre application peut détruire l'activité lorsque vous manquez de mémoire. En d'autres termes, si vous laissez une partie du traitement lié à la lecture à l'activité, vous ne pourrez pas continuer la lecture lorsque l'activité est détruite.
Par conséquent, l'idée importante dans la création d'un lecteur de musique fonctionnant sur Android est d'implémenter toutes les fonctions pour lire des chansons du côté du service, et du côté de l'activité, d'effectuer des opérations telles que la lecture et la pause selon l'opération de l'utilisateur `` Session multimédia ''. C'est ** pour clarifier la division des rôles **, comme afficher simplement les informations de la chanson en cours de lecture depuis la Media Session
sur l'interface utilisateur.
Vous n'avez pas à craindre que l'activité soit détruite en affichant simplement les informations de la chanson à l'utilisateur et en fournissant le contrôle pour contrôler le lecteur.
Lorsque l'activité est régénérée, il vous suffit de vous reconnecter à la Media Session
.
Référence: Présentation des applications multimédias
Dans l'exemple précédent, Media Session a été implémentée dans un service normal.
Cependant, dans la pratique, il est recommandé de l'implémenter en utilisant le Media Browser Service
, qui est une extension du Service.
La «Session multimédia» mentionnée précédemment agit comme un serveur qui fournit des opérations et des informations sur les chansons, et le «Service de navigateur multimédia» agit comme une bibliothèque musicale. Si vous l'implémentez conformément aux spécifications de Media Browser, vous pourrez utiliser des fonctions telles que la sélection automatique de chansons dans Wear et Auto. Il encapsule également des opérations telles que la liaison et l'obtention d'un jeton de session.
Il semble qu'il n'y ait pas beaucoup de différence avec l'exemple précédent, car les noms des étapes ① et ② sont faciles à comprendre. En fait, une fonction pour fournir une liste de chansons sera ajoutée ici.
référence: Building a Media Browser Service Building a Media Browser Client
Ensuite, il s'agit de parcourir la bibliothèque. En bref, c'est une fonction qui affiche une liste de chansons pouvant être jouées à l'utilisateur.
Je vous ai dit la chanson que vous souhaitez jouer dans ④ de l'exemple précédent, mais si vous n'avez pas de liste de chansons en premier lieu, vous ne pouvez pas faire de demande. Comme mentionné ci-dessus, Media Browser dispose d'un mécanisme pour fournir une bibliothèque musicale.
La figure ci-dessus montre le processus de connexion au Media Browser Service
et l'obtention d'une liste de chansons.
Appelez subscribe (MediaId)
pour demander un élément multimédia du navigateur multimédia au service de navigateur multimédia
.
Ensuite, la liste des éléments multimédias est renvoyée. Les étapes ③ et ④ sont exécutées chaque fois que nécessaire.
Dans Media Browser Service
, tous les éléments des chansons, albums et genres sont représentés par la chaîne de caractères MediaId.
Et il y a MediaBrowser.MediaItem dans la classe qui contient le MediaId et les données qui l'accompagnent.
Le contenu détenu par MediaItem a un indicateur qui identifie s'il a un élément enfant, un élément qui a le rôle d'un soi-disant dossier ou un élément de musique lisible qui n'a pas d'enfants. Voir la figure ci-dessous.
Orange est l'élément qui a l'indicateur de dossier, et bleu est l'élément qui a l'indicateur jouable. La chaîne écrite indique MediaId.
Ne vous méprenez pas ici, le MediaItem marqué par un dossier lui-même n'a pas réellement d'éléments enfants en tant qu'objets. Dans la figure ci-dessus, il a été écrit comme une structure arborescente, mais il n'y a pas de relation parent-enfant en termes d'objets.
Je pense que c'est difficile à comprendre, alors donnons un exemple concret.
L'appel de Media Browser subscribe (mediaId)
appellera ʻonLoadChildren () du
Media Browser Serviceconnecté et retournera le contenu envoyé. Appelez initialement
subscribe (" root "). Ensuite,
{" Genre "," Album "}sera retourné. Supposons qu'un utilisateur veuille être par genre. Puis appelez
subscribe (" Genre "). Puis
{" Genre: Rock, "Genre: Jazz"} sera retourné. Allez ensuite dans
subscribe (" Genre: Rock ")et vous obtiendrez
{" music1 "," music2 "}`.
Lorsque l'utilisateur sélectionne music1, le contrôleur multimédia demande que le morceau avec MediaId «music1» soit lu.
Je l'ai écrit comme si la musique1 et la musique2 étaient automatiquement renvoyées lorsque j'ai appelé subscribe (" Genre: Rock ")
, mais j'ai dû écrire le mécanisme moi-même. Par exemple, si "Genre:" est ajouté au début de MediaId, il est considéré comme un identifiant qui répertorie toutes les chansons d'un genre spécifique et la chanson est renvoyée.
En d'autres termes, il est nécessaire de comprendre ce qui est requis uniquement par l'ID de média et de renvoyer la chanson ou la sous-catégorie.
Cependant, dans cet extrait, je donne simplement l'impression que deux chansons sont suspendues à la racine.
Les cours préparés à l'avance sont les suivants.
Nom | La description |
---|---|
MediaDescription | Une classe qui contient un minimum de métadonnées. |
MediaMetadata | LesinformationssurleschansonsfourniesparMediaSessionsontdansceformat.Enplusdesmétadonnéesminimales,vouspouvezdéfinirdesdonnéespersonnaliséescommes'ils'agissaitd'untableauassociatif.getDescription()Vous pouvez également générer une description de média avec. |
MediaBrowser.MediaItem | Le format pour récupérer les informations de MediaBrowserService. En plus des chansons, des éléments avec des éléments enfants tels que des genres et des albums peuvent également être exprimés. Il contient une description du média. |
MediaSession.QueueItem | Il contient une description de média et est utilisé pour obtenir des informations sur les chansons de la file d'attente. Par conséquent, les informations d'index sont jointes. |
Vous pouvez voir que nous utilisons «Media Browser» et «Media Controller» pour nous connecter au «Media Browser Service» et à la «Media Session». En fait, Wear et Auto ont également ces deux. Dans les exemples jusqu'à présent, il était connecté depuis Activity, mais Wear et Auto sont simplement connectés de la même manière, il n'est donc pas nécessaire d'écrire un processus dédié.
Les informations sur les morceaux livrés à partir de Media Session s'affichent. De plus, si vous définissez un élément de file d'attente dans Media Session, une liste de chansons s'affiche ci-dessous.
Le côté Wear a également MediaBrowser.subscribe, qui obtient une liste de chansons et les affiche. (Cette fois, seules les chansons, mais en fonction de l'implémentation de onLoadChildren, les menus par album ou par genre peuvent également être affichés)
Audio Focus
Ce n'est pas un gros problème étant donné la présence de sessions multimédia et de navigateurs, mais c'est aussi une fonctionnalité importante.
ʻAudio Focus est la version audio de Focus dans l'interface utilisateur. Assurez-vous qu'une seule application possède ʻAudio Focus
. Et si seules les applications avec ʻAudio Focus` jouent des chansons, vous pouvez empêcher plusieurs applications de lire de la musique en même temps.
Cependant, le système Android ne vous avertit que lorsque la mise au point atteint ou échoue, vous écrivez donc votre propre traitement, par exemple une pause lorsque la mise au point est perdue (par exemple, lorsqu'une autre application commence à jouer). est nécessaire.
Cette fois, nous utiliserons les données musicales stockées dans le dossier Assets. J'ai senti que ce serait compliqué quand l'histoire du stockage était impliquée, alors j'ai fait un compromis en pratiquant l'utilisation de Media Session. Les métadonnées de la chanson sont également codées en dur. Notez s'il vous plaît.
** Tout le code est ici **
Étant donné que le canal de notification est ajouté dans Android 8.0 et qu'il sera nécessaire de créer une branche en fonction de la version, cette fois, la cible est fixée à 25 et le minimum à 21. (Nous n'acceptons pas Tsukkomi que ce n'est pas imadoki ^^;)
MediaPlayer fonctionne bien car il ne lit que les MP3, mais j'utilise ExoPlayer que j'utilise habituellement. ExoPlayer est un lecteur multimédia développé par Google et est conçu pour remplacer le MediaPlayer par défaut, qui peut avoir différentes implémentations en fonction de la version et du modèle. Au fur et à mesure de sa mise à jour, le nombre de formats pouvant être lus augmentera et il comportera également des fonctions telles que la diffusion en direct. Comment utiliser ExoPlayer Comment utiliser ExoPlayer Je pense que ce sera utile.
Il utilise également la bibliothèque de support. Je recommande également le fonctionnaire de Google car il absorbe les différences entre les versions.
Par conséquent, utilisez celui avec Compat à la fin, tel que MediaSessionCompat
.
Les bibliothèques dépendantes sont les suivantes
build.gradle
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support:design:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.google.android.exoplayer:exoplayer:2.6.1'
}
・ Maina c Chity ty Activité affichée · Bibliothèque musicale Je l'ai emprunté à l'échantillon officiel de Google. Les informations de la chanson sont codées en dur. ・ Service de musique Mise en œuvre du service Meida Browser.
Vous trouverez ci-dessous un diagramme de la relation entre les rappels et les méthodes.
AndroidManifest.xml
AndroidManifest.xml
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MusicService">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
</application>
Ceci est un extrait de la partie Application.
Enregistrez MusicService
et configurez également le récepteur pour recevoir MediaButtonIntent
.
MainActivity UI ** Cliquez ici pour le fichier xml de l'interface utilisateur (https://github.com/SIY1121/MediaSessionSample/blob/master/app/src/main/res/layout/activity_main.xml) **
MainActivity.java
MediaBrowserCompat mBrowser;
MediaControllerCompat mController;
//L'interface utilisateur est omise
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Configuration de l'interface utilisateur (omis)
//Gardez le service en marche
//Cela n'est pas nécessaire si le service peut être arrêté en même temps que l'activité est détruite.
startService(new Intent(this, MusicService.class));
//Initialiser MediaBrowser
mBrowser = new MediaBrowserCompat(this, new ComponentName(this, MusicService.class), connectionCallback, null);
//Lien(Service de liaison)
mBrowser.connect();
}
//Rappel appelé lors de la connexion
private MediaBrowserCompat.ConnectionCallback connectionCallback = new MediaBrowserCompat.ConnectionCallback() {
@Override
public void onConnected() {
try {
//Depuis le jeton de session peut être obtenu lorsque la connexion est terminée
//Créer un contrôleur multimédia en l'utilisant
mController = new MediaControllerCompat(MainActivity.this, mBrowser.getSessionToken());
//Définir un rappel lorsque l'état du lecteur ou les informations sur la chanson envoyées par le service changent
mController.registerCallback(controllerCallback);
//S'il est déjà en cours de lecture, appelez le rappel lui-même et mettez à jour l'interface utilisateur
if (mController.getPlaybackState() != null && mController.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING) {
controllerCallback.onMetadataChanged(mController.getMetadata());
controllerCallback.onPlaybackStateChanged(mController.getPlaybackState());
}
} catch (RemoteException ex) {
ex.printStackTrace();
Toast.makeText(MainActivity.this, ex.getMessage(), Toast.LENGTH_LONG).show();
}
//Obtenez une liste de chansons jouables du service
mBrowser.subscribe(mBrowser.getRoot(), subscriptionCallback);
}
};
//Rappel qui est appelé lorsque vous vous abonnez
private MediaBrowserCompat.SubscriptionCallback subscriptionCallback = new MediaBrowserCompat.SubscriptionCallback() {
@Override
public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaBrowserCompat.MediaItem> children) {
//Demande de lecture de la première chanson si elle n'est pas déjà en cours de lecture
if (mController.getPlaybackState() == null)
Play(children.get(0).getMediaId());
}
};
Commencez par démarrer Music Service.
Après cela, MediaBrowser est initialisé et connecté.
Lorsque la connexion est terminée, ʻonConnected () est appelé, alors récupérez le Token avec
mBrowser.getSessionToken () et passez-le au constructeur de
MediaController` pour se connecter à la session média.
Appelez ensuite mBrowser.subscribe (mBrowser.getRoot (), subscriptionCallback)
pour demander une liste de chansons.
Lorsque le côté Service envoie une liste, le callback set ʻonChildrenLoaded () `est appelé, donc cette fois nous allons jouer le premier élément.
MainActivity.java
private void Play(String id) {
//Obtenez TransportControl pour demander l'opération de MediaController au service
//Lorsque playFromMediaId est appelé, onPlayFromMediaId dans le rappel de MediaSession côté service est appelé.
mController.getTransportControls().playFromMediaId(id, null);
}
Vous pouvez obtenir le TransportControl
utilisé pour demander une opération à la MediaSession avec mController.getTransportControls ()
.
MainActivity.java
//Rappel de MediaController
private MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() {
//Appelé lorsque les informations du morceau en cours de lecture sont modifiées
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
textView_title.setText(metadata.getDescription().getTitle());
imageView.setImageBitmap(metadata.getDescription().getIconBitmap());
textView_duration.setText(Long2TimeString(metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)));
seekBar.setMax((int) metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
}
//Appelé lorsque l'état du joueur change
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
//Changer le comportement du bouton et l'icône en fonction de l'état du joueur
if (state.getState() == PlaybackStateCompat.STATE_PLAYING) {
button_play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mController.getTransportControls().pause();
}
});
button_play.setImageResource(R.drawable.exo_controls_pause);
} else {
button_play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mController.getTransportControls().play();
}
});
button_play.setImageResource(R.drawable.exo_controls_play);
}
textView_position.setText(Long2TimeString(state.getPosition()));
seekBar.setProgress((int) state.getPosition());
}
};
Lorsque l'état du lecteur ou du morceau à lire change, des informations sont envoyées à partir de MediaSession et le rappel de MediaController est appelé. Reflétez le changement dans l'interface utilisateur.
MusicService Créez un service qui hérite de MediaBrowserService.
MusicService.java
//Appelé lors de la connexion à un client
//Décidez si vous souhaitez vous connecter à partir du nom du package, etc.
//Autorisation de connexion lorsqu'une chaîne de caractères arbitraire est renvoyée
//Refuser la connexion avec null
//Autoriser toutes les connexions cette fois
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName,
int clientUid,
Bundle rootHints) {
Log.d(TAG, "Connected from pkg:" + clientPackageName + " uid:" + clientUid);
return new BrowserRoot(ROOT_ID, null);
}
//Appelé lorsque le côté client appelle s'abonner
//Renvoie le contenu de la bibliothèque musicale
//Également utilisé pour la liste des chansons affichées dans Wear and Auto
//Par défaut, la chaîne retournée par onGetRoot est passée à parentMediaId
//L'ID est également transmis lorsque vous sélectionnez un MediaItem qui a un élément enfant sur l'écran du navigateur.
@Override
public void onLoadChildren(
@NonNull final String parentMediaId,
@NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
if (parentMediaId.equals(ROOT_ID))
//Envoyer une liste de chansons au client
result.sendResult(MusicLibrary.getMediaItems());
else//Cette fois ROOT_Non valide sauf ID
result.sendResult(new ArrayList<MediaBrowserCompat.MediaItem>());
}
ʻOnGetRoot () est appelé lors de la connexion et ʻonLoadChildren ()
est appelé lors de l'inscription. Puisque ʻonLoadChildren () est censé prendre du temps, cela prend la forme d'envoyer le résultat à l'objet résultat au lieu de le renvoyer avec return. Si vous pouvez le renvoyer immédiatement, utilisez
result.sendResult. Si vous souhaitez le renvoyer ultérieurement de manière asynchrone, utilisez
result.detatch ()`.
MusicService.java
final String TAG = MusicService.class.getSimpleName();//Balise de journal
final String ROOT_ID = "root";//ID onGetRoot pour retourner au client/Utilisé dans onLoadChildren
Handler handler;//Manutentionnaire pour le tournage régulier
MediaSessionCompat mSession;//Session médiatique du rôle principal
AudioManager am;//Gestionnaire de gestion des AudioFoucs
int index = 0;//Index en cours de lecture
ExoPlayer exoPlayer;//La substance du lecteur de musique
List<MediaSessionCompat.QueueItem> queueItems = new ArrayList<>();//Liste à utiliser pour la file d'attente
@Override
public void onCreate() {
super.onCreate();
//Obtenez AudioManager
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//Initialiser MediaSession
mSession = new MediaSessionCompat(getApplicationContext(), TAG);
//Définissez les fonctions fournies par cette session multimédia
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | //Boutons de poignée tels que des écouteurs
MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS | //Prend en charge l'utilisation des commandes de file d'attente
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); //Fournit des commandes pour la lecture, l'arrêt, le saut, etc.
//Définir le rappel en fonction de l'opération du client
mSession.setCallback(callback);
//Définir le jeton de session dans MediaBrowserService
setSessionToken(mSession.getSessionToken());
//Lorsque les métadonnées de la session multimédia et l'état du lecteur sont mis à jour
//Créer une notification/Mise à jour
mSession.getController().registerCallback(new MediaControllerCompat.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
CreateNotification();
}
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
CreateNotification();
}
});
//Ajouter un élément à la file d'attente
int i = 0;
for (MediaBrowserCompat.MediaItem media : MusicLibrary.getMediaItems()) {
queueItems.add(new MediaSessionCompat.QueueItem(media.getDescription(), i));
i++;
}
mSession.setQueue(queueItems);//La file d'attente s'affiche dans Wear and Auto
//Initialisation d'exoPlayer
exoPlayer = ExoPlayerFactory.newSimpleInstance(getApplicationContext(), new DefaultTrackSelector());
//Définir l'écouteur d'événement du joueur
exoPlayer.addListener(eventListener);
handler = new Handler();
//Mettre à jour les informations de lecture toutes les 500 ms
handler.postDelayed(new Runnable() {
@Override
public void run() {
//Mettre à jour pendant la lecture
if (exoPlayer.getPlaybackState() == Player.STATE_READY && exoPlayer.getPlayWhenReady())
UpdatePlaybackState();
//Courez à nouveau
handler.postDelayed(this, 500);
}
}, 500);
}
Comme le montant est important, je vais l'expliquer brièvement.
Tout d'abord, à propos de mSession.setFlags ()
qui est fait après l'initialisation de MediaSession.
Vous pouvez activer la fonction de session multimédia en définissant un indicateur.
Veuillez noter qu'aucune fonction ne peut être utilisée si elle n'est pas réglée en sens inverse.
Puis setSessionToken (mSession.getSessionToken ())
Cela définit la valeur retournée par getSessionToken ()
sur le client (MediaBrowser).
De plus, étant donné qu'ExoPlayer sera utilisé cette fois, nous initialiserons ExoPlayer.
Enfin, le gestionnaire est utilisé pour appeler ʻUpdatePlaybackState () `à intervalles réguliers. Ce processus sera expliqué plus loin.
Il s'agit de la partie associée à Transport Control qui a été utilisée pour demander des opérations à MediaSession dans MainActivity.
MusicService.java
//Rappel pour la session média
private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {
//Lire à partir de l'identifiant de la chanson
//Ceci est également appelé lorsqu'une chanson est sélectionnée à partir de l'écran de navigation Wear ou Auto.
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
//Cette fois, lisez le fichier audio contenu dans le dossier Assets
//Jouer depuis Uri
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(), Util.getUserAgent(getApplicationContext(), "AppName"));
MediaSource mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse("file:///android_asset/" + MusicLibrary.getMusicFilename(mediaId)));
//Cette fois, l'index est facilement calculé à partir du mediaId.
for (MediaSessionCompat.QueueItem item : queueItems)
if (item.getDescription().getMediaId().equals(mediaId))
index = (int) item.getQueueId();
exoPlayer.prepare(mediaSource);
mSession.setActive(true);
onPlay();
//Définir les informations sur la chanson en cours de lecture, fournies par MediaSession
mSession.setMetadata(MusicLibrary.getMetadata(getApplicationContext(), mediaId));
}
//Lorsqu'on lui a demandé de jouer
@Override
public void onPlay() {
//Demander le focus audio
if (am.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//Si vous pouvez l'obtenir, commencez à jouer
mSession.setActive(true);
exoPlayer.setPlayWhenReady(true);
}
}
//Lorsqu'une pause est demandée
@Override
public void onPause() {
exoPlayer.setPlayWhenReady(false);
//Libérer le focus audio
am.abandonAudioFocus(afChangeListener);
}
//Lorsqu'on lui a demandé d'arrêter
@Override
public void onStop() {
onPause();
mSession.setActive(false);
//Libérer le focus audio
am.abandonAudioFocus(afChangeListener);
}
//Lorsqu'une recherche est demandée
@Override
public void onSeekTo(long pos) {
exoPlayer.seekTo(pos);
}
//Quand la chanson suivante est demandée
@Override
public void onSkipToNext() {
index++;
if (index >= MusicLibrary.getMediaItems().size())//Après avoir joué jusqu'à la fin de la bibliothèque
index = 0;//Revenir au début
onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null);
}
//Lorsque la chanson précédente est demandée
@Override
public void onSkipToPrevious() {
index--;
if (index < 0)//Lorsque l'indice passe en dessous de 0
index = queueItems.size() - 1;//Passer à la dernière chanson
onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null);
}
//Également appelé lorsqu'un élément de la file d'attente est sélectionné avec Wear ou Auto
@Override
public void onSkipToQueueItem(long i) {
onPlayFromMediaId(queueItems.get((int)i).getDescription().getMediaId(), null);
}
//Appelé lorsque l'intention du bouton multimédia vole
//Aucun remplacement requis (il suffit de cracher des journaux cette fois)
//Les opérations qui peuvent être effectuées changent en fonction de l'indicateur Action de playbackState de MediaSession.
@Override
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
KeyEvent key = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
Log.d(TAG, String.valueOf(key.getKeyCode()));
return super.onMediaButtonEvent(mediaButtonEvent);
}
};
Il y a deux points à noter ici.
Tout d'abord, mSession.setMetadata ()
qui est le processus final de ʻonPlayFromMediaId () Si vous définissez les métadonnées pour la session multimédia, les informations sur la chanson seront transmises aux clients connectés. En prenant MainActivity comme exemple, le rappel MediaController ʻonMetadataChanged ()
est appelé.
Puis ʻonMediaButtonEvent () ` Il s'agit de l'endroit qui sera appelé lorsque l'intention du bouton multimédia vole. Il comprend également des boutons physiques sur les écouteurs filaires et des commandes d'appareils connectés Bluetooth.
Contrairement aux autres méthodes, il existe déjà une implémentation ici, il n'est donc fondamentalement pas nécessaire de la remplacer. Par exemple, un traitement tel que le mappage d'une simple pression de bouton physique sur un casque sur onPlay () et onPause () et une double pression sur onSkipNext () a déjà été implémenté.
Cependant, si vous souhaitez ajouter votre propre opération en appuyant sur le bouton physique, vous la gérerez ici.
MusicService.java
//Rappel du joueur
private Player.EventListener eventListener = new Player.DefaultEventListener() {
//Appelé lorsque le statut du joueur change
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
UpdatePlaybackState();
}
};
//Définir l'état actuel du lecteur fourni par MediaSession
//Des informations sur la position de lecture sont également incluses ici, donc mettez-les régulièrement à jour.
private void UpdatePlaybackState() {
int state = PlaybackStateCompat.STATE_NONE;
//Définissez le statut de session multimédia approprié à partir de l'état du lecteur
switch (exoPlayer.getPlaybackState()) {
case Player.STATE_IDLE:
state = PlaybackStateCompat.STATE_NONE;
break;
case Player.STATE_BUFFERING:
state = PlaybackStateCompat.STATE_BUFFERING;
break;
case Player.STATE_READY:
if (exoPlayer.getPlayWhenReady())
state = PlaybackStateCompat.STATE_PLAYING;
else
state = PlaybackStateCompat.STATE_PAUSED;
break;
case Player.STATE_ENDED:
state = PlaybackStateCompat.STATE_STOPPED;
break;
}
//Réglez les informations du lecteur, la position de lecture actuelle, etc.
//Définissez également les opérations pouvant être effectuées avec MeidaButtonIntent.
mSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_STOP)
.setState(state, exoPlayer.getCurrentPosition(), exoPlayer.getPlaybackParameters().speed)
.build());
}
Le premier rappel provient d'ExoPlayer. Comme son nom l'indique, il est appelé lorsque le statut du joueur change.
ʻUpdatePlaybackState () `définit l'état du lecteur fourni par MediaSession. Le contenu à livrer est l'état tel que Lecture et Pause, la position de lecture, la vitesse de lecture et les opérations actuellement acceptées. Le statut PlaybackStateCompat.STATE_XXXX, qui indique l'état du lecteur, et le statut d'ExoPlayer sont différents et doivent être convertis en ceux correspondants. En passant, le fait que la position de lecture soit incluse signifie que la position de lecture doit toujours être fournie. Par conséquent, j'essaye d'appeler ici toutes les 0,5 secondes dans la partie d'initialisation (onCreate).
MusicService.java
//Créer une notification, rendre le service au premier plan
private void CreateNotification() {
MediaControllerCompat controller = mSession.getController();
MediaMetadataCompat mediaMetadata = controller.getMetadata();
if (mediaMetadata == null && !mSession.isActive()) return;
MediaDescriptionCompat description = mediaMetadata.getDescription();
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
builder
//Définir les informations pour la chanson actuelle
.setContentTitle(description.getTitle())
.setContentText(description.getSubtitle())
.setSubText(description.getDescription())
.setLargeIcon(description.getIconBitmap())
//Définir l'intention en cliquant sur la notification
.setContentIntent(createContentIntent())
//Définir l'intention lorsque les notifications sont supprimées
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_STOP))
//Rendre la plage de notification publique afin qu'elle s'affiche sur l'écran de verrouillage
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.exo_controls_play)
//Définir la couleur utilisée pour la zone de notification
//Les styles changent en fonction de la version d'Android et les couleurs ne sont souvent pas appliquées
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
//Utiliser le style de média
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mSession.getSessionToken())
//Définit l'index du contrôle qui s'affiche lorsque la notification est repliée petit
.setShowActionsInCompactView(1));
// Android4.Avant 4, vous ne pouvez pas faire glisser pour ignorer les notifications
//Traitez en affichant le bouton d'annulation
//Cette fois, le SDK minimum est de 21, donc ce n'est pas nécessaire
//.setShowCancelButton(true)
//.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this,
// PlaybackStateCompat.ACTION_STOP)));
//Paramètres de contrôle des notifications
builder.addAction(new NotificationCompat.Action(
R.drawable.exo_controls_previous, "prev",
MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)));
//Définir les boutons de lecture et de pause dans l'état du lecteur
if (controller.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING) {
builder.addAction(new NotificationCompat.Action(
R.drawable.exo_controls_pause, "pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_PAUSE)));
} else {
builder.addAction(new NotificationCompat.Action(
R.drawable.exo_controls_play, "play",
MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_PLAY)));
}
builder.addAction(new NotificationCompat.Action(
R.drawable.exo_controls_next, "next",
MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_SKIP_TO_NEXT)));
startForeground(1, builder.build());
//Autoriser les balayages pour désactiver les notifications lorsque vous ne jouez pas
if (controller.getPlaybackState().getState() != PlaybackStateCompat.STATE_PLAYING)
stopForeground(false);
}
Pour créer une notification avec contrôle de lecture, vous devez spécifier MediaStyle avec setStyle et définir l'intention du bouton multimédia avec addAction pour l'augmenter.
MusicService.java
//Rappel de focus audio
AudioManager.OnAudioFocusChangeListener afChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
//Si vous perdez complètement votre concentration
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
//Arrêtez
mSession.getController().getTransportControls().pause();
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {//Mise au point temporaire perdue
//Arrêtez
mSession.getController().getTransportControls().pause();
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {//Mise au point perdue en raison du son de notification (doit être désactivée et continuer à jouer)
//Normalement, vous devez baisser le volume temporairement, mais ne rien faire
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {//Quand la concentration est retrouvée
//Régénération
mSession.getController().getTransportControls().play();
}
}
};
Nous avons implémenté un mécanisme pour arrêter la lecture si une autre application commence à jouer et que la mise au point audio de cette application est perdue.
J'ai trouvé que c'était très bien fait car il n'y avait qu'une API implémentée à partir d'Android 5.0.
Étant donné que de nombreux lecteurs de musique Android sont déjà sortis, je pense qu'il y a peu de possibilités de créer un lecteur de musique particulièrement pur par vous-même, mais cela peut être utile si vous avez la possibilité de jouer de la musique en arrière-plan, comme la radio Internet. Hmm.
J'ai essayé d'expliquer l'API Android Media dans un article. Cela fait longtemps et je suis moi-même surpris. Dans un tel cas, est-il plus facile de lire en le divisant?
Quoi qu'il en soit, merci d'avoir regardé jusqu'au bout.
・ Exemple Android MediaBrowserService Cela a été simplifié et cet exemple a été créé. ・ Test du contrôleur multimédia Un échantillon qui implémente le navigateur multimédia et le contrôleur multimédia et peut contrôler d'autres applications musicales. ・ Exemple de lecteur de musique Android universel Une implémentation de lecteur de musique assez soignée. Vous utilisez correctement la fonction de navigation du service de navigateur multimédia.
Recommended Posts