Service使用文档
1. 服务的创建与启动
可以通过以下两种方式创建服务
1) startService(service);
如果服务不存在,那么首先创建服务,调用服务的onCreate()方法;如果服务存在,那么onCreate不会触发。
startService可以多次调用,但是一旦调用stopService,如果此时此服务没被其他组件绑定的话,服务将会销毁。
// Activity
public void do_startService(View view) {
Log.d(TAG, "do_startService");
Intent service = new Intent(this, MyService.class);
startService(service);
}
public void do_stopService(View view) {
Log.d(TAG, "do_stopService");
Intent service = new Intent(this, MyService.class);
stopService(service);
}
// Service
@Override
public void onCreate() {
Log.d(TAG, "onCreate " + this);
super.onCreate();
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy " + this);
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand startId ->" + startId + " " + this);
return START_STICKY;
}
触发了2次do_startService后,再触发一次do_stopService:
05-15 10:47:45.398 16504-16504/? D/Seven: do_startService
05-15 10:47:45.408 16504-16504/? D/Seven: onCreate com.seven.www.testserviceserver.MyService@4619f57
05-15 10:47:45.409 16504-16504/? D/Seven: onStartCommand startId ->1 com.seven.www.testserviceserver.MyService@4619f57
05-15 10:48:01.068 16504-16504/? D/Seven: do_startService
05-15 10:48:01.071 16504-16504/? D/Seven: onStartCommand startId ->2 com.seven.www.testserviceserver.MyService@4619f57
05-15 10:48:09.141 16504-16504/? D/Seven: do_stopService
05-15 10:48:09.144 16504-16504/? D/Seven: onDestroy com.seven.www.testserviceserver.MyService@4619f57
2) bindService(service, scn, Context.BIND_AUTO_CREATE);
第一个参数没什么好说的,第二个参数是一个ServiceConnection,如果使用同一个ServiceConnection对象进行多次绑定,那么只有一次绑定是有效的,
后续的绑定都不会触发它的onServiceConnected;第三个参数是一个标识位,如果使用Context.BIND_AUTO_CREATE,那么服务不存在时会自动创建,
如果使用0,那么这个绑定必须等到服务创建时才会触发绑定。注意:如果对已经解绑的ServiceConnection再次进行解绑,那么程序崩溃。
// Service
ServiceConnection scn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected -> " + name + " " + service);
messenger = new Messenger(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected -> " + name);
}
};
public void do_bindService(View view) {
Log.d(TAG, "do_bindService");
Intent service = new Intent(this, MyService.class);
Log.d(TAG, "bindService result -> " + bindService(service, scn, 0));
}
public void do_unbindService(View view) {
Log.d(TAG, "do_unbindService");
unbindService(scn);
}
// Activity
public void do_startService(View view) {
Log.d(TAG, "do_startService");
Intent service = new Intent(this, MyService.class);
startService(service);
}
调试标识位是0的情况,先触发do_bindService,再触发do_startService:
05-15 11:05:31.501 16847-16847/? D/Seven: do_bindService
05-15 11:05:31.505 16847-16847/? D/Seven: bindService result -> true
05-15 11:06:19.909 16847-16847/? D/Seven: do_startService
05-15 11:06:19.919 16847-16847/? D/Seven: onCreate com.seven.www.testserviceserver.MyService@6d2167b
05-15 11:06:19.920 16847-16847/? D/Seven: onBind com.seven.www.testserviceserver.MyService@6d2167b
05-15 11:06:19.924 16847-16847/? D/Seven: onStartCommand startId ->1 com.seven.www.testserviceserver.MyService@6d2167b
05-15 11:06:19.924 16847-16847/? D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.Handler$MessengerImpl@1634cf1
可以看出,在绑定服务时,并不会自动创建服务,但是绑定结果是成功的;后续通过startService去创建服务,在服务创建后立刻触发了绑定onBind。
触发3次do_bindService,再触发do_startService:
05-15 11:12:41.681 17040-17040/? D/Seven: do_bindService
05-15 11:12:41.685 17040-17040/? D/Seven: bindService result -> true
05-15 11:12:42.801 17040-17040/? D/Seven: do_bindService
05-15 11:12:42.803 17040-17040/? D/Seven: bindService result -> true
05-15 11:12:44.598 17040-17040/? D/Seven: do_bindService
05-15 11:12:44.600 17040-17040/? D/Seven: bindService result -> true
05-15 11:12:47.099 17040-17040/? D/Seven: do_startService
05-15 11:12:47.116 17040-17040/? D/Seven: onCreate com.seven.www.testserviceserver.MyService@1634cf1
05-15 11:12:47.118 17040-17040/? D/Seven: onBind com.seven.www.testserviceserver.MyService@1634cf1
05-15 11:12:47.121 17040-17040/? D/Seven: onStartCommand startId ->1 com.seven.www.testserviceserver.MyService@1634cf1
05-15 11:12:47.126 17040-17040/? D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.Handler$MessengerImpl@4619f57
可以看出,对于同一个ServiceConnection对象,进行多次绑定,只有第一次是有效的。
调试标识位是Context.BIND_AUTO_CREATE的情况
public void do_bindService(View view) {
Log.d(TAG, "do_bindService");
Intent service = new Intent(this, MyService.class);
Log.d(TAG, "bindService result -> " + bindService(service, scn, Context.BIND_AUTO_CREATE));
}
先触发do_bindService,再触发do_startService:
05-15 11:18:28.146 17288-17288/? D/Seven: do_bindService
05-15 11:18:28.151 17288-17288/? D/Seven: bindService result -> true
05-15 11:18:28.156 17288-17288/? D/Seven: onCreate com.seven.www.testserviceserver.MyService@678a44
05-15 11:18:28.157 17288-17288/? D/Seven: onBind com.seven.www.testserviceserver.MyService@678a44
05-15 11:18:28.160 17288-17288/? D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.Handler$MessengerImpl@e88b462
05-15 11:18:33.199 17288-17288/? D/Seven: do_startService
05-15 11:18:33.206 17288-17288/? D/Seven: onStartCommand startId ->1 com.seven.www.testserviceserver.MyService@678a44
可以看出,服务会在绑定时自动创建,并且,在startService时不会再次创建。
如果每次都使用一个全新的ServiceConnection来绑定服务:
// Activity
ArrayList scns = new ArrayList<>();
public void do_bindService(View view) {
Log.d(TAG, "do_bindService");
Intent service = new Intent(this, MyService.class);
ServiceConnection scn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected -> " + name + " " + service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected -> " + name);
}
};
if (bindService(service, scn, Context.BIND_AUTO_CREATE)) {
scns.add(scn);
};
}
public void do_unbindService(View view) {
Log.d(TAG, "do_unbindService");
if (scns.size() > 0) {
unbindService(scns.remove(scns.size() - 1));
}
}
先触发2次do_bindService,再触发2次do_unbindService:
04-19 16:16:05.950 3897-3897/com.seven.www.testserviceserver D/Seven: do_bindService
04-19 16:16:05.971 3897-3897/com.seven.www.testserviceserver D/Seven: onCreate com.seven.www.testserviceserver.MyService@8818907
04-19 16:16:05.972 3897-3897/com.seven.www.testserviceserver D/Seven: onBind com.seven.www.testserviceserver.MyService@8818907
04-19 16:16:05.975 3897-3897/com.seven.www.testserviceserver D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.Handler$MessengerImpl@951aa34
04-19 16:16:11.352 3897-3897/com.seven.www.testserviceserver D/Seven: do_bindService
04-19 16:16:11.367 3897-3897/com.seven.www.testserviceserver D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.Handler$MessengerImpl@951aa34
04-19 16:16:52.414 3897-3897/com.seven.www.testserviceserver D/Seven: do_unbindService
04-19 16:16:58.829 3897-3897/com.seven.www.testserviceserver D/Seven: do_unbindService
04-19 16:16:58.843 3897-3897/com.seven.www.testserviceserver D/Seven: onUnbind com.seven.www.testserviceserver.MyService@8818907
04-19 16:16:58.843 3897-3897/com.seven.www.testserviceserver D/Seven: onDestroy com.seven.www.testserviceserver.MyService@8818907
可以看出:
- 对于同一个客户端多次绑定只会触发一次onBind,这里所谓的客户端是指一个进程。
- 对于每一个ServiceConnection,在绑定成功后都会触发onServiceConnected。
- 有多少次bindService,就必须对应多少次unbindService,当所有链接都解绑后,onUnbind才会触发。
有一个更特殊的情况,就是当一个服务的onUnbind被调用后,如果服务没有销毁,那么我们再次绑定服务,onBind不会被触发。
先do_startService,do_bindService和do_unbindService;再do_bindService和do_unbindService
// onUnbind
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind " + this);
return super.onUnbind(intent);
}
// Log
04-24 14:10:25.715 12447-12447/? D/MyService: do_startService
04-24 14:10:25.755 805-1945/? I/ActivityManager: Start proc 12559:com.seven.www.testserviceserver:MyService/u0a107 for service com.seven.www.testserviceserver/.MyService
04-24 14:10:25.931 12559-12559/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:10:25.933 12559-12559/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:10:29.941 12447-12447/? D/MyService: do_bindService
04-24 14:10:29.946 12559-12559/? D/MyService: onBind com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:10:29.962 12447-12447/? D/MyService: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@c78f414
04-24 14:10:31.039 12447-12447/? D/MyService: do_unbindService
04-24 14:10:31.043 12559-12559/? D/MyService: onUnbind com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:10:33.489 12447-12447/? D/MyService: do_bindService
04-24 14:10:33.504 12447-12447/? D/MyService: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@c78f414
04-24 14:10:35.841 12447-12447/? D/MyService: do_unbindService
可以看出,如果服务触发了onUnbind,那么再也不会触发onBind了(在下次绑定时),但是如果我们想要监听这种场景也是可以的,将onUnbind的返回值修改成true,
那么下一次绑定时,onRebind就会触发,再次解绑时onUnbind依然会被调用。每次onUnbind的返回值,只能决定下一次绑定时onRebind是否会被调用。比如第一次onUbind
时返回了true,但是下一次onUbind时返回了false,那么第三次(及以后)绑定都不会触发onRebind了。注意:触发onBind/onRebind,才会触发onUnbind。
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind " + this);
return true;
}
先do_startService,do_bindService和do_unbindService;再do_bindService和do_unbindService
04-24 14:26:43.146 13654-13654/? D/MyService: do_startService
04-24 14:26:43.166 805-6467/? I/ActivityManager: Start proc 13708:com.seven.www.testserviceserver:MyService/u0a107 for service com.seven.www.testserviceserver/.MyService
04-24 14:26:43.323 13708-13708/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:26:43.326 13708-13708/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:26:44.206 13654-13654/? D/MyService: do_bindService
04-24 14:26:44.211 13708-13708/? D/MyService: onBind com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:26:44.225 13654-13654/? D/MyService: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@c78f414
04-24 14:26:44.872 13654-13654/? D/MyService: do_unbindService
04-24 14:26:44.876 13708-13708/? D/MyService: onUnbind com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:26:46.255 13654-13654/? D/MyService: do_bindService
04-24 14:26:46.259 13708-13708/? D/MyService: onRebind com.seven.www.testserviceserver.MyService@71ddf2e
04-24 14:26:46.272 13654-13654/? D/MyService: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@c78f414
04-24 14:26:46.905 13654-13654/? D/MyService: do_unbindService
04-24 14:26:46.909 13708-13708/? D/MyService: onUnbind com.seven.www.testserviceserver.MyService@71ddf2e
其实上面的测试只是在同一个APP的同一个进程中,并不能说明跨进程的情况。下面一节是关于跨进程绑定的情况。
2. 服务的跨进程访问
我们将MyService声明运行在其他进程:
先触发2次do_bindService,再触发2次do_unbindService:
// 客户端进程
04-19 17:23:54.395 6182-6182/com.seven.www.testserviceserver D/Seven: do_bindService
04-19 17:23:54.673 6182-6182/com.seven.www.testserviceserver D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@951aa34
04-19 17:23:55.277 6182-6182/com.seven.www.testserviceserver D/Seven: do_bindService
04-19 17:23:55.292 6182-6182/com.seven.www.testserviceserver D/Seven: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@951aa34
04-19 17:24:05.826 6182-6182/com.seven.www.testserviceserver D/Seven: do_unbindService
04-19 17:24:07.079 6182-6182/com.seven.www.testserviceserver D/Seven: do_unbindService
// 服务进程
04-19 17:23:54.668 6235-6235/com.seven.www.testserviceserver:MyService D/Seven: onCreate com.seven.www.testserviceserver.MyService@2ac2e7c
04-19 17:23:54.669 6235-6235/com.seven.www.testserviceserver:MyService D/Seven: onBind com.seven.www.testserviceserver.MyService@2ac2e7c
04-19 17:24:07.085 6235-6235/com.seven.www.testserviceserver:MyService D/Seven: onUnbind com.seven.www.testserviceserver.MyService@2ac2e7c
04-19 17:24:07.085 6235-6235/com.seven.www.testserviceserver:MyService D/Seven: onDestroy com.seven.www.testserviceserver.MyService@2ac2e7c
可以看出,结果和同进程绑定是一致的。结论已经在上面给出,这里就不重复了。
不同APP的跨进程服务绑定也是如此。
设置服务允许外部调用:
外部APP "com.seven.www.testservice" 代码:
private static final String ACTION_MYSERVICE = "com.seven.www.testserviceserver.myservice";
private ComponentName mServiceComponent = new ComponentName("com.seven.www.testserviceserver",
"com.seven.www.testserviceserver.MyService");
public void do_startService(View view) {
Log.d(TAG, "do_startService");
Intent service = new Intent(ACTION_MYSERVICE);
service.setComponent(mServiceComponent);
startService(service);
}
public void do_stopService(View view) {
Log.d(TAG, "do_stopService");
Intent service = new Intent(ACTION_MYSERVICE);
service.setComponent(mServiceComponent);
stopService(service);
}
ArrayList scns = new ArrayList<>();
public void do_bindService(View view) {
Log.d(TAG, "do_bindService");
Intent service = new Intent(ACTION_MYSERVICE);
service.setComponent(mServiceComponent);
ServiceConnection scn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "onServiceConnected -> " + name + " " + service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected -> " + name);
}
};
if (bindService(service, scn, Context.BIND_AUTO_CREATE)) {
scns.add(scn);
}
;
}
public void do_unbindService(View view) {
Log.d(TAG, "do_unbindService");
if (scns.size() > 0) {
unbindService(scns.remove(scns.size() - 1));
}
}
外部APP可以通过指定服务组件,进行相应的startService和bindService操作,这种情况和同一个APP多个进程是类似的。
如果服务不想被外部APP启动,那么设置
android:exported="false"
不过这是一棒子打死所有人的做法,有时候我们需要允许自家的其他APP绑定自己的服务,那么可以通过设置权限解决:
在com.seven.www.testserviceserver添加一个自定义权限,设置只有相同签名的其他APP才可以访问。
组件相应地加上权限
在com.seven.www.testservice申请权限:
3. 服务销毁与重建
下面说下正常情况下服务的销毁:
- 如果服务是通过startService启动的,期间没有bindService,那么无论执行了多少次startService,一旦触发stopService,那么服务就会销毁。注意:如果服务在
onStartCommand里面启用了线程,那么就算服务销毁了,线程依然会保持执行。 - 在 1 的情况下,在服务内部调用stopSelf()也相当于stopService的效果。
- 在 1 的情况下,在服务内部调用stopSelf(startId);也会销毁服务,但是这个startId必须是最新的startId.
还有一个带返回值的版本stopSelfResult(startId); 返回true表示可以成功销毁,返回false不可以。如果startId为最后一个,那么就会返回true。
其实1、2和3的区别是:1、2情况无论如何都会触发销毁;3情况只有在所有的start请求都被执行完成后才会销毁;假设用户同时调用了多次startService,那么只有
最后一次start请求才可以销毁服务。
onStartCommand的返回值:
START_STICKY_COMPATIBILITY
兼容到SDK=5的情况,目前可以不用考虑了。
START_STICKY
如果服务在onStartCommand执行完成后被杀掉了,那么系统稍后会重建这个服务;如果在被杀掉时有其他start请求没有执行,那么这些没被执行的start请求都会被继续传递给onStartCommand执行;如果被杀掉时没有start请求,那么传递给onStartCommand的intent将是一个null。
将服务的进程属性去掉,保证服务运行在主进程,打开com.seven.www.testserviceserver, 触发了一次do_startService,然后通过最近任务把APP杀掉后:
04-20 16:17:05.345 32180-32180/? D/MyService: do_startService
04-20 16:17:05.367 32180-32180/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@951aa34
04-20 16:17:05.371 32180-32180/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@951aa34
// 在这期间通过最近任务杀掉了APP
04-20 16:17:25.959 810-2637/? W/ActivityManager: Scheduling restart of crashed service com.seven.www.testserviceserver/.MyService in 1000ms
04-20 16:17:26.960 810-903/? W/ActivityManager: Unable to launch app com.seven.www.testserviceserver/10110 for service Intent { cmp=com.seven.www.testserviceserver/.MyService }: process is bad
可以看出,ActivityManager会在1000ms内重启服务,但是由于进程已经被杀掉,所以服务重启失败;也就说,服务想要自动重建,必须保证服务重建时,所在的进程是活着的。
否则重建失败。
将服务设置进程属性,其他保持和上面不变:
04-20 16:24:18.903 32417-32417/? D/MyService: do_startService
04-20 16:24:18.934 810-9326/? I/ActivityManager: Start proc 32472:com.seven.www.testserviceserver:MyService/u0a110 for service com.seven.www.testserviceserver/.MyService
04-20 16:24:19.241 32472-32472/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:24:19.243 32472-32472/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:24:32.873 810-9326/? I/ActivityManager: Killing 32472:com.seven.www.testserviceserver:MyService/u0a110 (adj 500): remove task
04-20 16:24:32.938 810-9326/? W/ActivityManager: Scheduling restart of crashed service com.seven.www.testserviceserver/.MyService in 1000ms
04-20 16:24:33.940 810-903/? W/ActivityManager: Unable to launch app com.seven.www.testserviceserver/10110 for service Intent { cmp=com.seven.www.testserviceserver/.MyService }: process is bad
可以看出,整个流程就多了创建进程和杀掉进程,其他都是一致的。
因为正常情况下,我们无法在1000ms内启动进程,那么就无法测试服务自动重建的情况,在这里我们可以利用系统的崩溃重新打开的提示进行测试,在onStartCommand里添加崩溃逻辑,保证服务运行在独立的进程中。
@Override
public int onStartCommand(Intent intent, int flags, final int startId) {
Log.d(TAG, "onStartCommand intent ->" + intent + " startId ->" + startId + " " + this);
if (startId == 1) {
throw new RuntimeException();
}
return START_STICKY;
}
触发do_bindService, 在触发do_startService,将会出现崩溃:
04-20 16:37:58.999 1363-1363/? D/MyService: do_bindService
04-20 16:37:59.021 810-5302/? I/ActivityManager: Start proc 1428:com.seven.www.testserviceserver:MyService/u0a110 for service com.seven.www.testserviceserver/.MyService
04-20 16:37:59.344 1428-1428/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:37:59.345 1428-1428/? D/MyService: onBind com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:37:59.348 1363-1363/? D/MyService: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@951aa34
04-20 16:38:46.372 1363-1363/? D/MyService: do_startService
04-20 16:38:46.377 1428-1428/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:38:46.388 1428-1428/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.seven.www.testserviceserver:MyService, PID: 1428
这时候系统会弹出一个重新打开应用的对话框,点击“重新打开应用”,那么服务所在的进程会自动重建,等待一定时间后服务也会自动重建并继续传递上面没有完成的intent进来:
04-20 16:44:01.741 810-2105/? I/ActivityManager: Killing 1710:com.seven.www.testserviceserver:MyService/u0a110 (adj 0): crash
04-20 16:44:01.742 810-2105/? W/ActivityManager: Scheduling restart of crashed service com.seven.www.testserviceserver/.MyService in 9986ms
04-20 16:44:01.810 1641-1641/? D/MyService: onServiceDisconnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService}
04-20 16:44:11.751 810-903/? I/ActivityManager: Start proc 1756:com.seven.www.testserviceserver:MyService/u0a110 for service com.seven.www.testserviceserver/.MyService
04-20 16:44:12.077 1756-1756/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:44:12.079 1756-1756/? D/MyService: onBind com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:44:12.081 1641-1641/? D/MyService: onServiceConnected -> ComponentInfo{com.seven.www.testserviceserver/com.seven.www.testserviceserver.MyService} android.os.BinderProxy@223aea3
04-20 16:44:12.082 1756-1756/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 16:44:12.089 1756-1756/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.seven.www.testserviceserver:MyService, PID: 1756
java.lang.RuntimeException: Unable to start service
可以看出,当点击“重新打开应用时”,onServiceDisconnected被触发,并且ActivityManager将在9986ms后重启服务。重启服务时,会伴随着进程的创建(这个和从最近任务杀掉应用是不一样的,最近任务杀掉无法自动重建进程)。然后重新绑定(这里可以暂时忽视),接着传递之前没执行完成intent和相同的startId,最后依然是奔溃。
注意:如果主动创建了等待重建服务的进程,那么该等待创建的服务会立刻重建。
START_NOT_STICKY
如果服务在onStartCommand执行完成后被杀掉了,系统也不会重建服务。
START_REDELIVER_INTENT
服务每次自动重建,都会传递已经执行过的所有Intent以及没执行的Intent。将服务onStartCommand代码修改如下:
@Override
public int onStartCommand(Intent intent, int flags, final int startId) {
Log.d(TAG, "onStartCommand intent ->" + intent + " startId ->" + startId + " " + this);
return START_REDELIVER_INTENT;
}
触发3次do_startService, 最近任务杀掉应用,接着重新打开应用,再触发一次do_startService:
04-20 17:16:45.572 2796-2796/? D/MyService: do_startService
04-20 17:16:45.578 2834-2834/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@2e9bf5a
04-20 17:16:45.581 2834-2834/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@2e9bf5a
04-20 17:16:46.208 2796-2796/? D/MyService: do_startService
04-20 17:16:46.214 2834-2834/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->2 com.seven.www.testserviceserver.MyService@2e9bf5a
04-20 17:16:47.891 2796-2796/? D/MyService: do_startService
04-20 17:16:47.895 2834-2834/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->3 com.seven.www.testserviceserver.MyService@2e9bf5a
04-20 17:16:54.750 810-5302/? I/ActivityManager: Killing 2834:com.seven.www.testserviceserver:MyService/u0a110 (adj 500): remove task
04-20 17:16:54.828 810-5302/? W/ActivityManager: Scheduling restart of crashed service com.seven.www.testserviceserver/.MyService in 18506ms
// 重新打开应用,触发do_startService
04-20 17:17:04.264 3610-3610/? D/MyService: do_startService
04-20 17:17:04.285 810-850/? I/ActivityManager: Start proc 3672:com.seven.www.testserviceserver:MyService/u0a110 for service com.seven.www.testserviceserver/.MyService
04-20 17:17:04.594 3672-3672/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 17:17:04.596 3672-3672/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 17:17:04.599 3672-3672/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->2 com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 17:17:04.600 3672-3672/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->3 com.seven.www.testserviceserver.MyService@2ac2e7c
04-20 17:17:04.603 3672-3672/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->5 com.seven.www.testserviceserver.MyService@2ac2e7c
服务运行在主进程中也是一样的:
04-20 17:19:14.726 3848-3848/? D/MyService: do_startService
04-20 17:19:14.747 3848-3848/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@951aa34
04-20 17:19:14.749 3848-3848/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@951aa34
04-20 17:19:16.080 3848-3848/? D/MyService: do_startService
04-20 17:19:16.096 3848-3848/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->2 com.seven.www.testserviceserver.MyService@951aa34
04-20 17:19:16.694 3848-3848/? D/MyService: do_startService
04-20 17:19:16.709 3848-3848/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->3 com.seven.www.testserviceserver.MyService@951aa34
04-20 17:19:19.842 810-2621/? W/ActivityManager: Scheduling restart of crashed service com.seven.www.testserviceserver/.MyService in 10218ms
// 重新打开应用
04-20 17:19:25.198 3947-3947/? D/MyService: onCreate com.seven.www.testserviceserver.MyService@2f59bb1
04-20 17:19:25.200 3947-3947/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->1 com.seven.www.testserviceserver.MyService@2f59bb1
04-20 17:19:25.202 3947-3947/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->2 com.seven.www.testserviceserver.MyService@2f59bb1
04-20 17:19:25.204 3947-3947/? D/MyService: onStartCommand intent ->Intent { cmp=com.seven.www.testserviceserver/.MyService } startId ->3 com.seven.www.testserviceserver.MyService@2f59bb1
总结:服务重建自动需要依赖进程存活,只有在进程意外退出(奔溃、内存不足回收)后,服务自动重建才可以伴随着创建对应的进程,用户手动结束进程的情况服务自动重建不能重建进程。
绑定服务重建
下面说的都是属于 服务运行在独立进程的情况。
从上面可以知道,所绑定的远程服务一旦死亡,那么onServiceDisconnected会触发,并且等到远程服务重建时,会自动重新绑上该服务。注意 ,服务被杀死后,有可能很快就会重启,而在服务重启时,如果进程没有处于活跃状态,那么服务重启就会失败。
也就是说,除非本地进程死亡/主动调用unbindService(conn), 否则每次远程进程启动都会重新绑定上。
不想自动重新绑定上可以这样做:
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "onServiceDisconnected -> " + name);
unbindService(this);
}
实现服务死亡主动重连:
在onServiceDisconnected 时主动 调用 unbindService(this)解绑,然后重新绑定。
使用Binder.linkToDeath实现同样的效果。 因为如果客户端绑定服务端后,给服务端传递了一个Binder,那么服务端就可以这样监听。
注意,无法通过这种办法启动一个处于关闭APP,但是如果服务所在的APP是处于打开状态,那么即使服务声明在独立子进程里面,其他APP也可以启动这个服务。