Статьи

Как разработать приложение для Android Music Streaming

Этот пост был обновлен в ноябре 2016 года, чтобы отразить изменения в Модифицированной библиотеке .

Мультимедийная платформа Android обеспечивает расширенную поддержку воспроизведения различных типов мультимедиа, что позволяет интегрировать аудио, видео и изображения в приложения. Вы можете воспроизводить аудио или видео из мультимедийных файлов, хранящихся в ресурсах приложения (необработанных ресурсах), из автономных файлов в файловой системе или из потока данных, поступающего по сетевому соединению. В этой статье мы рассмотрим, как использовать мультимедийную среду для воспроизведения потокового аудио. Мы рассмотрим два основных класса в среде мультимедиа — MediaPlayer (основной API для воспроизведения звука и видео) и AudioManager (управляет источниками звука и выводом звука на устройстве) и используем их при создании простого аудиоплеера, который позволяет пользователю просматривать и воспроизводить контент из SoundCloud.

Начиная

Как уже упоминалось, мы собираемся создать приложение, которое обращается к API-интерфейсу SoundCloud, поэтому сначала необходимо зарегистрировать учетную запись на портале SoundCloud Developer по адресу developers.soundcloud.com . Создав учетную запись, нажмите ссылку «Зарегистрировать новое приложение» в правой части этой страницы.

Register a new SoundCloud App

На следующем экране назовите свое приложение. Мы назовем наше приложение SPPlayer .

Naming your App

На следующей странице вам будут показаны детали вашего приложения. Оставьте страницу открытой, так как вам нужно будет скопировать идентификатор клиента, показанный в приложении Android.

С этой настройкой мы сейчас создадим приложение для Android. Создайте новый проект Android (я использую Android Studio. Если вы используете Eclipse, некоторые шаги, предпринятые в этом руководстве, будут отличаться, например, добавление библиотек. Вы можете найти онлайн-справку о том, как использовать Eclipse.).

Создайте новый проект и назовите приложение SPPlayer . Установите собственный домен компании и нажмите «Далее». На следующем экране я оставил Minimum SDK со значением по умолчанию API 15. Выберите шаблон Empty Activity на следующем экране и на последнем экране нажмите Finish .

Затем откройте файл build.gradle вашего приложения и добавьте библиотеки gson , retrofit , converter-gson и picasso .

Adding Dependencies

Добавьте следующие зависимости в файл.

 compile 'com.google.code.gson:gson:2.8.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' compile 'com.squareup.retrofit2:converter-gson:2.1.0' compile 'com.squareup.picasso:picasso:2.5.2' 

Я пойду, почему мы используем библиотеки по мере прохождения учебника.

Когда вы изменяете файл build.gradle , вы получите сообщение о том, что для правильной работы среды IDE необходима синхронизация . Нажмите Sync Now справа от этого сообщения. Gradle получит добавленные зависимости. Вам понадобится подключение к Интернету для Gradle, чтобы получить их.

Для начала создайте класс, который будет содержать некоторые данные конфигурации, такие как идентификатор клиента и базовый URL-адрес для конечных точек SoundCloud. Создайте класс с именем Config и измените его, как показано

 package com.echessa.spplayer; /** * Created by echessa on 11/26/16. */ public class Config { public static final String CLIENT_ID = "YOUR_CLIENT_ID" ; public static final String API_URL = "https://api.soundcloud.com" ; } 

Замените идентификатор клиента приложения SoundCloud своим собственным.

Константа API_URL содержит базовый URL-адрес для конечных точек API, как указано в этом справочнике по API SoundCloud HTTP . Если вы хотите знать, что можно сделать с помощью API, справочное руководство является хорошим источником для чтения.

