Erstellen Sie eine Vorlage für einen sehr gängigen Musikplayer.
Darüber hinaus werden wir eine Methode anwenden, die Wear und Auto unterstützt, ohne eine dedizierte Verarbeitung zu implementieren.
Es gibt keine japanischen Artikel über Android-Musikplayer. Ich habe einen Artikel gemacht, weil ich es mit verschiedenen Anstrengungen geschafft habe, ihn umzusetzen. Es wird länger dauern, aber bitte bleiben Sie in Kontakt, wenn Sie möchten.
Wenn Sie nur die Implementierung sehen möchten, fahren Sie mit der Implementierung fort.
Verwenden Sie beim Erstellen eines Musik-Players, der auf einem PC ausgeführt wird, verschiedene Instanzmethoden wie MediaPlayer. Ich denke, der Hauptweg, es zu machen, war es, es aufzurufen, indem man auf die Schaltfläche klickt, die in das Formular eingefügt wurde.
Mobile Plattformen wie Android und iOS waren in den letzten Jahren jedoch nicht auf Smartphones beschränkt. Es wurde in verschiedene Geräte wie Uhren, Auto-Audios und Fernseher integriert. Dann wurde es notwendig, eine Verbindung mit einem Smartphone herzustellen, den Player über eine Smartwatch oder ein Auto-Audio zu bedienen und Songinformationen anzuzeigen. Wenn ein Entwickler Code für verschiedene Geräte schreibt (für Android Wear, Auto, TV usw.), ist dies eine schwere Belastung. Daher gab es eine Bewegung, um eine solche Verarbeitung in der Android-Serie zu standardisieren, und sie wurde in Android 5.0 implementiert. Das ist "MediaSession".
Die obige Abbildung zeigt den Mechanismus für Musik unter Android. Diese ähneln Server-Client-Beziehungen.
Die "Mediensitzung" fungiert als Server. Es bietet Funktionen zum Bedienen von Songs, Player-Status, Song-Informationen usw. für verbundene Clients.
Der Client fordert eine Operation von der Mediensitzung unter Verwendung von "Media Controller", "Media Button Intent" usw. an. Zeigt den Player-Status und die zu benachrichtigenden Song-Informationen an.
Referenz: Understanding MediaSession (Part 1/4) Understanding MediaSession (Part 2/4)
Beide werden verwendet, um die "Mediensitzung" aufzufordern, einen Song zu bedienen (Abspielen, Stoppen, Überspringen usw.). Der Unterschied besteht darin, dass "Media Button Intent" nur Vorgänge anfordern kann, während "Media Controller" auch Player-Status und Song-Informationen abrufen kann.
Übrigens wird Media Session eine eindeutige ID namens "Session Token" zugewiesen. Wenn Sie die obige Serveranalogie verwenden, entspricht dies einer IP-Adresse. Mit anderen Worten, wenn Sie das Token kennen, können Sie eine Verbindung von außen herstellen.
Übergeben Sie Token beim Verbinden an den Konstruktor von Media Controller
.
Ich denke, es ist schwer vorstellbar, wie man Media Session aus der obigen Erklärung verwendet, also habe ich ein Diagramm erstellt.
Wenn Sie sich die Figur ansehen, sieht es so aus, als würden Sie etwas ziemlich nerviges tun. Es ist jedoch sehr sinnvoll, beim Erstellen eines Musik-Players auf der Android-Plattform die oben beschriebene Form anzunehmen.
Ich denke, dass der Musik-Player, den Sie normalerweise verwenden, weiterhin abgespielt wird, auch wenn Sie Aktivität aus den letzten Aufgaben entfernen. Selbst wenn Sie es nicht aus der Aufgabe entfernen, kann das Wechseln zu einer anderen App die Aktivität zerstören, wenn Ihnen der Speicher ausgeht. Mit anderen Worten, wenn Sie einen Teil der Verarbeitung im Zusammenhang mit der Wiedergabe der Aktivität überlassen, können Sie die Wiedergabe nicht fortsetzen, wenn die Aktivität zerstört wird.
Daher besteht die wichtige Idee beim Erstellen eines Musik-Players, der unter Android ausgeführt wird, darin, alle Funktionen zum Abspielen von Songs auf der Serviceseite zu implementieren und auf der Aktivitätsseite Vorgänge wie Wiedergabe und Pause gemäß dem Vorgang "Mediensitzung" des Benutzers auszuführen. Es ist **, um die Aufteilung der Rollen zu verdeutlichen **, z. B. einfach die Informationen des gespielten Songs anzuzeigen, die von Media Session
auf der Benutzeroberfläche gesendet wurden.
Sie müssen keine Angst haben, dass die Aktivität zerstört wird, indem Sie dem Benutzer einfach die Songinformationen anzeigen und den Player steuern. Wenn die Aktivität neu generiert wird, müssen Sie nur die Verbindung zur "Mediensitzung" wiederherstellen.
Referenz: Übersicht über Medien-Apps
Im vorherigen Beispiel wurde die Mediensitzung in einem normalen Dienst implementiert. In der Praxis wird jedoch empfohlen, es mit "Media Browser Service" zu implementieren, einer Erweiterung des Dienstes.
Die zuvor erwähnte "Mediensitzung" fungiert als Server, der Song-Operationen und -Informationen bereitstellt, und der "Medienbrowser-Dienst" fungiert als Musikbibliothek. Wenn Sie es gemäß den Spezifikationen des Medienbrowsers implementieren, können Sie Funktionen wie die automatische Auswahl von Songs aus Wear und Auto verwenden. Es kapselt auch Vorgänge wie das Binden und Abrufen eines Sitzungstokens.
Es scheint, dass es keinen großen Unterschied zum vorherigen Beispiel gibt, da die Namen der Schritte ① und ② leicht zu verstehen sind. Tatsächlich wird hier eine Funktion zum Bereitstellen einer Liste von Songs hinzugefügt.
Referenz: Building a Media Browser Service Building a Media Browser Client
Als nächstes geht es um das Durchsuchen der Bibliothek. Kurz gesagt, es ist eine Funktion, die eine Liste von Songs anzeigt, die dem Benutzer abgespielt werden können.
Ich habe Ihnen das Lied gesagt, das Sie in ④ des vorherigen Beispiels spielen möchten, aber wenn Sie überhaupt keine Liste von Liedern haben, können Sie keine Anfrage stellen. Wie oben erwähnt, verfügt der Medienbrowser über einen Mechanismus zum Bereitstellen einer Musikbibliothek.
Die obige Abbildung zeigt den Vorgang des Herstellens einer Verbindung zum Media Browser Service
und des Abrufs einer Liste von Songs.
Rufen Sie "subscribe (MediaId)" auf, um ein Medienelement vom Medienbrowser beim "Medienbrowserdienst" anzufordern.
Anschließend wird die Liste der Medienelemente zurückgegeben. Die Schritte ③ und ④ werden jedes Mal ausgeführt, wenn dies erforderlich ist.
Im Media Browser Service
werden alle Elemente von Songs, Alben und Genres durch die Zeichenkette MediaId dargestellt.
In der Klasse, die die MediaId und die zugehörigen Daten enthält, befindet sich MediaBrowser.MediaItem.
Der von MediaItem gehaltene Inhalt verfügt über ein Flag, das angibt, ob es ein untergeordnetes Element, ein Element, das die Rolle eines sogenannten Ordners hat, oder ein abspielbares Musikelement, das keine untergeordneten Elemente enthält. Siehe die Abbildung unten.
Orange ist das Objekt mit der Ordnerflagge und Blau ist das Objekt mit der spielbaren Flagge. Die geschriebene Zeichenfolge gibt MediaId an.
Verstehen Sie mich hier nicht falsch, MediaItem selbst mit dem Ordner-Flag enthält keine untergeordneten Elemente als Objekte. In der obigen Abbildung wurde es wie eine Baumstruktur geschrieben, es gibt jedoch keine Eltern-Kind-Beziehung in Bezug auf Objekte.
Ich denke, es ist schwer zu verstehen, also geben wir ein konkretes Beispiel.
Wenn Sie "Media Browser subscribe (mediaId)" aufrufen, wird "onLoadChildren ()" des verbundenen "Media Browser Service" aufgerufen und der von ihm gesendete Inhalt zurückgegeben.
Rufen Sie zunächst subscribe (" root ")
auf.
Dann wird "{" Genre "," Album "}" zurückgegeben.
Angenommen, ein Benutzer möchte nach Genre geordnet sein.
Rufen Sie dann subscribe (" Genre ")
auf. Dann wird "{" Genre: Rock, "Genre: Jazz"} "zurückgegeben.
Gehen Sie dann zu "subscribe (" Genre: Rock ")" und Sie erhalten "{" music1 "," music2 "}".
Wenn der Benutzer music1 auswählt, fordert der Media Controller die Wiedergabe des Songs mit MediaId "music1" an.
Ich habe es so geschrieben, als ob music1 und music2 automatisch zurückgegeben würden, wenn ich "subscribe (" Genre: Rock ")" anrief, aber ich musste den Mechanismus selbst schreiben. Wenn beispielsweise "Genre:" am Anfang von MediaId hinzugefügt wird, wird dies als ID angesehen, die alle Titel eines bestimmten Genres auflistet, und der Titel wird zurückgegeben. Mit anderen Worten, es ist notwendig zu verstehen, was nur von der Medien-ID benötigt wird, und das Lied oder die Unterkategorie zurückzugeben.
In diesem Beispiel habe ich jedoch einfach das Gefühl, dass zwei Songs an der Wurzel hängen.
Die im Voraus vorbereiteten Klassen sind wie folgt.
Name | Erläuterung |
---|---|
MediaDescription | Eine Klasse, die mindestens Metadaten enthält. |
MediaMetadata | InformationenzuSongs,dievonMediaSessiongeliefertwerden,habendiesesFormat.ZusätzlichzudenMindestmetadatenkönnenSiebenutzerdefinierteDatensoeinstellen,alswäreeseinassoziativesArray.getDescription()Sie können auch eine Medienbeschreibung mit generieren. |
MediaBrowser.MediaItem | Das Format zum Abrufen von Informationen aus dem MediaBrowserService. Neben Songs können auch Elemente mit untergeordneten Elementen wie Genres und Alben ausgedrückt werden. Es enthält eine Medienbeschreibung. |
MediaSession.QueueItem | Es enthält eine Medienbeschreibung und wird für Informationen zu Titeln in der Warteschlange verwendet. Daher werden Indexinformationen angehängt. |
Sie können sehen, dass wir "Media Browser" und "Media Controller" verwenden, um eine Verbindung zu "Media Browser Service" und "Media Session" herzustellen. Tatsächlich haben Wear und Auto auch diese beiden. In den bisherigen Beispielen wurde es über Aktivität verbunden, aber Verschleiß und Auto sind nur auf dieselbe Weise verbunden, sodass kein dedizierter Prozess geschrieben werden muss.
Informationen zu Songs, die von Media Session geliefert wurden, werden angezeigt. Wenn Sie in der Mediensitzung ein Warteschlangenelement festlegen, wird unten eine Liste der Titel angezeigt.
Auf der Wear-Seite befindet sich auch MediaBrowser.subscribe, mit dem eine Liste der Songs abgerufen und angezeigt wird. (Diesmal nur Songs, aber je nach Implementierung von onLoadChildren können auch Menüs nach Album oder Genre angezeigt werden.)
Audio Focus
Angesichts der Präsenz von Mediensitzungen und Browsern ist dies keine große Sache, aber es ist auch ein wichtiges Merkmal.
Audio Focus
ist die Audioversion von Focus in der Benutzeroberfläche.
Stellen Sie sicher, dass nur eine App über "Audio Focus" verfügt. Und wenn Sie nur Apps mit "Audio Focus" erlauben, Songs abzuspielen, können Sie verhindern, dass mehrere Apps gleichzeitig Musik abspielen.
Das Android-System benachrichtigt Sie jedoch nur, wenn Focus trifft oder verfehlt, sodass Sie Ihre eigene Verarbeitung schreiben, z. B. eine Pause, wenn der Fokus verloren geht (z. B. wenn eine andere App abgespielt wird). wird gebraucht.
Dieses Mal verwenden wir die Musikdaten, die im Ordner "Assets" gespeichert sind. Ich hatte das Gefühl, dass es kompliziert sein würde, wenn es um die Speichergeschichte ging, und machte deshalb Kompromisse, indem ich die Verwendung von Media Session übte. Die Song-Metadaten sind ebenfalls fest codiert. Bitte beachten Sie.
** Der gesamte Code ist hier **
Da der Benachrichtigungskanal in Android 8.0 hinzugefügt wurde und je nach Version verzweigt werden muss, ist diesmal das Ziel auf 25 und das Minimum auf 21 festgelegt. (Wir akzeptieren Tsukkomi nicht, dass es nicht imadoki ist ^^;)
MediaPlayer ist in Ordnung, da nur MP3 abgespielt wird, aber ich verwende ExoPlayer, den ich normalerweise verwende. ExoPlayer ist ein von Google entwickelter Media Player, der den Standard-MediaPlayer ersetzen soll, der je nach Version und Modell unterschiedliche Implementierungen aufweisen kann. Wenn es aktualisiert wird, werden die Formate, die abgespielt werden können, erhöht und es werden auch Funktionen wie Live-Streaming verfügbar sein. Verwendung von ExoPlayer Verwendung von ExoPlayer Ich denke das wird hilfreich sein.
Es wird auch die Support-Bibliothek verwendet. Ich empfehle auch den Google-Beamten, da er die Unterschiede zwischen den Versionen aufnimmt. Verwenden Sie daher am Ende die mit Compat, z. B. "MediaSessionCompat". Abhängige Bibliotheken sind wie folgt
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 Aktivität angezeigt · Musiksammlung Ich habe es aus Googles offiziellem Beispiel ausgeliehen. Die Songinformationen sind fest codiert. ・ MusicService Implementierung des Meida Browser Service.
Unten sehen Sie ein Diagramm der Beziehung zwischen Rückrufen und Methoden.
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>
Dies ist ein Auszug aus dem Anwendungsteil. Registrieren Sie "MusicService" und stellen Sie den Receiver so ein, dass er "MediaButtonIntent" empfängt.
MainActivity UI ** Klicken Sie hier für die XML-Datei der Benutzeroberfläche (https://github.com/SIY1121/MediaSessionSample/blob/master/app/src/main/res/layout/activity_main.xml) **
MainActivity.java
MediaBrowserCompat mBrowser;
MediaControllerCompat mController;
//UI-bezogen wird weggelassen
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//UI-Setup (weggelassen)
//Halten Sie den Dienst am Laufen
//Dies ist nicht erforderlich, wenn der Dienst gleichzeitig mit der Zerstörung der Aktivität gestoppt werden kann.
startService(new Intent(this, MusicService.class));
//MediaBrowser initialisieren
mBrowser = new MediaBrowserCompat(this, new ComponentName(this, MusicService.class), connectionCallback, null);
//Verbindung(Bindungsservice)
mBrowser.connect();
}
//Rückruf beim Verbinden aufgerufen
private MediaBrowserCompat.ConnectionCallback connectionCallback = new MediaBrowserCompat.ConnectionCallback() {
@Override
public void onConnected() {
try {
//Da Sitzungstoken kann erhalten werden, wenn die Verbindung hergestellt ist
//Erstellen Sie damit einen Media Controller
mController = new MediaControllerCompat(MainActivity.this, mBrowser.getSessionToken());
//Legen Sie einen Rückruf fest, wenn sich der Player-Status oder die vom Dienst gesendeten Song-Informationen ändern
mController.registerCallback(controllerCallback);
//Wenn es bereits abgespielt wird, rufen Sie den Rückruf selbst auf und aktualisieren Sie die Benutzeroberfläche
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();
}
//Holen Sie sich eine Liste der abspielbaren Songs vom Dienst
mBrowser.subscribe(mBrowser.getRoot(), subscriptionCallback);
}
};
//Rückruf, der beim Abonnieren aufgerufen wird
private MediaBrowserCompat.SubscriptionCallback subscriptionCallback = new MediaBrowserCompat.SubscriptionCallback() {
@Override
public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaBrowserCompat.MediaItem> children) {
//Fordern Sie an, das erste Lied abzuspielen, falls es noch nicht abgespielt wird
if (mController.getPlaybackState() == null)
Play(children.get(0).getMediaId());
}
};
Starten Sie zuerst den Musikdienst.
Danach wird MediaBrowser initialisiert und verbunden. Wenn die Verbindung hergestellt ist, wird "onConnected ()" aufgerufen. Holen Sie sich also das Token mit "mBrowser.getSessionToken ()" und übergeben Sie es an den Konstruktor von "MediaController", um eine Verbindung zur Mediensitzung herzustellen.
Rufen Sie dann "mBrowser.subscribe (mBrowser.getRoot (), AbonnementCallback)" auf, um eine Liste der Songs anzufordern. Wenn die Serviceseite eine Liste sendet, wird der festgelegte Rückruf "onChildrenLoaded ()" aufgerufen, sodass wir diesmal das erste Element spielen.
MainActivity.java
private void Play(String id) {
//Lassen Sie TransportControl den Betrieb von MediaController an den Service anfordern
//Wenn playFromMediaId aufgerufen wird, wird onPlayFromMediaId im Rückruf von MediaSession auf der Serviceseite aufgerufen.
mController.getTransportControls().playFromMediaId(id, null);
}
Sie können das "TransportControl" verwenden, um Operationen von der MediaSession mit "mController.getTransportControls ()" anzufordern.
MainActivity.java
//MediaController-Rückruf
private MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() {
//Wird aufgerufen, wenn die Informationen des abgespielten Songs geändert werden
@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));
}
//Wird aufgerufen, wenn sich der Status des Spielers ändert
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
//Ändern Sie das Tastenverhalten und das Symbol je nach Spielerstatus
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());
}
};
Wenn sich der Status des Players oder des abzuspielenden Songs ändert, werden Informationen von MediaSession gesendet und der Rückruf von MediaController aufgerufen. Reflektieren Sie die Änderung in der Benutzeroberfläche.
MusicService Erstellen Sie einen Dienst, der von MediaBrowserService erbt.
MusicService.java
//Wird beim Herstellen einer Verbindung zu einem Client aufgerufen
//Entscheiden Sie, ob Sie eine Verbindung über den Paketnamen usw. herstellen möchten.
//Verbindungsberechtigung, wenn eine beliebige Zeichenfolge zurückgegeben wird
//Verbindung mit null verweigern
//Lassen Sie diesmal alle Verbindungen zu
@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);
}
//Wird aufgerufen, wenn die clientseitigen Anrufe abonnieren
//Gibt den Inhalt der Musikbibliothek zurück
//Wird auch für die Liste der Songs verwendet, die in Wear und Auto angezeigt werden
//Standardmäßig wird die von onGetRoot zurückgegebene Zeichenfolge an parentMediaId übergeben
//Die ID wird auch übergeben, wenn Sie ein MediaItem auswählen, das auf dem Browserbildschirm ein untergeordnetes Element enthält.
@Override
public void onLoadChildren(
@NonNull final String parentMediaId,
@NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) {
if (parentMediaId.equals(ROOT_ID))
//Senden Sie eine Liste der Songs an den Client
result.sendResult(MusicLibrary.getMediaItems());
else//Diesmal ROOT_Ungültig außer ID
result.sendResult(new ArrayList<MediaBrowserCompat.MediaItem>());
}
"onGetRoot ()" wird beim Verbinden aufgerufen und "onLoadChildren ()" wird beim Abonnieren aufgerufen. Da "onLoadChildren ()" einige Zeit in Anspruch nehmen soll, wird das Ergebnis an das Ergebnisobjekt gesendet, anstatt es mit "return" zurückzugeben. Wenn Sie es sofort zurückgeben können, verwenden Sie result.sendResult
, und wenn Sie es später asynchron zurückgeben möchten, verwenden Sie result.detatch ()
.
MusicService.java
final String TAG = MusicService.class.getSimpleName();//Protokoll-Tag
final String ROOT_ID = "root";//ID onGetRoot, um zum Client zurückzukehren/Wird in onLoadChildren verwendet
Handler handler;//Handler zum regelmäßigen Wenden
MediaSessionCompat mSession;//Mediensitzung der Hauptrolle
AudioManager am;//Manager für den Umgang mit AudioFoucs
int index = 0;//Index wird gespielt
ExoPlayer exoPlayer;//Die Substanz des Musik-Players
List<MediaSessionCompat.QueueItem> queueItems = new ArrayList<>();//Liste für die Warteschlange
@Override
public void onCreate() {
super.onCreate();
//Holen Sie sich AudioManager
am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//MediaSession initialisieren
mSession = new MediaSessionCompat(getApplicationContext(), TAG);
//Stellen Sie die Funktionen dieser Mediensitzung ein
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | //Griffknöpfe wie Kopfhörer
MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS | //Unterstützt die Verwendung von Warteschlangenbefehlen
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); //Bietet Steuerelemente zum Abspielen, Stoppen, Überspringen usw.
//Stellen Sie den Rückruf entsprechend der Operation vom Client ein
mSession.setCallback(callback);
//Legen Sie das Sitzungstoken in MediaBrowserService fest
setSessionToken(mSession.getSessionToken());
//Wenn die Metadaten der Mediensitzung und der Player-Status aktualisiert werden
//Benachrichtigung erstellen/Aktualisieren
mSession.getController().registerCallback(new MediaControllerCompat.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
CreateNotification();
}
@Override
public void onMetadataChanged(MediaMetadataCompat metadata) {
CreateNotification();
}
});
//Element zur Warteschlange hinzufügen
int i = 0;
for (MediaBrowserCompat.MediaItem media : MusicLibrary.getMediaItems()) {
queueItems.add(new MediaSessionCompat.QueueItem(media.getDescription(), i));
i++;
}
mSession.setQueue(queueItems);//Die Warteschlange wird in Verschleiß und Auto angezeigt
//Initialisierung von exoPlayer
exoPlayer = ExoPlayerFactory.newSimpleInstance(getApplicationContext(), new DefaultTrackSelector());
//Stellen Sie den Listener für Spielerereignisse ein
exoPlayer.addListener(eventListener);
handler = new Handler();
//Aktualisieren Sie die Wiedergabeinformationen alle 500 ms
handler.postDelayed(new Runnable() {
@Override
public void run() {
//Update während der Wiedergabe
if (exoPlayer.getPlaybackState() == Player.STATE_READY && exoPlayer.getPlayWhenReady())
UpdatePlaybackState();
//Führe es nochmals aus
handler.postDelayed(this, 500);
}
}, 500);
}
Da die Menge groß ist, werde ich es kurz erklären.
Zunächst zu mSession.setFlags ()
, das nach der Initialisierung von MediaSession erfolgt.
Sie können die Mediensitzungsfunktion aktivieren, indem Sie ein Flag setzen.
Bitte beachten Sie, dass keine Funktion verwendet werden kann, es sei denn, sie ist umgekehrt eingestellt.
Dann setSessionToken (mSession.getSessionToken ())
Dadurch wird der von getSessionToken () auf dem Client (MediaBrowser) zurückgegebene Wert festgelegt.
Da diesmal ExoPlayer verwendet wird, werden wir ExoPlayer initialisieren.
Schließlich wird der Handler verwendet, um in regelmäßigen Abständen "UpdatePlaybackState ()" aufzurufen. Dieser Vorgang wird später erläutert.
Dies ist der Teil, der mit Transport Control gekoppelt ist und zum Anfordern von Vorgängen an MediaSession in MainActivity verwendet wurde.
MusicService.java
//Rückruf für Mediensitzung
private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {
//Spielen Sie von der Song-ID
//Dies wird auch aufgerufen, wenn ein Titel im Wear- oder Auto-Browsing-Bildschirm ausgewählt wird.
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
//Spielen Sie diesmal die im Ordner "Assets" enthaltene Audiodatei ab
//Spiel von 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)));
//Dieses Mal kann der Index leicht aus der mediaId berechnet werden.
for (MediaSessionCompat.QueueItem item : queueItems)
if (item.getDescription().getMediaId().equals(mediaId))
index = (int) item.getQueueId();
exoPlayer.prepare(mediaSource);
mSession.setActive(true);
onPlay();
//Legen Sie Informationen zu dem von MediaSession gelieferten Titel fest
mSession.setMetadata(MusicLibrary.getMetadata(getApplicationContext(), mediaId));
}
//Wenn Sie zum Spielen aufgefordert werden
@Override
public void onPlay() {
//Audio-Fokus anfordern
if (am.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
//Wenn Sie es bekommen können, fangen Sie an zu spielen
mSession.setActive(true);
exoPlayer.setPlayWhenReady(true);
}
}
//Wenn eine Pause angefordert wird
@Override
public void onPause() {
exoPlayer.setPlayWhenReady(false);
//Lassen Sie den Audiofokus los
am.abandonAudioFocus(afChangeListener);
}
//Wenn Sie aufgefordert werden, aufzuhören
@Override
public void onStop() {
onPause();
mSession.setActive(false);
//Lassen Sie den Audiofokus los
am.abandonAudioFocus(afChangeListener);
}
//Wenn eine Suche angefordert wird
@Override
public void onSeekTo(long pos) {
exoPlayer.seekTo(pos);
}
//Wenn das nächste Lied angefordert wird
@Override
public void onSkipToNext() {
index++;
if (index >= MusicLibrary.getMediaItems().size())//Nach dem Spielen bis zum Ende der Bibliothek
index = 0;//Kehren Sie zum Anfang zurück
onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null);
}
//Wenn das vorherige Lied angefordert wird
@Override
public void onSkipToPrevious() {
index--;
if (index < 0)//Wenn der Index unter 0 fällt
index = queueItems.size() - 1;//Gehe zum letzten Lied
onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null);
}
//Wird auch aufgerufen, wenn ein Element in der Warteschlange mit Verschleiß oder Auto ausgewählt wird
@Override
public void onSkipToQueueItem(long i) {
onPlayFromMediaId(queueItems.get((int)i).getDescription().getMediaId(), null);
}
//Wird aufgerufen, wenn der Media Button Intent fliegt
//Keine Überschreibung erforderlich (diesmal nur Protokolle ausspucken)
//Die Operationen, die ausgeführt werden können, ändern sich entsprechend dem Aktionsflag von playbackState of 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);
}
};
Hier sind zwei Punkte zu beachten.
Zunächst der letzte Prozess von "onPlayFromMediaId ()", "mSession.setMetadata ()" Wenn Sie Metadaten für Mediensitzungen festlegen, werden Songinformationen an verbundene Clients gesendet. Am Beispiel von MainActivity wird der MediaController-Rückruf "onMetadataChanged ()" aufgerufen.
Dann onMediaButtonEvent ()
Dies ist der Ort, der aufgerufen wird, wenn der Media Button Intent fliegt.
Es enthält auch physische Tasten an kabelgebundenen Kopfhörern und Bedienelemente von Bluetooth-verbundenen Geräten.
Im Gegensatz zu anderen Methoden gibt es hier bereits eine Implementierung, sodass diese grundsätzlich nicht überschrieben werden muss. Beispielsweise wurde die Verarbeitung wie das Zuordnen eines einzelnen physischen Tastendrucks auf einem Kopfhörer zu onPlay () und onPause () und eines doppelten Drucks zu onSkipNext () bereits implementiert.
Wenn Sie jedoch Ihre eigene Operation hinzufügen möchten, indem Sie die physische Taste drücken, werden Sie hier damit umgehen.
MusicService.java
//Rückruf des Spielers
private Player.EventListener eventListener = new Player.DefaultEventListener() {
//Wird aufgerufen, wenn sich der Status des Spielers ändert
@Override
public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
UpdatePlaybackState();
}
};
//Stellen Sie den aktuellen Player-Status ein, der von MediaSession geliefert wird
//Informationen zur Wiedergabeposition finden Sie auch hier. Aktualisieren Sie sie daher regelmäßig.
private void UpdatePlaybackState() {
int state = PlaybackStateCompat.STATE_NONE;
//Stellen Sie den entsprechenden Mediensitzungsstatus aus dem Status des Players ein
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;
}
//Stellen Sie die Spielerinformationen, die aktuelle Wiedergabeposition usw. ein.
//Legen Sie außerdem die Vorgänge fest, die mit MeidaButtonIntent ausgeführt werden können.
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());
}
Der erste Rückruf kommt von ExoPlayer. Wie der Name schon sagt, wird es aufgerufen, wenn sich der Status des Players ändert.
UpdatePlaybackState ()
legt den Status des von MediaSession gelieferten Players fest.
Der zu liefernde Inhalt ist der Status wie Wiedergabe und Pause, Wiedergabeposition, Wiedergabegeschwindigkeit und aktuell akzeptierte Vorgänge.
Der Status PlaybackStateCompat.STATE_XXXX, der den Status des Players angibt, und der Status von ExoPlayer sind unterschiedlich und müssen in die entsprechenden konvertiert werden.
Die Tatsache, dass die Wiedergabeposition enthalten ist, bedeutet übrigens, dass die Wiedergabeposition immer geliefert werden muss.
Daher versuche ich hier alle 0,5 Sekunden im Initialisierungsteil (onCreate) aufzurufen.
MusicService.java
//Benachrichtigung erstellen, Service in den Vordergrund stellen
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
//Legen Sie Informationen für das aktuelle Lied fest
.setContentTitle(description.getTitle())
.setContentText(description.getSubtitle())
.setSubText(description.getDescription())
.setLargeIcon(description.getIconBitmap())
//Legen Sie die Absicht fest, wenn Sie auf Benachrichtigung klicken
.setContentIntent(createContentIntent())
//Legen Sie die Absicht fest, wenn Benachrichtigungen entfernt werden
.setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_STOP))
//Machen Sie den Benachrichtigungsbereich öffentlich, damit er auf dem Sperrbildschirm angezeigt wird
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(R.drawable.exo_controls_play)
//Stellen Sie die Farbe für den Benachrichtigungsbereich ein
//Die Stile ändern sich je nach Android-Version, und Farben werden häufig nicht angewendet
.setColor(ContextCompat.getColor(this, R.color.colorAccent))
//Verwenden Sie den Medienstil
.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mSession.getSessionToken())
//Legen Sie den Index des Steuerelements fest, der angezeigt wird, wenn die Benachrichtigung klein gefaltet wird
.setShowActionsInCompactView(1));
// Android4.Vor 4 können Sie nicht wischen, um Benachrichtigungen zu schließen
//Behandeln Sie dies, indem Sie die Schaltfläche Abbrechen anzeigen
//Dieses Mal ist das min SDK 21, es ist also nicht notwendig
//.setShowCancelButton(true)
//.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this,
// PlaybackStateCompat.ACTION_STOP)));
//Einstellungen für die Benachrichtigungssteuerung
builder.addAction(new NotificationCompat.Action(
R.drawable.exo_controls_previous, "prev",
MediaButtonReceiver.buildMediaButtonPendingIntent(this,
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)));
//Stellen Sie die Wiedergabe- und Pause-Tasten im Player-Status ein
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());
//Ermöglichen Sie Swipes, Benachrichtigungen zu deaktivieren, wenn Sie nicht spielen
if (controller.getPlaybackState().getState() != PlaybackStateCompat.STATE_PLAYING)
stopForeground(false);
}
Um eine Benachrichtigung mit Wiedergabesteuerung zu erstellen, müssen Sie MediaStyle mit setStyle angeben und Media Button Intent mit addAction festlegen, um sie auszulösen.
MusicService.java
//Rückruf des Audiofokus
AudioManager.OnAudioFocusChangeListener afChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
//Wenn Sie den Fokus vollständig verlieren
if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
//halt
mSession.getController().getTransportControls().pause();
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {//Temporärer Fokus verloren
//halt
mSession.getController().getTransportControls().pause();
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {//Fokus aufgrund von Benachrichtigungston verloren (sollte leiser gestellt werden und weiter spielen)
//Normalerweise sollten Sie die Lautstärke vorübergehend verringern, aber nichts tun
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {//Wenn der Fokus wieder hergestellt ist
//Wiedergabe
mSession.getController().getTransportControls().play();
}
}
};
Wir haben einen Mechanismus implementiert, um die Wiedergabe zu stoppen, wenn eine andere App abgespielt wird und der Audiofokus dieser App verloren geht.
Ich fand es sehr gut gemacht, weil nur eine API von Android 5.0 implementiert war.
Da bereits viele Android-Musikplayer veröffentlicht wurden, gibt es meines Erachtens nur wenige Möglichkeiten, einen besonders reinen Musikplayer selbst zu erstellen. Dies kann jedoch nützlich sein, wenn Sie die Möglichkeit haben, Musik im Hintergrund abzuspielen, z. B. im Internetradio. Hmm.
Ich habe versucht, die Android Media API in einem Artikel zu erklären. Es ist lange her und ich bin selbst überrascht. Ist es in einem solchen Fall einfacher zu lesen, indem man es teilt?
Wie auch immer, danke, dass du bis zum Ende zugesehen hast.
・ Android MediaBrowserService-Beispiel Dies wurde vereinfacht und dieses Beispiel wurde erstellt. ・ Media Controller Test Ein Beispiel, das Media Browser und Media Controller implementiert und andere Musik-Apps steuern kann. ・ Beispiel für einen Universal Android Music Player Eine ziemlich nette Implementierung des Musik-Players. Sie verwenden die Browsing-Funktion des Media Browser Service ordnungsgemäß.
Recommended Posts