[JAVA] Wie man einen imposanten Android-Musikplayer macht

Ich werde so etwas machen

sc1.png Erstellen Sie eine Vorlage für einen sehr gängigen Musikplayer.   cap2.gif

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.

Wie ein Musik-Player unter Android aussehen sollte

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.

fig1.png

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".

Position und Rolle der Mediensitzung

fig2.png

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)

Media Controller und Media Button Intent

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.

Beispiel für die Implementierung einer Mediensitzung

Ich denke, es ist schwer vorstellbar, wie man Media Session aus der obigen Erklärung verwendet, also habe ich ein Diagramm erstellt. fig3-3.png

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

Mediensitzung und Medienbrowser

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.

fig4.png 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

Durchsuchen der Bibliothek gemäß dem Designkonzept von Media Browser

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.

fig6.png

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.

Media ID-Konzept

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.

fig5.png

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.

Liste der Klassen, in denen Songinformationen gespeichert sind

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.

Warum Sie mit Wear und Auto arbeiten können

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.

Kooperationsbeispiel

cap3.gif 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.

cap2.gif 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.

Es ist Zeit zu implementieren

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 **

Zielversion

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 ^^;)

Bibliothek verwendet

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'
}

Konstruktion

image.png

・ 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. fig9.png

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 fig7.png   ** 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) **

Initialisieren und verbinden

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.

Kommunizieren Sie mit Media Session über Media Controller

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.

Verarbeitung, wenn Informationen von MediaSession geliefert werden

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.

Verarbeitung bei Verbindung von einem Client

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

Initialisieren

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.

Bearbeiten Sie Anforderungen für Mediensitzungen von Clients

Dies ist der Teil, der mit Transport Control gekoppelt ist und zum Anfordern von Vorgängen an MediaSession in MainActivity verwendet wurde. fig8.png

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.

Benachrichtigen Sie den Client über den Spielerstatus

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.

Benachrichtigung erstellen

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.

Audiofokus handhaben

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.

Impressionen

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.

Offizielles Google-Beispiel

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

Wie man einen imposanten Android-Musikplayer macht
[Android] So erstellen Sie ein Dialogfragment
So erstellen Sie eine App mit Tensorflow mit Android Studio
Wie man ein schattiertes Glas macht
Wie erstelle ich Unity Native Plugin (Android-Version)
Wie man einen Oleore-Generator mit Swagger Codegen herstellt
Java - So erstellen Sie JTable
Erstelle eine Android App. (Tag 5)
Umgang mit Instanzen
[Schienen] Wie man Samen macht
[Version 2020] So senden Sie eine E-Mail mit Android Studio Javamail
Was ist ein unveränderliches Objekt? [Erklärung, wie man macht]
So erstellen Sie einen Java-Container
So erstellen Sie einen JDBC-Treiber
Erstelle eine Android App. (Erster Tag)
So fügen Sie eine externe Bibliothek ein
So erstellen Sie ein Jenkins-Plug-In
Wie erstelle ich ein Maven-Projekt?
So erstellen Sie ein Java-Array
Ich habe versucht, eine Android-Anwendung mit MVC zu erstellen (Java)
So beschneiden Sie ein Bild in libGDX
Erstellen Sie mit Android Studio eine ausführbare JAR-Datei
So erstellen Sie eine Java-Kalenderzusammenfassung
So verwischen Sie das Bild (super einfach)
Ich möchte eine ios.android App machen
[Android] Wie man mit dunklen Themen umgeht
So erkennen Sie Mikrofonkonflikte unter Android
Wie erstelle ich einen Discord Bot (Java)
So definieren Sie eine Bean der inneren Klasse
So erstellen Sie eine App mit einem Plug-In-Mechanismus [C # und Java]
Verwendung von ExpandableListView in Android Studio
So erstellen Sie eine Beurteilungsmethode, um nach einem beliebigen Zeichen im Array zu suchen
Ich mache eine Android-App und sie steckt voller Fehler und wie man sie löst
[Android] So konvertieren Sie eine Zeichenfolge in resourceId
Wie schreibe ich eine if-Anweisung, um die Lesbarkeit von Java zu verbessern?
[Android] So erkennen Sie Lautstärkeänderungen (= Drücken der Lautstärketaste)
So verwenden Sie ein Array für HashMap-Schlüssel
So verkleinern Sie das Spring Boot Docker-Image
Wie man Stimme oder Musik mit Javascript spielt
Erstellen Sie eine Software, die den Android-Bildschirm auf einen PC 1 spiegelt
So machen Sie doppelte Prüflogik lesbarer
So erstellen Sie eine leichte JRE für den Vertrieb
Rails6.0 ~ So erstellen Sie eine umweltfreundliche Entwicklungsumgebung
So lösen Sie Ausdrucksprobleme in Java
Wie schreibe ich React Native Bridge ~ Android-Version ~
[Rails] So erstellen Sie eine Umgebung mit Docker
So erstellen Sie ein Oleore-Zertifikat (SSL-Zertifikat, Selbstzertifikat)
[Java] Wie man mehrere for-Schleifen einzeln macht
So installieren Sie Ruby auf einer EC2-Instanz von AWS
[Android Studio] So ändern Sie TextView in eine beliebige Schriftart [Java]
[Swift] So spielen Sie Songs aus der Musikbibliothek ab