Затем создайте объект Track который будет содержать данные для каждого аудиофайла, извлеченного из SoundCloud. Создайте класс с именем Track и добавьте следующие поля и методы получения.

 package com.echessa.spplayer; import com.google.gson.annotations.SerializedName; /** * Created by echessa on 11/26/16. */ public class Track { @SerializedName ( "title" ) private String mTitle; @SerializedName ( "id" ) private int mID; @SerializedName ( "stream_url" ) private String mStreamURL; @SerializedName ( "artwork_url" ) private String mArtworkURL; public String getTitle () { return mTitle; } public int getID () { return mID; } public String getStreamURL () { return mStreamURL; } public String getArtworkURL () { return mArtworkURL; } } 

В приведенном выше коде мы создаем четыре поля, в которых будут храниться данные о треках, которые нас интересуют. Чтобы узнать, какие данные получит вызов API, вы можете попробовать следующий URL в своем браузере. Он извлекает данные одной дорожки в формате JSON.

 http: //api .soundcloud .com /tracks/ 13158665. json?client_id=YOUR_CLIENT_ID 

В классе Track обратите внимание на аннотации @SerializedName для каждого поля и импорт gson . Библиотека Gson — это библиотека с открытым исходным кодом от Google, которая сериализует и десериализует объекты Java в (и из) JSON. Выше он будет извлекать JSON и сопоставлять его с полями объекта, в противном случае вам придется написать намного больше кода, чтобы получить данные из JSON и создать объект Track с ним. Вот почему мы не используем никаких сеттеров в классе, Gson установит поля автоматически при создании объекта Track.

Примечание : я не буду упоминать импорт, необходимый после каждого добавления кода. Если требуется импорт, IDE предложит вам добавить его, и вы также можете настроить IDE так, чтобы он автоматически разрешал импорт . Я буду упоминать импорт только в случае конфликта импорта, чтобы вы знали, какой класс использовать.

Откройте файл AndroidManifest.xml и добавьте следующее разрешение.

 < uses-permission android:name = "android.permission.INTERNET" /> 

Затем добавьте Interface к проекту под названием SCService . Измените его, как показано.

 package com.echessa.spplayer; import java.util.List; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; /** * Created by echessa on 11/26/16. */ public interface SCService { @GET ( "/tracks?client_id=" + Config.CLIENT_ID) Call<List<Track>> getRecentTracks( @Query ( "created_at" ) String date); } 

Здесь мы используем библиотеку Retrofit в интерфейсе. Retrofit — это библиотека с открытым исходным кодом от Square, которая упрощает HTTP-связь, превращая удаленные API в декларативные, безопасные для типов интерфейсы. Методы, объявленные в интерфейсе, представляют собой одну удаленную конечную точку API. Аннотации описывают, как метод отображается на HTTP-запрос.

Библиотека упрощает загрузку данных JSON или XML из веб-API. Как только данные загружены, они анализируются в простой старый Java-объект (POJO), который должен быть определен для каждого ресурса в ответе.

Приведенный выше код получит список треков из SoundCloud. Мы добавляем аннотацию @Query чтобы добавить дополнительные параметры в URL, который будет вызываться. Здесь мы указываем параметр created_at at, который будет использоваться API SoundCloud для фильтрации возвращаемых результатов; API вернет только треки, созданные с указанной даты.

Чтобы проверить, что это работает, поместите следующее в метод onCreate(Bundle) в MainActivity.java после вызова setContentView(R.layout.activity_main); ,

 Retrofit retrofit = new Retrofit.Builder() .baseUrl(Config.API_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); SCService scService = retrofit.create(SCService.class); scService.getRecentTracks( "last_week" ).enqueue( new Callback<List<Track>>() { @Override public void onResponse (Call<List<Track>> call, Response<List<Track>> response) { if (response.isSuccessful()) { List<Track> tracks = response.body(); showMessage(tracks.get( 0 ).getTitle()); } else { showMessage( "Error code " + response.code()); } } @Override public void onFailure (Call<List<Track>> call, Throwable t) { showMessage( "Network Error: " + t.getMessage()); } }); 

