1.什么是前台服务
前台服务是那些被认为用户知道(用户所认可的)且在系统内存不足的时候不允许系统杀死的服务。前台服务必须给状态栏提供一个通知,它被放到正在运行(Ongoing)标题之下——这就意味着通知只有在这个服务被终止或从前台主动移除通知后才能被解除。
官方描述:A foreground service(前台服务) is a service that's considered to be(被用户所认可的) something the user is actively aware of and thus not a candidate for(而不是一个候选的,可以在内存不足时,被系统杀死的) the system to kill when low on memory. A foreground service must provide a notification for the status bar(前台服务必须提供一个显示通知), which is placed under the "Ongoing" heading(它是不可以忽略的), which means that the notification cannot be dismissed unless the service is either stopped or removed from the foreground.(意思是通知信息不能被忽略,除非服务停止或主动移除,否则将一直显示。)
2.为什么要使用前台服务
在一般情况下,Service几乎都是在后台运行,一直默默地做着辛苦的工作。但这种情况下,后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收。
那么,如果我们希望Service可以一直保持运行状态且不会在内存不足的情况下被回收时,可以选择将需要保持运行的Service设置为前台服务。
例:
App中的音乐播放服务应被设置在前台运行(前台服务)——在App后台运行时,便于用户明确知道它的当前操作、在状态栏中指明当前歌曲信息、提供对应操作。
3.如何创建一个前台服务
- 新建一个服务
public class MusicPlayerService extends Service {
private static final String TAG = MusicPlayerService.class.getSimpleName();
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind()");
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
- 构建通知消息
在Service的onStartCommand中添加如下代码构建Notification:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand()");
// 在API11之后构建Notification的方式
Notification.Builder builder = new Notification.Builder
(this.getApplicationContext()); //获取一个Notification构造器
Intent nfIntent = new Intent(this, MainActivity.class);
builder.setContentIntent(PendingIntent.
getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
.setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
R.mipmap.ic_large)) // 设置下拉列表中的图标(大图标)
.setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题
.setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
.setContentText("要显示的内容") // 设置上下文内容
.setWhen(System.currentTimeMillis()); // 设置该通知发生的时间
Notification notification = builder.build(); // 获取构建好的Notification
notification.defaults = Notification.DEFAULT_SOUND; //设置为默认的声音
return super.onStartCommand(intent, flags, startId);
}
其它Notification构建方式:
Notification(int icon, CharSequence tickerText, long when)
This constructor was deprecated in API level 11. Use Notification.Builder instead.
// 已过时--在API11之后就被淘汰了,之后需要调用Notification.Builder()来构建Notification.
Notification notification = new Notification(R.mipmap.ic_launcher,
"前台服务测试",System.currentTimeMillis());
getNotification()
This method was deprecated in API level 16. Use build() instead.
// 在API16之后,可以使用build()来进行Notification的构建 Notification
notification = new Notification.Builder(this.getApplicationContext())
.setContentText("这是一个前台服务")
.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(System.currentTimeMillis())
.build();
- 启动前台服务
在完成Notification通知消息的构建后,在Service的onStartCommand中可以使用startForeground方法来让Android服务运行在前台。
// 参数一:唯一的通知标识;参数二:通知消息。
startForeground(110, notification);// 开始前台服务
注:当使用的通知ID一致时,只会更新当前Notification。
- 停止前台服务
在Service的onDestory中使用stopForeground方法来停止正在运行的前台服务。
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
stopForeground(true);// 停止前台服务--参数:表示是否移除之前的通知
super.onDestroy();
}
4.自定义Notification布局并设置点击事件
- 自定义Notification布局
有时候我们需要个性化前台服务的通知内容,那么我么就需要通过自定义Notification布局的方式来达到想要的效果:
RemoteViews remoteViews = new RemoteViews(this.getPackageName(),
R.layout.notification_layout);// 获取remoteViews(参数一:包名;参数二:布局资源)
builder = new Notification.Builder(this.getApplicationContext())
.setContent(remoteViews);// 设置自定义的Notification内容
builder.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.ic_launcher);
Notification notification = builder.getNotification();// 获取构建好的通知--.build()最低要求在
// API16及以上版本上使用,低版本上可以使用.getNotification()。
Notificationnotification.defaults = Notification.DEFAULT_SOUND;//设置为默认的声音
startForeground(110, notification);// 开始前台服务
自定义效果.png
- 为自定义通知内容上的控件绑定点击事件
在通知(Notification)中为自定义布局上的控件绑定点击事件监听,我们需要通广播的形式来实现效果。 - 注册广播
private static final int REQUEST_CODE = 100;
private static final String ACTION_PLAY = "play";
Intent intentPlay = new Intent(ACTION_PLAY);// 指定操作意图--设置对应的行为ACTION
PendingIntent pIntentPlay = PendingIntent.getBroadcast(this.getApplicationContext(),
REQUEST_CODE, intentPlay, PendingIntent.FLAG_UPDATE_CURRENT);// 取的一个PendingIntent,
// 它会发送一个广播,如同Context.sendBroadcast.
- 绑定点击事件
remoteViews.setOnClickPendingIntent(R.id.iv_pause, pIntentPlay);// 绑定点击事件(参数一:
// 控件id;参数二:对应触发的PendingIntent)
- 注册广播监听器,监听对应广播
- 动态注册
- 在Service的onCreate中添加如下代码注册广播监听:
// 动态注册广播
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
playerReceiver = new PlayerReceiver();
IntentFilter mFilter = new IntentFilter();
mFilter.addAction(ACTION_PLAY);
mFilter.addAction(ACTION_PAUSE);
mFilter.addAction(ACTION_LAST);
mFilter.addAction(ACTION_NEXT);
registerReceiver(playerReceiver, mFilter);
}
- 在Service销毁时(OnDestory中)解除广播注册:
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
stopForeground(true);// 停止前台服务
if (playerReceiver != null)
unregisterReceiver(playerReceiver);// 解除广播注册
super.onDestroy();
}
- 静态注册
在AndroidManifest.xml的receiver标签内添加需要过滤的内容,如:
<receiver
android:name=".PlayerReceiver"
android:exported="true">
<intent-filter>
<action android:name="play"/>
<action android:name="pause"/>
<action android:name="last"/>
<action android:name="next"/>
</intent-filter>
</receiver>
- BroadcastReceiver代码如下:
public class PlayerReceiver extends BroadcastReceiver {
private static final String TAG = PlayerReceiver.class.getSimpleName();
public PlayerReceiver() {
}
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
String action = intent.getAction();// 获取对应Action
Log.d(TAG,"action:"+action);
if(action.equals(MusicPlayerService.ACTION_PLAY)){
// 进行对应操作
} else if(action.equals(MusicPlayerService.ACTION_PAUSE)){
} else if(action.equals(MusicPlayerService.ACTION_LAST)){
} else if(action.equals(MusicPlayerService.ACTION_NEXT)){
}
}
}
点击后监听到的动作.png
1.建立对应的Intent(意图)--即指定对应操作的行为Action;
2.PendingIntent来处理意图,(Pending译为“待定的”、“延迟”,PendingIntent类提供了一种创建可由其它应用程序在稍晚时间触发的Intent的机制--推荐阅读
解析Android延迟消息(PendingIntent)处理
;
3.通过RemoteViews.setOnClickPendingIntent(int viewId,PendingIntent pendingIntent)方法来为指定的控件绑定对应的意图;
4.在Service中注册广播,监听对应操作。
5.修改自定义通知(Notification)上的显示内容
在自定义通知布局后,我们在有些场景下需要修改一些控件的显示内容(如修改TextView
显示文字、ImageView
图片、ProgressBar
进度等),那么我们可以通过如Notification.contentView
的setTextViewText、setImageViewBitmap、setProgressBar
等方法打成效果,示例代码如下:
notification.contentView.setTextViewText(R.id.title_tv, "标题");
注:方法的差异不大,都需要传递控件的id,需要设置的内容、属性等参数。
6.前台服务与普通服务的区别
- 前台Service的系统优先级更高、不易被回收;
- 前台Service会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。
7.Service系列拓展阅读
【Android】Service那点事儿
【Android】远程服务(Remote Service)的使用
用到的图片资源(看不清没关系,右击保存即可...):