Вы определенно захотите использовать сервис, а не AsyncTask. Основная причина этого заключается в том, что если вы хотите, чтобы музыка запускалась, даже когда приложение было приостановлено / помещено в фоновый режим, например, когда пользователь переходит в другое приложение, это будет делать только служба. AsyncTasks не будет работать в фоновом режиме таким образом.
Чтобы включить некоторую справочную информацию о фоновых сервисах, они используют события из контекстов приложения, таких как действия и сервисы переднего плана, чтобы уведомить их о том, когда следует выполнять работу. Эта работа выполняется с помощью функции onStartCommand () службы. Подробнее об услугах можно прочитать в документации для Android https://developer.android.com/guide/components/services
При этом служба будет работать в фоновом режиме, но ее все равно можно будет прервать, если ОС потребуется выполнить еще одну задачу. Поэтому, чтобы музыка воспроизводилась надежно и перезапускалась вскоре после того, как ОС по какой-либо причине выгрузила службу, вам необходимо указать START_STICKY в качестве возвращаемого значения из функции onStartCommand () службы. Но, как и во всем Android, предпочитайте версию совместимости START_STICKY_COMPATIBILITY несовместимой версии. START_STICKY / START_STICKY_COMPATIBILITY подходит для возврата в случае команды PLAY. То есть если событие, которое получает служба, это PLAY.
Возврат START_STICKY или START_STICKY_COMPATIBILITY из onStartCommand () в каждом случае приведёт к появлению службы, которая никогда не умирает, что потребляет вычислительную мощность и время автономной работы телефона, на котором он работает. Это может привести к потреблению процессора и разрядке батареи. Вот почему важно возвращать START_NOT_STICKY из onStartCommand, если пользователь пытается сделать паузу. То есть если событие, которое получает служба, - это ПАУЗА.
Вот урезанная версия того, что вы можете сделать так, чтобы ваша onStartCommand вашего сервиса выглядела так:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_PLAY)) {
...
return START_STICKY_COMPATIBILITY;
} else { // i.e. action is ACTION_PAUSE
...
return START_NOT_STICKY;
}
}
Редактировать: чтобы оставить это и остаток этого ответа - пытаясь упростить пост, я исключил соображения, связанные с ожиданием подготовки медиаплеера. Как примечание, сервис, вероятно, также должен будет обрабатывать ожидание, пока медиаплеер готовится к отдельному событию или из обработки события PLAY. Может также быть обработано изнутри действия до запуска службы, но это может быть более или менее сложным. Объяснить остальную часть проблем / соображений в этом ответе гораздо проще, не говоря об этом аспекте проблемы, хотя это необходимо учитывать для создания функционального приложения музыкального плеера.
Необходимо также предусмотреть, когда устройство заблокировано, чтобы некоторые аппаратные устройства не отключались. Попробуйте учесть следующее в ответ на событие PLAY в onStartCommand службы, чтобы учесть это:
// Setup Wake Mode Wake Lock so the music can play while device screen is locked
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.FULL_WAKE_LOCK);
// Setup wifi lock so wifi can stay on while device is locked and trying to save power
wifiLock = ((WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
wifiLock.acquire();
Другая проблема заключается в том, что ваш пользователь в идеале сможет убить службу, если она запущена. Если они убьют приложение, оно не убьет службу, как предполагалось, и музыка продолжит играть. Таким образом, пользователь должен иметь возможность контролировать службу с помощью уведомлений с элементами управления для приостановки и воспроизведения музыки. Это можно сделать с помощью службы переднего плана. Если вы хотите добавить слой обслуживания переднего плана, вы можете добавить вызов startForeground () в onStartCommand () службы в ответ на событие широковещания для PLAY. Вот урезанное onStartCommand () с добавленной логикой переднего плана:
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_PLAY)) {
...
Notification notification = setupNotification(); // where setupNotification is your own function
startForeground(1, notification);
return START_STICKY_COMPATIBILITY;
} else { // i.e. action is ACTION_PAUSE
...
stopForeground(false);
// NotificationManagerCompat needed here for swipeable notification b/c service will be killed
Notification notification = setupNotification();
NotificationManagerCompat nmc = NotificationManagerCompat.from(this);
nmc.notify(1, notification);
return START_NOT_STICKY;
}
}
Функции startForeground () принимают идентификатор и объект уведомления в качестве своих параметров. Уведомление может быть создано с помощью NotificationCompat.Builder с кодом, который выглядит примерно так (отмечая, что некоторые переменные здесь должны быть вставлены в соответствующее приложение):
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher_round_large);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.app_name))
.setContentText("Music is now playing") // change to paused on paused
.setTicker("Music Playing")
.setSmallIcon(R.drawable.status_bar_icon_xhdpi_48px)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingTapIntent)
.setDeleteIntent(pendingSwipeIntent)
.addAction(iconId, buttonText, pendingButtonIntent)
.setWhen(System.currentTimeMillis());
Обратите внимание на ожидающие намерения в коде выше. Они создаются с помощью класса PendingIntent.То есть создать ожидающее намерение для кнопки воспроизведения в уведомлении, например, так (где «this» - это фоновая служба, если вы создаете это намерение из фоновой службы)
Intent playIntent = new Intent(this, MyService.class);
playIntent.setAction(ACTION_PLAY);
PendingIntent pendingPlayIntent = PendingIntent.getService(this, 0, playIntent, 0);
Аналогично, создайте ожидающее намерение, когда пользователь нажимает на уведомление, чтобы оно открывало приложение со следующим кодом (опять же, где «this» - это фоновая служба, если вы создаете это намерение из фоновой службы). ):
Intent notificationIntent = new Intent(this, StreamActivity.class);
notificationIntent.setAction("ACTION_MAIN");
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Кроме того, создайте ожидающее намерение для действия смахивания в уведомлении, чтобы убить фоновую службу, подобную этой (опять же, где «this» - фоновая служба, если вы создаете эту цель из фоновой службы):
Intent swipeIntent = new Intent(this, MyService.class);
swipeIntent.setAction(ACTION_END_SERVICE);
PendingIntent pendingSwipeIntent = PendingIntent.getService(this, 0, swipeIntent, 0);
Надеюсь, этого достаточно, чтобы начать, но я бы порекомендовал начать этот процесс без сложного уровня активности на переднем плане. Затем добавьте его, как только увидите, что воспроизведение музыки из приложения работает.