Выше сначала создается экземпляр объекта Retrofit с вызовом нескольких функций. При создании экземпляра объекта необходимо baseUrl() и build() , а baseUrl() — до build() . Другие методы не являются обязательными.

baseUrl() устанавливает базовый URL-адрес API, а build() создает экземпляр Retrofit с настроенными значениями. Выше мы addConverterFactory() который добавляет фабрику преобразователей для сериализации и десериализации объектов. Наш проект будет обрабатывать данные JSON, поэтому мы используем GsonConverterFactory который способен кодировать и декодировать объекты в и из JSON. Доступны другие модули конвертера, и вы можете использовать более одного в соответствии с потребностями вашего приложения. При использовании более одного конвертера порядок, в котором они указаны, имеет значение. Модифицированный попытается проанализировать данные, которые он получает с первым указанным преобразователем, и он перейдет к следующему преобразователю, только если предыдущий не сможет проанализировать данные. Ниже приведен список доступных конвертеров.

  • Gson : com.squareup.retrofit2:converter-gson:2.1.0
  • Простой XML : com.squareup.retrofit2:converter-simplexml:2.1.0
  • Джексон : com.squareup.retrofit2:converter-jackson:2.1.0
  • Protobuf : com.squareup.retrofit2:converter-protobuf:2.1.0
  • Моши : com.squareup.retrofit2:converter-moshi:2.1.0
  • Провод : com.squareup.retrofit2:converter-wire:2.1.0
  • com.squareup.retrofit2:converter-scalars:2.1.0 : com.squareup.retrofit2:converter-scalars:2.1.0

Вы также можете создать свой собственный конвертер, расширив абстрактный класс Converter.Factory .

Retrofit — это класс, который преобразует интерфейс API в объект, который выполняет сетевые запросы. Чтобы использовать наш SCService мы создаем объект Retrofit а затем используем его для создания экземпляра интерфейса.

В приведенном выше коде класс Retrofit генерирует реализацию интерфейса SCService . Каждый вызов сгенерированного SCService отправляет HTTP-запрос на удаленный веб-сервер.

Мы вызываем SCService getRecentTracks() передавая ей строку last_week которая будет использоваться в качестве значения параметра запроса created_at . SoundCloud ожидает дату для этого параметра в формате yyyy-mm-dd hh:mm:ss , но также указывает некоторые специальные значения, которые можно использовать для указания даты. Доступны следующие варианты: last_year , last_two_weeks , last_week , last_day и last_hour .

Чтобы выполнить запрос к серверу, вы можете сделать синхронный вызов с помощью call.execute() или асинхронный вызов с использованием call.enqueue(new Callback<>() {}) . Мы используем последнее, передавая ему функцию Callback которая будет вызываться при возврате ответа. В обратном вызове мы возвращаем треки и отображаем название первого трека в списке, если запрос был успешным; в противном случае мы выводим сообщение об ошибке пользователю.

Прямо здесь вы можете увидеть преимущество использования Retrofit. Когда ответ возвращается с сервера, его response.body() можно назначить List<Track> tracks которые автоматически проанализируют данные JSON с сервера и преобразуют их в объекты Track . Без Retrofit вам пришлось бы писать код, который выполняет анализ.

Вышеуказанная реализация не лучший способ сделать HTTP-запросы. Мы сделаем несколько звонков на сервер и, используя вышеизложенное, будем создавать Retrofit и SCService каждый раз, когда делается запрос, и это дорогостоящие операции, поэтому это влияет на общую производительность. Поэтому мы будем использовать шаблон Singleton , чтобы Retrofit и SCService создавались только один раз и использовались при необходимости. Мы сделаем это в ближайшее время. А пока добавьте следующую функцию в класс, который вызывается для отображения сообщений Toast для пользователя.

 private void showMessage (String message) { Toast.makeText(MainActivity. this , message, Toast.LENGTH_LONG).show(); } 

