前台服务Service更新Notification进度条

在弄音乐播放的app的时候,我们时常需要用一个前台Service来播放音乐,同时管理和更新Notification,而且,Notification的点击事件需要用Broadcast来传递。

今天就手把手来看看如何在Service中管理Notification以及Broadcast!

前台服务Service更新Notification进度条

 首先,我们先来看看Notification的布局,

前台服务Service更新Notification进度条

xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="20dp"
    android:orientation="vertical"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@drawable/bg_notification">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:orientation="horizontal">
        <ImageView
            android:id="@+id/notify_cover"
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:src="@mipmap/ic_launcher"
            android:layout_gravity="center_horizontal" />
        <LinearLayout
            android:id="@+id/ll_songinfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:layout_marginLeft="20dp"
            android:layout_gravity="center_vertical">
            <TextView
                android:id="@+id/notify_song"
                android:text="歌曲名"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                 />
            <TextView
                android:id="@+id/notify_singer"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="歌手"/>
        </LinearLayout>

    </LinearLayout>

    <ProgressBar
        android:id="@+id/notify_progress_bar"
        android:layout_width="match_parent"
        android:layout_height="10px"
        android:layout_below="@id/notify_cover"
        android:layout_marginTop="10dp"
        style="@style/Widget.AppCompat.ProgressBar.Horizontal"
        android:max="100"
        android:progress="50"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginTop="10dp"
        android:paddingLeft="40dp"
        android:paddingRight="40dp"
        android:gravity="center"
        >
        <Button
            android:id="@+id/notify_last_song"
            android:layout_width="40dp"
            android:layout_height="match_parent"
            android:background="@drawable/ic_icon_last_music"/>
        <Button
            android:id="@+id/notify_pause"
            android:layout_width="40dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="40dp"
            android:layout_marginRight="40dp"
            android:background="@drawable/ic_play_btn_pause"/>
        <Button
            android:id="@+id/notify_next_song"
            android:layout_width="40dp"
            android:layout_height="match_parent"
            android:background="@drawable/ic_icon_next_music"/>
    </LinearLayout>

</LinearLayout>

没什么好说的,重点也不是这个!!

再来看看我们接收Notification点击事件的BroadcastReceiver,是用kotlin写的

class NotificationBroadcast : BroadcastReceiver() {

    companion object {
        //稍后设置点击事件Intent的Action
        val START_OR_PAUSE = "com.example.hp.MSCloudMusic.StartOrPause"
    }

    /**
     * @param context The Context in which the receiver is running.
     * @param intent The Intent being received.
     */
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.e("Broadcast: ","receive broadcast!")
    }
}

然后在PlayService的onCreate方法中

public class PlayService extends Service {

    private NotificationHelper notificationHelper;
    Notification notification;

    @Override
    public void onCreate() {
        super.onCreate();
        //注册广播接收者
        registerReceiver();
        //设置Notification
        setNotification();
    }

    /**
    *    注册广播接受者
    */
    private void registerReceiver() {
        NotificationBroadcast broadcast = new NotificationBroadcast();
        //动态设置该广播接收者接收的Action
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(NotificationBroadcast.Companion.getSTART_OR_PAUSE());
        registerReceiver(broadcast,intentFilter);
    }

    /**
    *    开启Notification
    */
    private void setNotification(){
        //初始化NotificationHelper
        notificationHelper = new NotificationHelper(this);  
        //这里获取内容,等下设置到Notification上     
        String song="",singer="";
        int progress=0;
        if(mPlayingMusic!=null){
            song = mPlayingMusic.getTitle();
            singer = mPlayingMusic.getAlbumPic();
            progress = (int)(((float)mPlayer.getCurrentPosition() / (float)mPlayer.getDuration())*100);
        }
        //通过NotificationHelper类来得到Notification
        notification = notificationHelper.getPlayMusicNotification(song,singer,progress);
        //开启前台服务
        startForeground(1,notification);
    }
}

Service主要注册了广播接受者和其接收的Action;然后通过NotificationHelper类的getPlayMusicNotification方法来创建Notification,然后startForeground来开启Notification,这里startForeground的第一个参数需要和后面更新Notification时传入的id一样。我们来看一下NotificationHelper这个类

public class NotificationHelper {

    private NotificationManager manager;
    private Context context;
    private RemoteViews remoteViews;
    private NotificationCompat.Builder builder;

    public NotificationHelper(Context context){
        this.context = context;
        init();
    }

    public void init(){
        if(manager==null) {
            manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        }
    }

    private NotificationCompat.Builder getNotificationBuilder(){
        if(builder!=null){
            return builder;
        }
        //targetSdk26及以上需要添加NotificationChannel
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            NotificationChannel channel = new NotificationChannel("channel_id","channel_name",NotificationManager.IMPORTANCE_LOW);
            channel.setLockscreenVisibility(VISIBILITY_SECRET);
            manager.createNotificationChannel(channel);
        }
        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
        //设置状态栏上的小图标,否则会报错
        builder.setSmallIcon(R.drawable.ic_icon_quantum_statistical);
        this.builder = builder;
        return this.builder;
    }

    public Notification getPlayMusicNotification(String song ,String singer,int progress){
        NotificationCompat.Builder builder = getNotificationBuilder();

        //自定义Notificaiton的时候需要用到RemoteViews,传入布局文件
        remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_play_music);
        //设置控件内容
        remoteViews.setTextViewText(R.id.notify_song, TextUtils.isEmpty(song)?"未知":song);
        remoteViews.setTextViewText(R.id.notify_singer,TextUtils.isEmpty(singer)?"未知":singer);
        remoteViews.setProgressBar(R.id.notify_progress_bar,100,progress,false);

        //设置点击事件,Action为NotificationBroadcast的静态变量com.example.hp.MSCloudMusic.StartOrPause
        Intent intent = new Intent(NotificationBroadcast.Companion.getSTART_OR_PAUSE());
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0,intent,0);
        remoteViews.setOnClickPendingIntent(R.id.notify_pause,pendingIntent);

        //将remoteViews设置给builder,setCustomContentView是正常情况下的视图
        //setCustomBigContentView是展开时的Notification
        //这两个方法可以传入不同的remoteViews,但是点击事件和内容也要分别设置
        builder.setCustomContentView(remoteViews);
        builder.setCustomBigContentView(remoteViews);

        return builder.build();
    }

    //更新Notification的方法
    public void updateNotification(String song,String singer,int progress){
        Notification newNotification = getPlayMusicNotification(song,singer,progress);
        //这里notify方法的第一个参数要和startForeground的的id一样
        manager.notify(1,newNotification);
    }
}

这里需要注意几点:
①targetSdk26及以上需要适配NotificationChannel
②设置状态栏上的小图标,否则会报错

其他的地方注释写的很清楚了。

这样,Service调用startForeground的时候,Notification就可以显示了,一个是正常情况下的高度,一个是展开后的高度

前台服务Service更新Notification进度条前台服务Service更新Notification进度条

接下来就是Notification的更新了

public class PlayService extends Service {

    //更新Notification
    private void updateNotification() {
        if(notificationHelper==null){
            setNotification();
        } else {
            String song = "", singer = "";
            int progress = 0;
            if (mPlayingMusic != null) {
                song = mPlayingMusic.getTitle();
                singer = mPlayingMusic.getArtist();
                progress = (int)(((float)mPlayer.getCurrentPosition() / (float)mPlayer.getDuration())*100);
//                Log.e(TAG, mPlayer.getCurrentPosition()+"/"+mPlayer.getDuration());
//                Log.e(TAG, progress+"");
            }
            //调用updateNotification
            notificationHelper.updateNotification(song, singer, progress);
        }
    }
}

这里没什么,只是调用了NotificationHelper的updateNotification方法,传入新的内容

    public void updateNotification(String song,String singer,int progress){
        Notification newNotification = getPlayMusicNotification(song,singer,progress);
        manager.notify(1,newNotification);
    }

这里很简单,通过getPlayMusicNotification获取设置了新内容的Notification,然后调用manager的notify方法,这里的id需要和前面startforeground传入的id一样。

至此,Notification的更新也结束了! 点击按钮时,也能通过BroadcastReceiver处理事件了!

前台服务Service更新Notification进度条

前台服务Service更新Notification进度条

喜欢点个赞~~