简介
一种可在后台执行长时间运行操作而不提供界面的应用组件。不会自己创建线程和进程(除非指定运行进程),只在启动该服务的进程中执行,通常情况下是主线程,因此降低应用正在运行的任何 Activity 的性能,所以如果需要在服务中执行耗时操作或者CPU密集型操作,就需要在服务内创建新线程来处理,从而降低发生ANR风险。
分类
服务类型不在于自身,主要依赖外部组件调用它的方式,其他组件通过bindService启动就是绑定服务,用startService启动就是后台服务,如果内部有常驻通知就是前台服务。
- 绑定服务
组件通过调用 bindService() 绑定一个服务,该服务就是绑定服务了,当有一个组件开始绑定时该服务创建,当所有组件解除绑定时服务自动销毁 - 后台服务
执行用户不会直接注意到的操作,组件通过startService() 启动,生命周期独立于启动他的组件。当执行任务完成后需要内部调用stopSelf()来自行销毁,或者其他组件调用stopService() - 前台服务
执行一些用户能注意到的操作,在通知栏有常驻通知
简单使用
- 创建服务
- 直接继承自基类Service:必须创建用于执行所有服务工作的新线程,因为服务默认使用应用的主线程,这会降低应用正在运行的任何 Activity 的性能
public class MusicService extends Service {
private static final String LOG_TAG = MusicService.class.getSimpleName();
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(() -> {
while (true){
try {
Thread.sleep(1000);
Log.d(LOG_TAG, "MusicService 正在运行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}
- 继承自IntentService:Service 的子类,其使用工作线程逐一处理所有启动请求。
- 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
- 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
- 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
- 提供 onBind() 的默认实现(返回 null)。
- 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
public class MusicService extends IntentService {
private static final String LOG_TAG = MusicService.class.getSimpleName();
public MusicService() {
super(LOG_TAG);
}
@Override
protected void onHandleIntent(Intent intent) {
while (true){
try {
Thread.sleep(1000);
Log.d(LOG_TAG, "MusicService 正在运行中");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行结果,在不同的线程
17:36:35.176 25660-25660/com.mh.smart D/MainActivity: MainActivity启动服务
17:36:51.232 25660-25751/com.mh.smart D/MusicService: MusicService 正在运行中
17:36:52.233 25660-25751/com.mh.smart D/MusicService: MusicService 正在运行中
17:36:53.239 25660-25751/com.mh.smart D/MusicService: MusicService 正在运行中
- 在AndroidManifest.xml中声明服务
<manifest ... >
...
<application ... >
<service android:name=".MusicService"
//防止其他应用启动本应用服务
android:exported=“false”/>
...
</application>
</manifest>
- 启动服务
不用使用隐式Intent,只用显示Intent,这样才能知道启动了哪些服务便于管理,并且在5.0以上用隐式Intent去绑定服务会抛异常
Intent intent = new Intent(this, MusicService.class);
startService(intent);
- 停止服务
在服务内部直接调用stopSelf()
在其他组件内调用stopService()
Intent intent = new Intent(this, MusicService.class);
stopService(intent)
前台服务
- 前台服务需要额外申请权限
<uses-permission android:name=“android.permission.FOREGROUND_SERVICE” />
- 在服务内部发送前台通知
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(LOG_TAG, "服务运行");
Intent notificationIntent = new Intent(BaseApplication.application, MusicActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(BaseApplication.application, 1, notificationIntent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(BaseApplication.application, NotifyUtils.NOTIFY_CHANNEL_MUSIC);
builder.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("通知标题")
.setContentText("这是一条通知的内容")
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent);
startForeground(1, builder.build());
return super.onStartCommand(intent, flags, startId);
}
- 删除通知
1.直接关闭服务
stopService()
2.服务内部调用
stopForeground(1);
绑定服务
上面开启的服务都是在后台默默工作,为应用提供服务,与组件没啥交互。绑定服务就有点像C/S中的服务器了,它作为服务器,当有组件绑定它时,组件就是客户端,多个组件绑定也就是多个客户端,绑定它的组件可以是其他应用的,所以通信方面就用Binder
使用绑定服务有三种方式:
扩展 Binder 类
适用于该服务只会被本应用的组件绑定,并且客户端和服务端所在进程相同
创建可被绑定服务,即创建服务器
public class MusicControlService extends Service {
private static final String LOG_TAG = MusicControlService.class.getSimpleName();
private IBinder binder = new MusicControlBinder();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
//返回一个Binder对象
return binder;
}
/**
* 该服务拥有的功能
*/
public void nextPlay(){
mAudioPlayerManager.playNext();
}
public void prePlay(){
mAudioPlayerManager.playPre();
}
public AudioPlayer getCurrentAudioPlayer(){
return mAudioPlayer;
}
//通常使用内部类继承Binder,将本类的实例提供给客户端,客户端用实例直接使用需要的功能
public class MusicControlBinder extends Binder{
public MusicControlService getService(){
return MusicControlService.this;
}
}
}
和与一般服务器通信一样需要连接,这边是使用ServiceConnection,在组件中先创建连接
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//这边的service就是服务中onBinder返回的IBinder,多个组件一起绑定的时候返回的是相同的IBinder
musicControlService = ((MusicControlService.MusicControlBinder) service).getService();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
开始连接
private void bindMusicService(){
Intent intent = new Intent(this, MusicControlService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
连接完成后就可以使用服务提供的功能了
musicControlService.getCurrentAudioPlayer();
musicControlService.nextPlay();
musicControlService.prePlay();
记得断开连接
private void unBindMusicService(){
unbindService(serviceConnection);
}
使用 Messenger
适用于客户端不在当前进程的场景,即其他应用或本应用其他进程绑定。它与经常用的handler发送message内部实现和使用方式基本类似,内部也是一个队列,一次处理一个message,不同的是它是简化版的AIDL,可以进行进程间的通信,比AIDL使用方便。
创建绑定服务
public class MusicControlService extends Service {
private static final String LOG_TAG = MusicControlService.class.getSimpleName();
public static final int MUSIC_CONTROL_NEXT = 1;
//需要一个handler,来处理客户端发来的消息
public static class MusicControlHandler extends Handler{
MusicControlHandler(){}
@Override
public void handleMessage(@NonNull Message msg) {
//处理消息
switch (msg.what){
case MUSIC_CONTROL_NEXT:
Log.d(LOG_TAG, "MusicControlService 收到消息 参数是 :" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
}
@Override
public IBinder onBind(Intent intent) {
//利用Messenger生成IBinder供客户端使用
return new Messenger(new MusicControlHandler()).getBinder();
}
}
客户端创建连接
private Messenger musicControlService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//用传来的Ibinder生成Messenger,后面利用Messenger来发送消息,Messenger中的队列一个一个来处理消息
musicControlService = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
musicControlService = null;
}
};
开始连接(开始绑定)没有变
private void bindMusicService(){
Intent intent = new Intent(this, MusicControlService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
使用服务端的功能方式变了,应使用连接时拿到的Messenger来发送一个message
public void playNext(){
try {
Message message = new Message();
message.what = MusicControlService.MUSIC_CONTROL_NEXT;
message.obj = "上层参数";
musicControlService.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
断开连接也没变
private void unBindMusicService(){
unbindService(serviceConnection);
}
注:
- 当第一个客户端绑定服务时,onBind方法会被执行,这时已经产生了IBinder,后续客户端绑定不会执行onBind会延用第一个产生的IBinder,除非该服务没有一个客户端绑定,才会执行onBind
- 当没有客户端与服务绑定时,服务会自动销毁,除非还通过startService启动了该服务
- 调用stopService时,如果有组件绑定了服务,服务也不会关闭
- 调用bindService后,服务内部调用的是onCreate,onBind。调用unBindService后,服务内部调用的是onUnbind和onDestroy
- 多次startService,服务的onCreate只会被调用一次
使用 AIDL
和Messanger不同的是可以一次处理多个,这时需要自己处理这个多线程,安全难度大,谷歌不建议用,但是如果是多进程通信并且需要内部多线程处理时还是需要用到AIDL。