Запустите приложение. Вы должны увидеть сообщение Toast, в котором будет показан заголовок первой выбранной дорожки. Я получил:

First Track Fetched

Теперь мы улучшим производительность приложения, придерживаясь шаблона Singleton .

Добавьте следующий класс в проект.

 package com.echessa.spplayer; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; /** * Created by echessa on 11/26/16. */ public class SoundCloud { private static final Retrofit RETROFIT = new Retrofit.Builder() .baseUrl(Config.API_URL) .addConverterFactory(GsonConverterFactory.create()) .build(); private static final SCService SERVICE = RETROFIT.create(SCService.class); public static SCService getService () { return SERVICE; } } 

Это создает объекты Retrofit и SCService как мы это делали ранее, затем включает функцию, которая возвращает сервис. Поскольку RETROFIT и SERVICE являются окончательными и статическими , они будут создаваться только один раз и повторно использоваться каждый раз при создании объекта SoundCloud .

Теперь вы можете использовать это в коде, который мы поместили в onCreate(Bundle) в классе MainActivity .

 SCService scService = SoundCloud.getService(); scService.getRecentTracks( "last_week" ).enqueue( new Callback<List<Track>>() { @Override public void onResponse (Call<List<Track>> call, Response<List<Track>> response) { if (response.isSuccessful()) { List<Track> tracks = response.body(); showMessage(tracks.get( 0 ).getTitle()); } else { showMessage( "Error code " + response.code()); } } @Override public void onFailure (Call<List<Track>> call, Throwable t) { showMessage( "Network Error: " + t.getMessage()); } }); 

Если вы запустите приложение, вы сможете увидеть заголовок первого возвращенного трека, как и раньше.

Теперь, когда мы можем получить данные с сервера, нам нужно создать список, который будет отображать данные.

Добавьте файл макета, щелкнув правой кнопкой мыши папку «Макет» и выбрав « Создать» -> «Файл ресурса макета» . Назовите его tracklistrow и добавьте следующее в файл track_list_row.xml .

 <?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:orientation = "vertical" android:paddingLeft = "@dimen/activity_vertical_margin" android:paddingRight = "@dimen/activity_vertical_margin" android:layout_width = "match_parent" android:layout_height = "@dimen/row_height" > < ImageView android:id = "@+id/track_image" android:layout_centerVertical = "true" android:padding = "@dimen/image_padding" android:layout_width = "@dimen/image_size" android:layout_height = "@dimen/image_size" /> < TextView android:layout_centerVertical = "true" android:id = "@+id/track_title" android:layout_marginLeft = "@dimen/activity_vertical_inner_margin" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:textAppearance = "@style/TextAppearance.AppCompat.Body2" /> </ RelativeLayout > 

Приведенное выше создает макет, который мы будем использовать для каждой строки в списке дорожек. Мы покажем изображение обложки трека и название трека.

Модифицируйте Activity_main.xml как показано.

 <?xml version="1.0" encoding="utf-8"?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".MainActivity" > < ListView android:id = "@+id/track_list_view" android:layout_width = "match_parent" android:layout_height = "match_parent" /> </ RelativeLayout > 

Это добавляет ListView к деятельности.

Добавьте следующее в файл измерения.xml . Не заменяйте другие значения в этом файле, мы все еще используем их.

 < dimen name = "row_height" > 72dp </ dimen > < dimen name = "image_padding" > 4dp </ dimen > < dimen name = "image_size" > 48dp </ dimen > < dimen name = "activity_vertical_inner_margin" > 56dp </ dimen > 

