【发布时间】:2018-06-29 03:56:18
【问题描述】:
我想流式传输长度约为 1 小时的 mp3 文件。我想在后台播放文件。我看了很多教程视频。其中一些使用服务,一些使用异步任务。我也不知道我能在这些之间选择什么。哪个更好?
【问题讨论】:
标签: android android-studio android-asynctask
我想流式传输长度约为 1 小时的 mp3 文件。我想在后台播放文件。我看了很多教程视频。其中一些使用服务,一些使用异步任务。我也不知道我能在这些之间选择什么。哪个更好?
【问题讨论】:
标签: android android-studio android-asynctask
您肯定会想要使用服务,而不是 AsyncTask。这样做的主要原因是,如果您希望即使应用程序已暂停/进入后台也能运行音乐,例如当用户移动到另一个应用程序时,只有服务会这样做。 AsyncTasks 不会以这种方式在后台运行。
为了包含有关后台服务的一些背景信息,它们使用来自应用上下文(例如活动和前台服务)的事件来通知它们何时开始工作。这项工作是通过服务的 onStartCommand() 函数处理的。有关服务的更多信息,请参阅 Android 文档 https://developer.android.com/guide/components/services
话虽如此,服务将允许在后台运行,但如果操作系统需要完成另一项任务,它仍然可以被抢占。因此,为了让音乐可靠地播放,并在操作系统出于任何原因抢占服务后不久重新启动,您需要将 START_STICKY 指定为服务的 onStartCommand() 函数的返回值。但是,与所有 Android 一样,与不兼容的版本相比,更喜欢兼容版本 START_STICKY_COMPATIBILITY。 START_STICKY/START_STICKY_COMPATIBILITY 适合在 PLAY 命令的情况下返回。 IE。如果服务正在接收的事件是 PLAY。
在任何情况下从 onStartCommand() 返回 START_STICKY 或 START_STICKY_COMPATIBILITY 都会为您提供永不中断的服务,从而消耗运行它的手机的处理能力和电池寿命。这可能会导致处理器消耗和电池消耗。这就是为什么在用户尝试暂停时从 onStartCommand 返回 START_NOT_STICKY 很重要的原因。 IE。如果服务正在接收的事件是 PAUSE。
这是您可能希望服务的 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 事件的处理中。也可以在启动服务之前从活动内部处理,但这可能或多或少复杂。在不讨论问题的这一方面的情况下,解释此答案中的其余问题/考虑要容易得多,尽管必须考虑制作功能性音乐播放器应用程序。
还需要设置设备何时锁定,以免某些硬件外围设备关闭。考虑添加以下内容以响应服务的 onStartCommand 中的 PLAY 事件来解决此问题:
// 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();
另一个问题是,如果服务正在运行,您的用户最好能够终止该服务。如果他们终止应用程序,它不会按预期终止服务,并且音乐将继续播放。因此,用户应该能够通过带有控件的通知来控制服务以暂停和播放音乐。这可以使用前台服务来完成。如果要添加前台服务层,可以在服务的onStartCommand()中添加对startForeground()的调用,以响应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() 函数将一个 id 和一个 Notification 对象作为其参数。可以使用 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 类创建的。 IE。像这样为通知上的播放按钮创建待处理的意图(其中“this”是后台服务,假设您是从后台服务中创建此意图)
Intent playIntent = new Intent(this, MyService.class);
playIntent.setAction(ACTION_PLAY);
PendingIntent pendingPlayIntent = PendingIntent.getService(this, 0, playIntent, 0);
同样,当用户点击通知时创建一个待处理的 Intent,以便它使用以下代码打开应用程序(同样,“this”是后台服务,假设您是从后台服务中创建此 Intent ):
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);
希望这足以让您继续前进,但我建议在没有复杂的前台活动层的情况下开始此过程。然后在您看到应用内的音乐播放正常后添加它。
【讨论】:
如果您希望在用户退出应用后仍能播放音乐,您需要为此使用前台服务。所有音乐播放器都使用这种方法。
如果您希望音乐在用户存在特定活动时结束,那么只有在这种情况下才会考虑服务以外的任何内容。
异步任务不应用于此目的,因为它非常适合小型后台任务。播放音乐不在此类别中。
【讨论】: