概述
1 概要
Service是一种计算型组件,主要用于在后台执行一系列计算任务,不直接与用户交互,执行任务的结果可以和外界进行通信。
由于Service组件工作在后台,因此用户无法直接感知到它的存在。Activity组件只有一种运行模式-启动状态,但是Service组件却有两种状态:启动状态和绑定状态。①通过startService启动的服务处于启动状态,它的内部可以执行一些后台计算,并且不需要和外界有直接的交互。②通过bindService启动的服务处于绑定状态,Service内部同样也可以执行后台计算,但是处于这种状态的Service可以通过ServiceConnection和外界进行通信。
1.1 Service的生命周期
1.2 什么时候使用Service
(1)播放多媒体的时候,用户启动了其他的Activity这个时候程序要在后台继续播放。
(2)后台service下载插件、更新包。
(3)检测SD卡上文件的变化。
(4)在后台记录你地理位置的改变等等。
1.3 Service分为本地服务(LocalService)和远程服务(RemoteService)
(1)默认情况下是在同一个主线程中。但可以通过配置android:process=”:remote” 属性让Service运行在不同的进程。
(2)本地服务依附在主进程上而不是独立的进程,不需要IPC,也不需要AIDL,主进程被Kill后,服务便会终止。
(3)远程服务为独立的进程,进程名格式为:包名 + android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响。
2 startService和bindService的区别
2.1 通过start方式开启服务
2.1.1 startService的步骤
1、定义一个类继承service
2、manifest.xml文件中配置service
3、使用context的startService(Intent)方法启动service
4、不使用时,调用stopService(Intent)方法停止服务
2.1.2 主要方法
// 开启服务
Intent service = new Intent(this, MyService.class);
startService(service);
// 结束服务
stopService(service);
2.1.3 生命周期
(1)完整生命周期
onCreate() -- > onStartCommand() -- > onDestory()
(2)调用一次startService,生命周期执行的方法依次是
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
(3)调用多次startService,onCreate只有第一次会被执行,而onStartCommand会执行多次
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: onStartCommand---startId = 2 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: onStartCommand---startId = 3 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
(4)结束服务时,调用stopService,执行onDestroy方法,并且多次调用stopService时,onDestroy只有第一次会被执行
TestService: onDestroy---
2.1.4 与activity之间的关系
(1)开启服务以后,与activity就没有关联,不受影响,独立运行。
2.2 采用bind的方式开启服务
2.2.1 bindService的步骤
1、定义一个类继承Service
2、在manifest.xml文件中注册service
3、使用context的bindService(Intent,ServiceConnection,int)方法启动service
4、不使用时,调用unbindService(ServiceConnection)方法停止该服务
2.2.2 主要方法
// 绑定服务
Intent bindIntent = new Intent(this, TestService.class);
// 参数1:Intent意图
// 参数2:是一个接口,通过这个接口接收服务开启或者停止的消息,并且这个参数不能为null
// 参数3:开启服务时的操作,BIND_AUTO_CREATE代表自动创建service
bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 只有当TestService的onBind方法返回值不为null时,才会被调用
LogUtils.d(TAG, "onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 只有出现异常时才会调用,服务正常退出不会调用。
LogUtils.e(TAG, "onServiceDisconnected");
}
};
// 结束服务
unbindService(conn);
2.2.3 生命周期
bindService开启服务时,根据生命周期里onBind方法的返回值是否为空,有两种情况
@Override
public IBinder onBind(Intent intent) {
LogUtils.d(TAG, "onBind---");
// 返回mBinder对象或null
return mBinder;
}
2.2.3.1 onBind返回值是null
(1)完整生命周期
onCreate() -- > onBind() -- > onDestory()
(2)调用一次bindService,生命周期执行的方法依次是
TestService: onCreate---
TestService: onBind---
(3)调用多次bindService,onCreate和onBind也只在第一次会被执行
TestService: onCreate---
TestService: onBind---
(4)调用unbindService结束服务,执行onDestroy方法,并且unbindService方法只能调用一次,多次调用应用会抛出异常
TestService: onDestroy---
(5)注意:调用unbindService一定要确保服务已经开启,以及避免多次调用unbindService,否则应用会抛出异常,如下:
2.2.3.2 onBind返回值不为null
(1)完整生命周期
onCreate() -- > onBind() --> onServiceConnected() -- > onDestory()
(2)调用一次bindService,onServiceConnected方法也被调用了,生命周期执行的方法依次是
TestService: onCreate---
TestService: onBind---
TestService + TestActivity: onServiceConnected
(3)调用多次bindService,onCreate和onBind也只在第一次会被执行
TestService: onCreate---
TestService: onBind---
TestService + TestActivity: onServiceConnected
(4)调用unbindService结束服务,执行onDestroy方法,并且unbindService方法只能调用一次,多次调用应用会抛出异常
TestService: onDestroy---
2.2.4 与activity之间的关系
(1)bindService开启服务以后,与activity存在关联,退出activity时必须调用unbindService方法,否则会报ServiceConnection泄漏的错误。
2.3 同时采用start和bind的方式开启服务
2.3.1 采用start和bind的方式开启服务,没有先后顺序的要求,TestService的onCreate只会执行一次。
(1)先start再bind
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: onBind---
TestService + TestActivity: onServiceConnected
// 重复bind,没有回调;重复start,回调如下
TestService: onStartCommand---startId = 2 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
(2)先bind再start
TestService: onCreate---
TestService: onBind---
TestService + TestActivity: onServiceConnected
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
// 重复bind,没有回调;重复start,回调如下
TestService: onStartCommand---startId = 2 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
2.3.2 关闭服务需要stopService和unbindService都被调用,也没有先后顺序的影响,TestService的onDestroy也只执行一次
(1)如果只用一种方式关闭服务,不论是哪种关闭方式,onDestroy都不会被执行,服务也不会被关闭
(2)同时stopService和unbindService后执行,多次调用unbindService应用会抛出异常,如同2.2
TestService: onDestroy---
3 startService方式开启服务: onStartCommand诡异的返回值
3.1 源码分析
(1)源码实现方式
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
(2)默认实现方式
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
(3)默认实现方式返回的结果是START_STICKY
// 应用程序targetSdkVersion小于2.0就返回 START_STICKY_COMPATIBILITY,否则返回START_STICKY
mStartCompatibility = getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.ECLAIR; // 5 ,Android 2.0
(4)自定义的实现方式有4种
START_STICKY
START_NOT_STICKY
START_REDELIVER_INTENT
START_STICKY_COMPATIBILITY
3.2 TestService–>Runnable–>run制造异常,模拟系统杀死该进程
(1)START_STICKY(粘性的):服务被异常kill掉,当内存又存在的时候,service又被重新创建
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: 2s after---
// 异常:com.seniorlibs.lifecycle E/AndroidRuntime: FATAL EXCEPTION: main
TestService: onCreate---
TestService: onStartCommand---startId = 2 and intent = null
TestService: 2s after---
解释:如果Service所在的进程,在执行了onStartCommand方法后,被清理了,那么这个Service会被保留在已开始的状态,但是不保留传入的Intent,随后系统会尝试重新创建此Service,由于服务状态保留在已开始状态,所以重启服务后一定会调用onCreate和onStartCommand方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null,startId=2。
(2)START_NOT_STICKY(非粘性的):服务被异常kill掉,系统不会自动重启该服务
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: 2s after---
// 异常:com.seniorlibs.lifecycle E/AndroidRuntime: FATAL EXCEPTION: main
解析:如果Service所在的进程,在执行了onStartCommand方法后,被清理了,则系统不会重新启动此Service。
(3)START_REDELIVER_INTENT(重新提交intent):服务被异常kill掉,当内存又存在的时候,系统会自动重启该服务,并将Intent的值传入
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: 2s after---
// 异常:com.seniorlibs.lifecycle E/AndroidRuntime: FATAL EXCEPTION: main
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: 2s after---
解析:如果Service所在的进程,在执行了onStartCommand方法后,被清理了,则结果和START_STICKY一样,也会重新创建此Service并调用onCreate和onStartCommand方法。接着系统会自动重启该服务,将Intent的值传入,并且startId=1。说明该int可以保留上次的startId与intent。
(4)START_STICKY_COMPATIBILITY(STICKY的兼容版本):不保证服务被kill后一定能重启
TestService: onCreate---
TestService: onStartCommand---startId = 1 and intent = Intent { cmp=com.seniorlibs.lifecycle/.service.TestService }
TestService: 2s after---
// 异常:com.seniorlibs.lifecycle E/AndroidRuntime: FATAL EXCEPTION: main
TestService: onCreate---
TestService: 2s after---
解析:如果Service所在的进程,在执行了onStartCommand方法后,被清理了,只调用onCreate方法,没有调用onStartCommand方法。
5 问题解答
5.1 Service与Activity怎么实现通信?
5.1.1 通过Binder实现通信(进程内通信)
(0)概要
(1)Activity调用bindService(Intent service, ServiceConnection conn, int flags)方法,绑定一个Service的继承类TestService;
(2)通过实例化ServiceConnection接口的onServiceConnected()方法,获取TestService中的Binder对象;
(3)通过此对象调用Binder继承类TestBinder中的方法将消息传给Service;
(4)如果想实现Service将消息传给Activity的,可以在TestBinder里面提供一个回调方法,在Activity中实现方法。
(1)在manifest.xml文件中注册service
<service android:name=".service.TestService" />
(2)定义一个类继承Service
public class TestService extends Service {
public static final String TAG = "TestService";
private Handler sWork = new Handler();
private TestBinder mBinder = new TestBinder();
private OnTestListener mListener;
@Override
public void onCreate() {
super.onCreate();
LogUtils.d(TAG, "onCreate---");
// 制造异常,模拟系统杀死该进程
// sWork.postDelayed(new Runnable() {
// @Override
// public void run() {
// LogUtils.d(TAG, 2000 / 1000 + "s after---");
// // 故意制造异常,使该进程挂掉
// Integer.parseInt("ok");
// }
// }, 5000);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtils.d(TAG, "onStartCommand---startId = " + startId + " and intent = " + intent);
// 实验中,可轮换这几个值测试 :START_NOT_STICKY | START_STICKY | START_STICKY_COMPATIBILITY | START_REDELIVER_INTENT;
// return super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
LogUtils.d(TAG, "onBind---");
// 返回mBinder对象
return mBinder;
}
@Override
public void onDestroy() {
LogUtils.d(TAG, "onDestroy---");
super.onDestroy();
}
/**
* 新建一个继承自Binder的类TestBinder
*/
public class TestBinder extends Binder {
// Activity通过Binder来调用TestBinder的方法将消息传给Service
public void sendMsgToService(String msg) {
LogUtils.d(TAG, "收到来自Activity的消息: " + msg);
// 并回调mListener.onTest告诉Activity已收到消息
if (mListener != null) {
mListener.onTest("hi, activity");
}
}
// TestBinder里面提供一个注册回调的方法
public void setOnTestListener(OnTestListener listener) {
mListener = listener;
}
}
/**
* 自定义一个回调接口
*/
public interface OnTestListener {
void onTest(String str);
}
}
(3)使用context的bindService(Intent,ServiceConnection,int)方法启动service
public class TestActivity extends Activity implements View.OnClickListener {
public static final String TAG = TestService.TAG + " + TestActivity";
private Intent mStartIntent;
private TestService.TestBinder mBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
initView();
}
private void initView() {
findViewById(R.id.btn_start_service).setOnClickListener(this);
findViewById(R.id.btn_bind_service).setOnClickListener(this);
findViewById(R.id.btn_stop_service).setOnClickListener(this);
findViewById(R.id.btn_unbind_service).setOnClickListener(this);
findViewById(R.id.btn_send_msg_to_service).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_start_service:
// 开启服务
mStartIntent = new Intent(this, TestService.class);
startService(mStartIntent);
break;
case R.id.btn_bind_service:
// 绑定服务
Intent bindIntent = new Intent(this, TestService.class);
// 参数1:Intent意图
// 参数2:是一个接口,通过这个接口接收服务开启或者停止的消息,并且这个参数不能为null
// 参数3:开启服务时的操作,BIND_AUTO_CREATE代表自动创建service
bindService(bindIntent, mConnection, BIND_AUTO_CREATE);
break;
case R.id.btn_stop_service:
// 结束服务
stopService(mStartIntent);
break;
case R.id.btn_unbind_service:
// 结束连接服务
unbindService(mConnection);
break;
case R.id.btn_send_msg_to_service:
// 发送消息给Service
if (mBinder != null) {
mBinder.sendMsgToService("hi, service");
} else {
LogUtils.e(TAG, "请先绑定服务,再执行发送消息给Service");
}
break;
default:
break;
}
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 只有当TestService的onBind方法返回值不为null时,才会被调用
LogUtils.d(TAG, "onServiceConnected");
if (service != null) {
// 取得Service里的binder对象
mBinder = (TestService.TestBinder) service;
// 注册自定义回调
mBinder.setOnTestListener(new TestService.OnTestListener() {
@Override
public void onTest(String str) {
LogUtils.d(TAG, "收到来自Service的消息: "+str);
}
});
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 只有出现异常时才会调用,服务正常退出不会调用。
LogUtils.e(TAG, "onServiceDisconnected");
}
};
@Override
protected void onDestroy() {
super.onDestroy();
// 结束连接服务
unbindService(mConnection);
}
}
(4)不使用时,调用unbindService(ServiceConnection)方法停止该服务
@Override
protected void onDestroy() {
super.onDestroy();
// 结束连接服务
unbindService(mConnection);
}
(5)结果打印
TestService: 收到来自Activity的消息: hi, service
TestService + TestActivity: 收到来自Service的消息: hi, activity
5.1.2 通过Broadcast实现通信
(1)概要:Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好。
(2)参考链接:Android Service与Activity之间通信的几种方式
5.2 Service重要的方法运行在哪个线程?
(1)Service的onCreate、onStartCommand、onDestory等全部生命周期方法都运行在UI线程,
(2)ServiceConnection里面的回调方法也是运行在UI线程;
(3)bindService后,onServiceConnected调用时机是在onCreate和onBind之后被调用 ;
(4)千万不能在Service的生命周期方法中做非常耗时的操作,否则会引起主线程卡顿,严重时还会引起ANR;
(5)参考链接:【源码解析】Service几个重要的方法运行在哪个线程
5.3 Service里面可以弹出dialog或Toast吗?
(1)Toast通常使用Activity和Application的context,也可以使用Service、ContentProvider和BroadcastReceiver的context。但是在IntentService的onHandleIntent()不能使用,因为其在子线程中。
(2)Dialog只能在Activity中使用,其他组件使用会抛出异常。也有方法可以实现,如下(加个权限,在manifest中添加此权限以弹出dialog)。
<uses-permission Android:name="android.permission.SYSTEM_ALERT_WINDOW" />
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
Builder builder = new AlertDialog.Builder(this);
builder.setMessage("是否重启服务");
builder.setNegativeButton("取消", new OnClickListense() {
@Override
public void onClick(DialogInterface dialog, int which) {
// to do
}
});
builder.setPositiveButton("确定", new OnClickListense() {
@Override
public void onClick(DialogInterface dialog, int which) {
// to do
}
});
final AlertDialog dialog = buidler.create();
//在dialog show前添加此代码,表示该dialog属于系统dialog。
dialog.getWindow().setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
new Thread() {
public void run() {
SystemClock.sleep(2000);
hanlder.post(new Runnable() {
@Override
public void run() {
dialog.show();
}
});
};
}.start();
参考来源于:developerzjy/ServiceDemo
(3)参考链接
Android Context 上下文 你必须知道的一切
5.4 为什么开启Service去创建子线程而不是Activity中?
(1)场景:①若直接在Activity中新开一条线程来做耗时操作,当该Activity退出到桌面或其他情况时将成为一个后台进程。②若在Service中新启动线程,则此时Android会依据进程中当前活跃组件重要程度,将其判断为服务进程,优先级比①高。
(2)因为服务进程的优先级比后台进程的优先级高,所以对于一个需要启动一个长时间操作的activity来说,开启service去创建子线程比Activity中创建子线程更好,尤其是对于操作将很可能超出activity的持续时间时。
(3)比如要上传一个图片文件,应该开启一个service来进行上传工作,这样在用户离开activity时工作仍在进行。使用service将会保证操作至少有服务进程的优先级。
5.5 如何保证Service在后台不被杀死,进程保活?
在我的另一篇博客详细解析:Android进阶之进程优先级及提高优先级的方法(进程保活)
5.6 Service与IntentService的区别?
(1)IntentService,它是继承于Service并处理异步耗时请求的一个类。
(2)可以通过startService(Intent)方式来提交请求,和启动传统Service一样;
(3)该Service会在需要的时候创建;
(4)当完成所有的任务以后会自动关闭;
(5)可以启动多次,而每一个耗时操作会以工作队列的方式在onHandleIntent()中执行,每次只会执行一个工作线程;
(6)解决了传统的Service中处理完耗时操作忘记停止并销毁Service的问题。
(7)在我的另一篇博客详细解析:Android基础之深入理解IntentService
7 参考链接
Android:Service生命周期最全面解析
Android中startService和bindService的区别 - 简书
【推荐理由】详细全面地回答了上面的问题。
Service: onStartCommand 诡异的返回值
【推荐理由】通过实例来演示onStartCommand那诡异的返回值。
【源码解析】Service几个重要的方法运行在哪个线程
最后
以上就是淡定可乐为你收集整理的Android进阶之深入理解Service的全部内容,希望文章能够帮你解决Android进阶之深入理解Service所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复