Затем создайте следующий пользовательский адаптер, который будет использоваться для представления списка.

 package com.echessa.spplayer; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.squareup.picasso.Picasso; import java.util.List; /** * Created by echessa on 11/26/16. */ public class SCTrackAdapter extends BaseAdapter { private Context mContext; private List<Track> mTracks; public SCTrackAdapter (Context context, List<Track> tracks) { mContext = context; mTracks = tracks; } @Override public int getCount () { return mTracks.size(); } @Override public Track getItem ( int position) { return mTracks.get(position); } @Override public long getItemId ( int position) { return position; } @Override public View getView ( int position, View convertView, ViewGroup parent) { Track track = getItem(position); ViewHolder holder; if (convertView == null ) { convertView = LayoutInflater.from(mContext).inflate(R.layout.track_list_row, parent, false ); holder = new ViewHolder(); holder.trackImageView = (ImageView) convertView.findViewById(R.id.track_image); holder.titleTextView = (TextView) convertView.findViewById(R.id.track_title); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.titleTextView.setText(track.getTitle()); // Trigger the download of the URL asynchronously into the image view. Picasso.with(mContext).load(track.getArtworkURL()).into(holder.trackImageView); return convertView; } static class ViewHolder { ImageView trackImageView; TextView titleTextView; } } 

Приведенный выше адаптер использует шаблон проектирования ViewHolder, который улучшает производительность представления списка.

При прокрутке ListView ваш код может часто вызывать findViewById() что может снизить производительность. Даже когда адаптер возвращает раздутое представление для повторного использования, вам все равно нужно искать элементы и обновлять их. Способ обойти повторное использование findViewById() заключается в использовании шаблона проектирования ViewHolder .

Объект ViewHolder хранит каждое из представлений компонента в поле тега макета, поэтому вы можете сразу получить к ним доступ без необходимости повторного поиска.

В приведенном выше коде мы создаем класс для хранения набора представлений: static class ViewHolder . Затем в getView(int, View, ViewGroup) мы заполняем ViewHolder и сохраняем его внутри макета. После этого к каждому представлению теперь можно обращаться без необходимости поиска, что экономит ценные циклы процессора.

Мы устанавливаем текст строки списка дорожек в качестве заголовка дорожки и извлекаем изображение дорожки с помощью библиотеки Пикассо . Библиотека Пикассо, также из Square, является библиотекой загрузки и кэширования изображений для Android. Помимо загрузки и кэширования изображений, вы можете выполнять с ним некоторые преобразования, такие как заданный размер и обрезка. Вы также можете использовать его для установки изображения заполнителя, которое будет отображаться при загрузке изображений, и изображения «ошибка», которое будет отображаться при сбое загрузки. В приведенном выше коде мы используем его, чтобы загрузить изображение с заданного URL-адреса и поместить его в представление изображений строки списка дорожек.

В MainActivity.java добавьте следующие переменные класса.

 private List<Track> mListItems; private SCTrackAdapter mAdapter; 

Внесите следующие изменения в onCreate(Bundle) и добавьте метод loadTracks(List<Track>) показанный ниже.

 @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); mListItems = new ArrayList<Track>(); ListView listView = (ListView)findViewById(R.id.track_list_view); mAdapter = new SCTrackAdapter( this , mListItems); listView.setAdapter(mAdapter); SCService scService = SoundCloud.getService(); scService.getRecentTracks( "last_week" ).enqueue( new Callback<List<Track>>() { @Override public void onResponse (Call<List<Track>> call, Response<List<Track>> response) { if (response.isSuccessful()) { List<Track> tracks = response.body(); loadTracks(tracks); } else { showMessage( "Error code " + response.code()); } } @Override public void onFailure (Call<List<Track>> call, Throwable t) { showMessage( "Network Error: " + t.getMessage()); } }); } private void loadTracks (List<Track> tracks) { mListItems.clear(); mListItems.addAll(tracks); mAdapter.notifyDataSetChanged(); } 

В приведенном выше коде мы создаем представление списка и экземпляр SCTrackAdapter и устанавливаем его в качестве адаптера представления списка. Затем мы получаем последние треки из API и вызываем loadTracks(List<Track>) где мы добавляем треки в список массивов, используемый адаптером, и уведомляем адаптер об изменении.

Запустите приложение, и список загрузится с последними треками из SoundCloud, а обложка альбома отобразится слева от списка. Для треков без обложки альбома вы можете указать изображение, которое будет отображаться с помощью Picasso. Обратитесь к документации, чтобы узнать, что еще предлагает библиотека.

Showing Artwork

Когда вы выбираете элемент в списке, ничего не происходит. Мы хотим, чтобы выбранный трек был воспроизведен.

Сначала мы добавим панель инструментов внизу экрана, которая покажет выбранную дорожку и элементы управления для воспроизведения и паузы. Модифицируйте Activity_main.xml как показано.

 < LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" android:orientation = "vertical" tools:context = ".MainActivity" > < ListView android:id = "@+id/track_list_view" android:layout_weight = "100" android:layout_width = "match_parent" android:layout_height = "0dp" /> < android.support.v7.widget.Toolbar android:background = "#333333" android:theme = "@style/Base.ThemeOverlay.AppCompat.Dark.ActionBar" android:layout_width = "match_parent" android:layout_height = "100dp" > < ImageView android:id = "@+id/selected_track_image" android:layout_width = "92dp" android:layout_height = "92dp" /> < TextView android:id = "@+id/selected_track_title" android:paddingLeft = "8dp" android:layout_width = "wrap_content" android:layout_height = "wrap_content" /> < ImageView android:id = "@+id/player_control" android:layout_gravity = "right" android:layout_width = "@dimen/image_size" android:layout_height = "@dimen/image_size" /> </ android.support.v7.widget.Toolbar > </ LinearLayout > 

Здесь мы добавляем панель инструментов в макет, который расположен в нижней части экрана. Панель инструментов имеет ImageView , который отображает обложку альбома, TextView , который отображает название трека, и другой ImageView , который отображает значки воспроизведения и паузы .

В MainActivity.java добавьте следующие переменные класса.

 private TextView mSelectedTrackTitle; private ImageView mSelectedTrackImage; 

Добавьте следующее в onCreate(Bundle) под listView.setAdapter(mAdapter); заявление.

 mSelectedTrackTitle = (TextView)findViewById(R.id.selected_track_title); mSelectedTrackImage = (ImageView)findViewById(R.id.selected_track_image); listView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick (AdapterView<?> parent, View view, int position, long id) { Track track = mListItems.get(position); mSelectedTrackTitle.setText(track.getTitle()); Picasso.with(MainActivity. this ).load(track.getArtworkURL()).into(mSelectedTrackImage); } }); 

