【Android】MediaPlayer之前台服务播放音乐

作者:邹峰立,微博:zrunker,邮箱:[email protected],微信公众号:书客创作,个人平台:www.ibooker.cc。

本文选自书客创作平台第47篇文章。阅读原文 。

【Android】MediaPlayer之前台服务播放音乐_第1张图片
书客创作

在实际应用程序当中,往往会把播放音频的操作放在Service中,这样即使前台页面显示,后台依旧可以完成播放相关功能。而前台服务是实现即使前台页面不存在了,依旧可以完成播放相关功能,简单一点来说就是杀不死的服务,即使在内存不足的情况下也会存在。

之前两篇文章已经详细的说明了MediaPlayer的生命周期和使用技巧,而这篇文章讲述前台服务播放音乐的实现。

【Android】MediaPlayer生命周期分析

【Android】MediaPlayer之音频播放

在实现前台服务之前,首先要明白什么是Notification?什么是Service?

Notification通知

Notification是显示在通知栏上的框架,一般用于展示通知类的消息。

一般情况下是通过NotificationCompat.Builder进行创建。通过NotificationManager进行管理,如取消通知,更新通知等。但是要注意在创建Notification三要素不能少,否则将会创建失败或者运行异常,Notification三要素:小图标、标题、内容。不同的机型显示的效果也会有所不同。

显示一个Notification:

Intent intent = new Intent(this, MainActivity.class);
PendingIntent contentPendingIntent = PendingIntent.getActivity(this, CONTENT_PENDINGINTENT_REQUESTCODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent delIntent = new Intent(this, MediaPlayerService.class);
PendingIntent delPendingIntent = PendingIntent.getService(this, DELETE_PENDINGINTENT_REQUESTCODE, delIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Bitmap largeIcon = BitmapFactory.decodeResource(getResources(), R.drawable.test_bg);
// 初始化Notification,Notification三要素:小图标、标题、内容
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
          // 设置小图标,不展开时显示,当展开时现在在左侧
          .setSmallIcon(R.mipmap.ic_launcher)
          // 设置状态栏的显示的信息
          .setTicker("这是一个音频播放器")
          // 设置标题
          .setContentTitle("ZMediaPlayer")
          // 设置内容
          .setContentText("内容")
          // 设置通知时间,默认为系统发出通知的时间,通常不用设置
          .setWhen(System.currentTimeMillis())
          // 设置是否显示时间
          .setShowWhen(true)
          // 设置大图标-展开时一些手机上大图标会替换掉小图标
          .setLargeIcon(largeIcon)
          // 点击通知后自动清除
          .setAutoCancel(false)
          // 设置点击通知效果
          .setContentIntent(contentPendingIntent)
          // 设置删除时候出发的动作
          .setDeleteIntent(delPendingIntent)
          // 自定义视图
          .setContent(RemoteViews views)
          // 设置额外信息,一般显示在右下角
          .setContentInfo("额外信息")
          // 向通知添加声音、闪灯和振动效果的最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合
          .setDefaults(Notification.DEFAULT_VIBRATE);

Notification notification = builder.build();
// 获取NotificationManager实例
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// 发送通知,并设置id
notificationManager.notify(id, notification);

修改Notification:

// 通过id进行更新Notification
notification = builder.setContentTitle("ZMediaPlayer111")
          // 设置内容
          .setContentText("内容111")
          .build();
notificationManager.notify(id, notification);

取消Notification:

// 通过id进行取消Notification
if (notificationManager != null)
    notificationManager.cancel(id);

Service服务

Service为Android四大组件之一,所以要使用Service,必须添加到清单文件中。Service一般于运行于后台。

Service启动方式有两种:

  1. startService(Intent)
  2. bindService(Intent,ServiceConnection,flags)

startService(Intent)

  • 生命周期: onCreate()- >onStartCommand()->startService()->onDestroy()

  • 该方法启动service,会执行一个onStartCommand()的方法,所以一些操作可以放在 onStartCommand() 中进行处理。并且会随着应用程序的退出而销毁。

Intent intent = new Intent(MainActivity.this, MediaPlayerService.class);
intent.putExtra("playing", isPlay);
startService(intent);
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 得到键值对
    boolean playing = intent.getBooleanExtra("playing", false);
    if (playing)
        ......
    return super.onStartCommand(intent, flags, startId);
}

bindService(Intent,ServiceConnection,flags)

  • 生命周期:onCreate()->onBind()->onUnbind()->onDestroy()

  • 可由多个页面进行绑定,不随应用程序的退出而销毁。

Intent intent = new Intent(MainActivity.this, MediaplayerBinderService.class);
/*
 * Service:Service的桥梁
 * ServiceConnection:处理链接状态
 * flags:BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY.
 */
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

前台服务

前台服务要利用到以下两个方法:

startForeground(int id, Notification notification)

这个方法是启动Notification到前台,一般写在onCreate方法当中。

topForeground(boolean removeNotification)

这个方法是停止Notification,一般写在onDestroy方法当中。

实例-前台服务播放音乐

首先看一下效果图:

【Android】MediaPlayer之前台服务播放音乐_第2张图片
前台服务播放音乐效果图

代码实现:(部分代码不给出,稍后会提供Github地址)

首先定义MediaplayerBinderService类让其继承Service,并实现其onCreate()->onBind()->onDestroy()。

在onCreate()方法当中,需要实现初始化MediaPlayer,定义并显示Notification。

@Override
public void onCreate() {
    super.onCreate();

    Log.d("MediaPlayerBService", "OnCreate");

    // 初始化MediaPlayer
    initMediaPlayer();

    // 设置点击通知结果
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent contentPendingIntent = PendingIntent.getActivity(this, CONTENT_PENDINGINTENT_REQUESTCODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    Intent delIntent = new Intent(this, MediaPlayerService.class);
    PendingIntent delPendingIntent = PendingIntent.getService(this, DELETE_PENDINGINTENT_REQUESTCODE, delIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    // 自定义布局
    views = new RemoteViews(getPackageName(), R.layout.layout_mediaplayer);

    // 下一首
    Intent intentNext = new Intent("nextMusic1");
    PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, NEXT_PENDINGINTENT_REQUESTCODE, intentNext, PendingIntent.FLAG_CANCEL_CURRENT);
    views.setOnClickPendingIntent(R.id.tv_next, nextPendingIntent);

    // 暂停/播放
    Intent intentPlay = new Intent("playMusic1");
    PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, PLAY_PENDINGINTENT_REQUESTCODE, intentPlay, PendingIntent.FLAG_CANCEL_CURRENT);
    views.setOnClickPendingIntent(R.id.tv_pause, playPendingIntent);

    // 停止
    Intent intentStop = new Intent("stopMusic1");
    PendingIntent stopPendingIntent = PendingIntent.getBroadcast(this, STOP_PENDINGINTENT_REQUESTCODE, intentStop, PendingIntent.FLAG_CANCEL_CURRENT);
    views.setOnClickPendingIntent(R.id.tv_cancel, stopPendingIntent);

    builder = new NotificationCompat.Builder(this)
        // 设置小图标
        .setSmallIcon(R.drawable.test_bg)
        // 设置标题
        .setContentTitle("ZMediaPlayer")
        // 设置内容
        .setContentText("内容")
        // 点击通知后自动清除
        .setAutoCancel(false)
        // 设置点击通知效果
        .setContentIntent(contentPendingIntent)
        // 设置删除时候出发的动作
        .setDeleteIntent(delPendingIntent)
        // 自定义视图
        .setContent(views);

    // 获取NotificationManager实例
    notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    // 前台服务
    startForeground(NOTIFICATION_PENDINGINTENT_ID, builder.build());
}

在onBind()方法中,返回一个已经实例化好的Binder类,这里可以通过定义一个内部类来实现,并该内部类中实现获取当前服务的方法。

// 定义Binder类-当然也可以写成外部类
private MediaplayerBinder mediaplayerBinder = new MediaplayerBinder();

public class MediaplayerBinder extends Binder {
    public Service getService() {
        return MediaplayerBinderService.this;
    }
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
    Log.d("MediaPlayerBService", "onBind");
    return mediaplayerBinder;
}

在onDestroy()方法当中实现MediaPlayer销毁,Notification的销毁等。

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d("MediaPlayerBService", "onDestroy");
    if (mediaPlayer != null) {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }
    if (wifiLock != null && wifiLock.isHeld())
        wifiLock.release();
    // 取消Notification
    if (notificationManager != null)
        notificationManager.cancel(NOTIFICATION_PENDINGINTENT_ID);
    stopForeground(true);
    // 停止服务
    stopSelf();
}

