前言
想转行android开发,本想年前投投简历,看能不能拿到offer呢,一番面试下来。感觉android知识面上还是太肤浅,很多知识往往知道怎么用,人家面试人员在问细一点就把自己打蒙了。比如经常被问到的一个点:
1. service是什么?
2. service的启动方式有哪些,有什么区别?
这些在去面试之前做了些理论上的补课,真的是很多东西知道是这么回事,但是理论上说吧,又说不出来,不知道是不是自己嘴太笨的语言组织能力太差的缘故。比如第二个,我回答上来了service的启动方式和区别(下文展开讨论),人家听完又问了一个问题——onStartCommand()方法的返回值有哪些?分别代表什么意思?这下子把我问住了,我的天,我只知道service在主线程中在后台运行,对用户处于不可交互状态,用来执行某些需要长期运行的任务的时候用到service,但是使用过程中,压根没想到这个什么鬼的onStartCommand()的返回值问题,只晓得调用context.startService(intent)方法后,调用onCreate()然后调用onStartCommand()方法后service将处于运行状态。这个问题问的当场把我打蒙,乖乖说没注意过这个点,那叫一个尴尬。
面试回来后,痛定思痛,各方面查资料。最后花了两三天时间把官方文档看了一遍,不懂的网上查漏补缺,很多以前没注意到的地方豁然有所得,特写此文记录service的过程已记录心得。
本文将从以下几个点记录你自己探索service的过程,更多的是倾向于让自己站在求职者回答问题的角度上去求知:
- service是什么?
- service的启动方式以及区别的探索。
- onStartCommand()的返回值探索
- 前台服务探究
- service和线程的探索
- intentservice的探索。
- messenger的使用
- aidl的使用以及aidl和messenger的区别.
- BroactReceiver里为什么不能bind Service?
正文
# 1.什么是service?
- 老生常谈,service是Android四大组件之一,在后台长期运行并切不提供和用户进行交互的接口。悄磨叽的就把事情给干掉了,不求露脸,但求低调。比如网络传输,播放音乐,文件读写,或者和内容提供者进行交互等这些不需要用户进行交互的,都可以让其在后台运行也就是放在service里
- service虽然在后台运行,但是它还是运行在主线程中。这就意味着不能在service里面进行CPU负载消耗严重的工作,这样将大大加大主线程的压力和运行效率。在这种情况下,可以在这service里面创建子线程让着些资源消型的任务放在子线程中运行
2.service的启动方式以及区别的探索。
Service 有两种启动方式:
第一种:调用context.startService(Intent intent);
第二种:调用context.bindService(Intent service,ServiceConnection conn,int flags);
不同的开启服务方式,对应Serice不同的生命周期方法调用:
2.1 StartService
当其他组件第一次调用startServeice()的时候,首先系统会调用onCreate()的方法,然后在调用onStartCommand()方法。然后此时Serice出去运行状态。此时通过此方法(调用startService)开启的Service和其开启者没有任何关系了,其生命周期比调用者更久。开启Service的组件没法调用Service的方法。比如在某个Activity里开启了一个Serice,如果此时销毁该Activity,则该Service仍然在后台默默的运行着。只有显式调用stopService(),或者Service自己调用StopSelf()才能停止该Service;
对应这种方式下的Service生命周期如下:
简单使用案例:
service端代码:
public class LocalService extends Service {
private static final String TAG = "LocalService";
public LocalService() {
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate: 服务创建成功");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "onStartCommand: 服务开启咯");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: 服务器使命完成了");
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
client端代码:
简单的在布局文件中放了两个按钮,并直接绑定了两个点击事件处理方法
Intent serviceIntent;
public void start(View v) {
serviceIntent = new Intent(this, LocalService.class);
startService(serviceIntent);
}
public void stop(View v) {
stopService(serviceIntent);
}
第一次点击start按钮和stop按钮打印输出:
02-07 06:48:00.974 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onCreate: 服务创建成功
02-07 06:48:00.975 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:48:09.617 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onDestroy: 服务器使命完成了
再次点击start按钮并多次点击start按钮的log如下
02-07 06:49:55.351 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onCreate: 服务创建成功
02-07 06:49:55.351 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:49:57.127 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:49:58.442 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onStartCommand: 服务开启咯
02-07 06:50:00.520 6717-6717/com.example.yanchunguo.bindservice I/LocalService: onDestroy: 服务器使命完成了
实践出真知:
- 可以看出,Service在开启前,首先调用而且仅调用一次onCreate()方法。onStartCommand()方法每次点击start按钮都会调用,但无论onStartCommand()方法无论调用多少次,调用一次stopService()方法,就可以关闭当前Service.
- 通过调用startService(),调用者比如Activity,不能调用Service里自定义的方法。Service孤家寡人般的遗世独立。
2.2 BindService
服务开启的第二种方式是,其他组件比如Activity调用bindService()。后面简称bindservice.以这种方式开启服务,开启Service的组件能调用Service的方法,bindservice的生命周期和开启它的组件一样长。并且一个bindservice可以被多个组件绑定,但是要停止该bindservice。需要绑定它的组件都取消绑定(调用unbind)的时候,该Service才会停止。一切理论总要经得起实践的推敲,在学习过程中还是要多思考,多敲代码,下面是简单的使用验证代码
先贴上bindservice对应的生命周期图
bindservice 代码
package com.example.yanchunguo.bindservice;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class BindService extends Service {
private static final String TAG = "BindService";
public BindService() {
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate: 绑定服务创建");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: 开始绑定");
return new MyBinder();
}
public class MyBinder extends Binder{
/**
* 客户端用过返回的binder ,来调用此方法,从而获取当前服务实例,
* 用来调用Service的方法,比如下面的sayHello()
* @return返回该服务的的当前对象
*/
public BindService getService(){
return BindService.this;
}
}
public String sayHello(){
return "你好,这美好的世界";
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: 服务器销毁咯");
super.onDestroy();
}
}
客户端代码:
/**
* 点击按钮绑定服务
* @param v
*/
public void bind(View v) {
bindLocalService();
}
private void bindLocalService() {
Intent intent = new Intent(this, BindService.class);
bindService(intent, mSerConection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mSerConection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "bind成功: ");
BindService.MyBinder binder = (BindService.MyBinder) service;
BindService bs = binder.getService();
Log.i(TAG, "onServiceConnected: " +bs.sayHello());
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "取消bind");
}
};
public void unbind(View v) {
unbindService(mSerConection);
}
先看正常点击绑定按钮和取消绑定按钮,后台打印的log信息
02-07 07:49:03.603 28483-28483/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 07:49:03.603 28483-28483/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 07:49:03.608 28483-28483/com.example.yanchunguo.bindservice I/MainActivity: bind成功:
02-07 07:49:03.608 28483-28483/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
02-07 07:49:10.387 28483-28483/com.example.yanchunguo.bindservice I/BindService: onDestroy: 服务器销毁咯
实践出真知——log信息中得知:
- 和startService启动模式一样,首先调用的是onCreate方法。
- 同一个组件比如Acitivty,多次调用bindservice。Service的onBind方法就调用一次。当然其他组件,比如另外一个Activity调用bindService()方法的时候,会再次调用onBind方法。
现在再次点击绑定按钮,
02-07 08:17:08.410 9839-9839/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 08:17:08.411 9839-9839/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 08:17:08.435 9839-9839/com.example.yanchunguo.bindservice I/MainActivity: bind成功:
02-07 08:17:08.435 9839-9839/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
上面的是服务正常创建和启动,和上面一样,没什么问题 ,然后点击手机虚拟返回键,打印的log信息如下:
2-07 08:17:24.260 9839-9839/com.example.yanchunguo.bindservice E/ActivityThread: Activity com.example.yanchunguo.bindservice.MainActivity has leaked ServiceConnection com.example.yanchunguo.bindservice.MainActivity$1@e376196 that was originally bound here
02-07 08:17:24.279 9839-9839/com.example.yanchunguo.bindservice I/BindService: onDestroy: 服务器销毁咯
log分析:
首先,直接点击手机虚拟返回键(注意是直接点击返回,没有显式调用unbind进行解绑),当前开启Service的活动被销毁了,会打印一个错误log,提示有一个ServiceConnection泄露了(leaked),也就是Serivce没有解除绑定。
然后紧接着打印出了在BindService中的重写(override)的onDestroy()方法。
结论:验证了bindservice的生命周期依赖于其绑定的组件,组件销毁了,服务也销毁了
本篇总结
- 两者的实现类都需要是Service的子类。
- 都在后台运行。
生命周期不同
startservice的生命周期比较长,和启动它的组件如activity无关,启动组件销毁了,startservice也在后台运行,除了系统因为内存不够而销毁service,需要启动组显示调用stopService(),或者Service自己调用stopSelf()来停止Serivce
bindservice的生命周期比较短,依赖于启动它的组件。绑定它的组件销毁了,service随之销毁,也可以显式调用unbindService()停止服务和service交互上面
startService的启动者组件不能调用定义在Service的方法。
bindService的绑定组件能够调用定义在Service的方法
引申
既使用startService又实用bindService的情况。
写着写着突然想起来这个情况,于是乎还是先从实践开始,然后通过log信息得出相应的结论
Service代码如下:在上班bindservice的基础伤重写了onStartCommand方法;这样此时的服务既可以用startService方法开启,又可以用bindService绑定了,同时也重写了onRebind()方法和onUnbind()方法。
public class BindService extends Service {
private static final String TAG = "BindService";
public BindService() {
}
@Override
public void onCreate() {
Log.i(TAG, "onCreate: 绑定服务创建");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind: 开始绑定");
return new MyBinder();
}
public class MyBinder extends Binder{
/**
* 客户端用过返回的binder ,来调用此方法,从而获取当前服务实例,
* 用来调用Service的方法,比如下面的sayHello()
* @return返回该服务的的当前对象
*/
public BindService getService(){
return BindService.this;
}
}
@Override
public void onRebind(Intent intent) {
Log.i(TAG, "onRebind: 重新绑定");
super.onRebind(intent);
}
@Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "onUnbind: 解除绑定");
return true;
}
@Override
public int onStartCommand(Intent intent,int flags, int startId) {
Log.i(TAG, "onStartCommand: 客户端调用onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
public String sayHello(){
return "你好,这美好的世界";
}
@Override
public void onDestroy() {
Log.i(TAG, "onDestroy: 服务器销毁咯");
super.onDestroy();
}
}
对应的client代码:代码没什么改动,主要是把start按钮点击处理事件里面的启动服务由LocalService.class改成了BindService.class
/**
* 点击按钮绑定服务
* @param v
*/
public void bind(View v) {
bindLocalService();
}
private void bindLocalService() {
Intent intent = new Intent(this, BindService.class);
bindService(intent, mSerConection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mSerConection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "bind成功: ");
BindService.MyBinder binder = (BindService.MyBinder) service;
BindService bs = binder.getService();
Log.i(TAG, "onServiceConnected: " +bs.sayHello());
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i(TAG, "取消bind");
}
};
public void unbind(View v) {
unbindService(mSerConection);
}
public void open(View v) {
Intent in = new Intent(this, OtherActivity.class);
startActivity(in);
}
Intent serviceIntent;
public void start(View v) {
serviceIntent = new Intent(this, BindService.class);
startService(serviceIntent);
}
public void stop(View v) {
stopService(serviceIntent);
}
@Override
protected void onDestroy() {
Log.i(TAG, "onDestroy: 主活动销毁了");
super.onDestroy();
}
首先,先调用startSevice()。在调用bindservice()log信息如下
02-07 10:20:23.221 2458-2458/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 10:20:23.222 2458-2458/com.example.yanchunguo.bindservice I/BindService: onStartCommand: 客户端调用onStartCommand.
02-07 10:20:26.991 2458-2458/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 10:20:27.011 2458-2458/com.example.yanchunguo.bindservice I/MainActivity: bind成功:
02-07 10:20:27.011 2458-2458/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
根据log信息得知,先由startService开启服务后onCreate()先执行,然后onStartCommand()方法顺利执行,然后在调用bindServicede,发现直接调用的是onBind()方法。onCreate()方法不再执行。 反过来,刚开始先调用bindService()然后在调用startService()一样,onCreate()也只是执行一次,其它log信息和上面相反,先调用onBind(),在调用onStartCommand()。在此不再赘述。
重点来了:现在我们先调用unbindservice()看log
02-07 10:32:25.670 12704-12704/com.example.yanchunguo.bindservice I/BindService: onUnbind: 解除绑定
此时由log信息可以看出服务的onUnbind()方法调用了,但是和上面单独讲bindservice的log不同,此时并没有打印出“服务销毁”这句log.然后我们此时在调用stopService();看log
02-07 10:36:54.198 12704-12704/com.example.yanchunguo.bindservice I/BindService: onDestroy: 服务器销毁咯
此时服务才被销毁。 返回来实践也一样,先调用stopService(),服务并没有被销毁,需要在调用unbindService()之后服务器才销毁。具体可以操作实践,这个log就不贴了
回到最初的上面来,也就是调用了startService()和bindService()的基础上,在调用unbindservice(),此时不调用stopService()而是直接在调用bindService()。看你卡这次会打印什么
02-07 10:58:24.600 4320-4320/com.example.yanchunguo.bindservice I/BindService: onCreate: 绑定服务创建
02-07 10:58:24.600 4320-4320/com.example.yanchunguo.bindservice I/BindService: onStartCommand: 客户端调用onStartCommand
02-07 10:58:25.607 4320-4320/com.example.yanchunguo.bindservice I/BindService: onBind: 开始绑定
02-07 10:58:25.627 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: bind成功:
02-07 10:58:25.627 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
02-07 10:58:27.852 4320-4320/com.example.yanchunguo.bindservice I/BindService: onUnbind: 解除绑定
02-07 10:58:29.420 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: bind成功:
02-07 10:58:29.420 4320-4320/com.example.yanchunguo.bindservice I/MainActivity: onServiceConnected: 你好,这美好的世界
02-07 10:58:29.421 4320-4320/com.example.yanchunguo.bindservice I/BindService: onRebind: 重新绑定
最后一个发现调用了onRebind()进行了重新绑定;
对于此种去看了下官方文档,查到了这种解释:
如果你的Service即通过调用startService()又通过bindService()进行绑定。那么在调用onbindService()进行解绑的时候,如果在重写的onUnbind()方法里返回true,在服务没有被停止的情况下,再次调用bindService()方法,会调用onRebind()方法。 上面的log也印证了这一点,同时官方也给出了相应的流程图,也印证了这一点,如下图
引申总结
如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStartCommand方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行,该服务被不同组件绑定了多少次就要unbindService多少次,也及时解除所有的额绑定。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。