Это устанавливает вид изображения панели инструментов и текстовое представление с данными выбранной дорожки.

Запустите приложение и при выборе дорожки из списка на панели инструментов появится информация о дорожке.

Track Information displayed

Теперь последний шаг воспроизведения выбранного трека.

Добавьте следующее к классу.

 private MediaPlayer mMediaPlayer; private ImageView mPlayerControl; 

Затем добавьте следующее после оператора, который назначает переменную mSelectedTrackImage .

 mPlayerControl = (ImageView)findViewById(R.id.player_control); 

Добавьте следующее в onCreate(Bundle) после setContentView(R.layout.activity_main); заявление.

 mMediaPlayer = new MediaPlayer(); mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mMediaPlayer.setOnPreparedListener( new MediaPlayer.OnPreparedListener() { @Override public void onPrepared (MediaPlayer mp) { togglePlayPause(); } }); 

Выше мы создаем экземпляр mMediaPlayer и устанавливаем для него тип аудиопотока. Класс MediaPlayer может использоваться для управления воспроизведением аудио / видео файлов и потоков. Затем мы регистрируем обратный вызов, который будет вызываться, когда источник медиа готов к воспроизведению. Обратный вызов делает вызов функции ниже.

Добавьте следующую функцию в класс.

 private void togglePlayPause () { if (mMediaPlayer.isPlaying()) { mMediaPlayer.pause(); mPlayerControl.setImageResource(R.drawable.ic_play); } else { mMediaPlayer.start(); mPlayerControl.setImageResource(R.drawable.ic_pause); } } 