最后在相应的Activity当中实现Service的绑定,并通过Binder获取服务实例,通过服务实例调用服务的效果方法。

// 定义两个全局变量
private MediaplayerBinderService bindService;
private boolean isBindService = false;

// 绑定服务
private void bindService() {
    if (!isBindService) {
        Intent intent = new Intent(MainActivity.this, MediaplayerBinderService.class);
        /*
         * Service:Service的桥梁
         * ServiceConnection:处理链接状态
         * flags:BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY.
         */
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

// 解除绑定
private void unBind() {
    if (isBindService) {
        unbindService(serviceConnection);
        isBindService = false;
    }
}

/**
 * serviceConnection是一个ServiceConnection类型的对象,它是一个接口,用于监听所绑定服务的状态
 */
private ServiceConnection serviceConnection = new ServiceConnection() {
    /**
     * 该方法用于处理与服务已连接时的情况。
     */
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        MediaplayerBinderService.MediaplayerBinder binder = (MediaplayerBinderService.MediaplayerBinder) service;
        bindService = (MediaplayerBinderService) binder.getService();
        isBindService = true;
    }

    /**
     * 该方法用于处理与服务断开连接时的情况。
     */
    @Override
    public void onServiceDisconnected(ComponentName name) {
        bindService = null;
    }
};

Github地址
阅读原文


【Android】MediaPlayer之前台服务播放音乐_第3张图片
微信公众号:书客创作

你可能感兴趣的:(【Android】MediaPlayer之前台服务播放音乐)