Выше проверяет, воспроизводится ли медиаплеер. Если это так, он приостанавливает его и меняет значок управления плеером на значок воспроизведения. Если медиаплеер не воспроизводился, он начинает воспроизведение и меняет значок на значок «Пауза».

Чтобы использовать используемые значки, загрузите папку «Ресурсы» и вставьте папки для рисования в папку res вашего проекта. Значки взяты из репозитория Google Material Design Icons .

Затем добавьте следующее в метод onItemClick(AdapterView<?>, View, int, long) представления списка после оператора, который устанавливает изображение панели инструментов с помощью Picasso.

 if (mMediaPlayer.isPlaying()) { mMediaPlayer.stop(); mMediaPlayer.reset(); } try { mMediaPlayer.setDataSource(track.getStreamURL() + "?client_id=" + Config.CLIENT_ID); mMediaPlayer.prepareAsync(); } catch (IOException e) { e.printStackTrace(); } 

Когда элемент выбран, проверяется, играет ли игрок. Если дорожка воспроизводилась, она останавливается и медиаплеер сбрасывается, прежде чем можно будет воспроизвести выбранную дорожку. Затем мы устанавливаем источник данных медиаплеера, который является полным URL потокового аудиофайла. Затем мы готовим проигрыватель к воспроизведению асинхронно. Здесь вы можете вызывать prepare() или prepareAsync() , но для потоков следует вызывать prepareAsync() который возвращает немедленно, а не блокирует, пока достаточное количество данных не буферизуется. Для файлов нормально вызывать prepare() который блокируется, пока MediaPlayer будет готов к воспроизведению.

Затем добавьте следующее после оператора, который инициализирует mPlayerControl .

 mPlayerControl.setOnClickListener( new View.OnClickListener() { @Override public void onClick (View v) { togglePlayPause(); } }); 

Вышеуказанное устанавливает слушателя по щелчку на представление изображения управления плеером, которое переключает состояние воспроизведения / паузы игрока.

Запустите приложение, и вы сможете воспроизвести выбранный аудиофайл, переключиться на другую дорожку, сделав новый выбор, и приостановить воспроизведение, нажав «Пауза» на панели инструментов.

Final App

После завершения воспроизведения аудиофайла значок на панели инструментов продолжит отображать значок паузы. Мы хотим, чтобы значок на панели инструментов изменился на Воспроизведение, когда трек завершит воспроизведение. Для этого мы установим прослушиватель по завершении на медиаплеере, чтобы определить, когда это сделано, и изменим значок панели инструментов.

Добавьте нижеприведенный вызов в mMediaPlayer.setOnPreparedListener() .

 mMediaPlayer.setOnCompletionListener( new MediaPlayer.OnCompletionListener() { @Override public void onCompletion (MediaPlayer mp) { mPlayerControl.setImageResource(R.drawable.ic_play); } }); 

Запустите приложение, и теперь значок панели инструментов должен измениться после завершения воспроизведения трека.

Наконец, добавьте следующее в файл. Это освобождает медиаплеер, когда активность уничтожена. Мы не хотим держаться за какие-либо ресурсы, когда они не используются.

 @Override protected void onDestroy () { super .onDestroy(); if (mMediaPlayer != null ) { if (mMediaPlayer.isPlaying()) { mMediaPlayer.stop(); } mMediaPlayer.release(); mMediaPlayer = null ; } } 

Это подводит нас к концу урока. Мы создали простой аудиоплеер и увидели, как использовать класс MediaPlayer для воспроизведения потокового аудио. Вы можете скачать готовый проект с GitHub здесь . Не забудьте вставить свой идентификатор клиента SoundCloud в класс Config.java.