说明:本篇文章涉及与Service的生命周期、特性、启动方式、Client-Service的交互(通信)、一些“特殊的”Service、注意事项,如果各位码友发现文中有不对的地方请指正,如果有什么不同的见解也欢迎沟通交流。转载或者CV操作,请务必注明出处,谢谢!
定义说明:
Service(服务)是Android提供的一种可以在后台长时间运行操作指令而没有用户界面的应用组件。Service可由其他应用组件启动(一般是Activity、Service,BroadcastReceiver)。Service默认是运行在主线程(UI线程)中的,因此不要在Service中执行耗时的操作,如果需要进行耗时操作可以开启子线程进行处理(注意不要直接new子线程),Service的运行不依赖于任何用户界面,即使App被切换到后台或者用户打开另一个App,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个App进程被杀掉时,所有依赖于该进程的Service也会停止运行。PS:Android四大应用组件都需要在清单文件(AndroidMan-ifest.xml)中声明。
生命周期:
Service的生命周期可以分为onCreate()、onStart()、onDestroy()三个时间段。当第一次启动Service时,会调用onCreate()、onStart()这两个方法,当停止Service时,则执行onDestroy()方法。
注意:1、Service所在进程如果被Kill掉,则不会调用onDestroy()方法。
2、如果Service已经启动了,当再次启动Service时,不会再执行onCreate()方法,而是直接执行onStart()方法。
3、正常状态下可以通过Service.stopSelf()方法或者Context.stopService()方法或者unbindService(.)来停止Service,而且只要调用一次就可以停止服务,无论之前调用了多少次的启动服务方法。
启动(使用)方式:
一、context.startService()方式启动。
即直接启动服务的方式。使用该方式启动需要关注方法onStartCommand(),无需关注onBind(该方法需要重写,使其返回为null即可),该方式的调用组件(Client)可以通过intent传递参数给Service,但是启动的Service中的方法不可以被外部调用,如果有消息需要通知调用者组件,可以使用广播、观察者模式(单例出来,可以参考我介绍Activity时文中的示例)。一般同一个App进程内建议使用显示调用。
1、生命周期流程:onCreate (第一次)--> onStart (多次调用startService()) --> onDestroy(stopService())。
2、onStartCommand(Intent intent, int flags, int startId)方法在每次调用startService()后都会被执行。
注意:1)、该方法一般使用第一种启动方式才有实际作用。
2)、intent参数是其他组件开启Service传递过来的,startId指明当前服务的唯一Id其实就是startService(...)的请求标识,与stopSelfResult(int startId)配合使用,可以更安全地根据Id停止服务。flags表示启动请求时是否有额外数据,可选值有0
(START_STICKY_COMPATIBILITY)、START_FLAG_REDELIVERY、START_FLAG_RETRY。
(1)、0是默认值。
(2)、START_FLAG_REDELIVERY代表onStartCommand方法的返回值为START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf方法停止服务。
(3)、TART_FLAG_RETRY该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。
3)、onStartCommand(Intent intent, int flags, int startId)的返回值。
(1)、START_NOT_STICKY:当Service因为内存不足而被系统kill后,接下来的某个时间内,即使系统内存足够可用,系统也不会尝试重新创建此Service。startService(...)被再次调用。
(2)、START_STICKY:当Service因为内存不足而被系统kill后,接下来的某个时间内,当系统内存足够可用的时候,系统将会尝试重新创建此Service,创建成功后将回调onStartCommand(...)方法,但其中的Intent将是null(pendingintent除外)。
(3)、START_REDELIVER_INTENT:当Service因为内存不足而被系统kill后,接下来的某个时间内,当系统内存足够可用的时候,系统将会尝试重新创建此Service,创建成功后将回调onStartCommand(...)方法,其中的Intent将是是最后一次调用startService(...) 中的intent。
(4)、START_STICKY_COMPATIBILITY:(1)的兼容类型,同样的作用。
3、调用stopService()不必判断Service是否被运行过、在运行中,而且只要调用一次就可以停止服务,无论之前调用了多少次的启动服务方法。也不管该Service是被哪个应用的哪个组件启动的。
二、context.bindService()方式启动
即绑定服务的方式启动。使用该方式启动无需关注onStartCommand(),需要关注onBind()方法,该方法必须返回一个IBinder接口的实现类,供客户端(Client)用来与服务(Service)进行通信,使用该方式调用者组件(Client)可以调用Service中的方法(支持同进程、跨进程通信或交互)。与启动服务不同的是绑定服务的生命周期通常依附于其他应用组件(如Activity),不会无限期在后台运行,也就是说宿主(如Activity)解除绑定后,绑定服务就会被销毁。所以在BroadcastReceiver中不可以使用该种方式启动服务,因为广播的生命周期很短。
1、生命周期流程: onCreate()(第一次)-> onStart ()(多次) ->onBind()(第一次) -> onUnbind()(unbindService()) -> onDestroy()。
2、使用bindService()启动一般可以分为三种。
1)、最常用的方式,从onBind()返回一个IBinder接口的实现类,同一App(进程)内可以这样使用,不同App(跨进程)则不要使用。另外需要注意unbindService(.)之前一定要确保该服务被绑定(启动)过,不然也会报错崩溃。该方式只是调用组件(Client)单项通知Server,如果需要Server 通知调用组件(Client),可以使用广播、观察者模式(单例出来,可以参考我介绍Activity时文中的示例)。
下边是个示例。
public class AdvLoadingService extends Service {
private String TAG = "AdvLoadingService";
public AdvLoadingService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();//返回一个IBinder接口的实现类。
}
public class MyBinder extends Binder {//IBinder接口的实现类
public AdvLoadingService getService() {
return AdvLoadingService.this;
}
}
public void xxx(){
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
public class BootAdvActivity extends Activity{
String TAG = "BootAdvActivity";
private ServiceConnection mAdvServiceConnection;
private AdvLoadingService.MyBinder mAdvLoadServerBinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE); // 设置无标题
Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏
window.setFormat(PixelFormat.RGBA_8888); // 设置图片参数
setContentView(R.layout.boot_adv_activity);
initAdvLoadServer();//初始化服务
}
private void initAdvLoadServer() {
Intent itService = new Intent(this, AdvLoadingService.class);
mAdvServiceConnection = new ServiceConnection() {//也可以采用implements
/**
* 绑定服务的时候被回调,在这个方法中获取绑定Service传递过来的IBinder对象,
* 通过这个IBinder对象,实现宿主和Service的交互。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAdvLoadServerBinder = (AdvLoadingService.MyBinder) service;//获取IBinder接口实现类。
}
/**
* 当取消绑定的时候被回调。但正常情况下是不被调用的,当Service服务被意外销毁时,
* 例如内存的资源不足时这个方法才被调用。
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mAdvLoadServerBinder = null;
}
};
bindService(itService, mAdvServiceConnection, Context.BIND_AUTO_CREATE);//绑定服务,自动创建。
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume" );
dealAdvLoadServer();
}
private void dealAdvLoadServer() {
if (mAdvLoadServerBinder != null && mAdvLoadServerBinder.getService() != null) {
mAdvLoadServerBinder.getService().xxx();//直接调用Service中的方法。
}
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy ");
if(mAdvServiceConnection != null && mAdvLoadServerBinder != null){//解除绑定
unbindService(mAdvServiceConnection);//注意一定要确保服务被绑定启动过。
}
}
}
2)、使用Messenger,通过Messenger方式返回Binder对象可以不用考虑调用组件(Client)和Service是否属于同一个App(进程),既可以在同一App(进程)使用也可以在不同App(跨进程)使用,并且可以实现调用组件(Client)和Service之间的双向通信。但是注意:不支持严格意义上的多线程并发处理,因为Messenger会在单一线程中创建包含所有请求的队列,也就是说Messenger是以串行的方式处理客户端(Client)发来的消息。这种实现方式说白了就是调用组件(Client)和Service各持有对方的一个消息队列对象,向对方的队列发送需要的消息进行处理。
下边是示例。
public class AdvLoadingService extends Service {
private String TAG = "AdvLoadingService";
private Messenger mClientMessenger;
private Messenger mServerMessenger = null;
class ServerHandler extends Handler {//注意handler的使用的内存泄漏,这里仅仅是示例。
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.MSG_ADV_INFO_FROM_BOOT:
Log.d(TAG, "receive msg from boot");
mClientMessenger = msg.replyTo;
Message msgToBoot = Message.obtain(null, Constant.MSG_ADV_INFO_TO_BOOT);
try {
if(mClientMessenger != null){
mClientMessenger.send(msgToBoot);
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException="+e);
}
break;
default:
super.handleMessage(msg);
}
}
public AdvLoadingService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
if(mServerMessenger == null){
mServerMessenger = new Messenger(new ServerHandler());
}
}
@Override
public IBinder onBind(Intent intent) {
if(mServerMessenger == null){
mServerMessenger = new Messenger(new ServerHandler());
}
return mServerMessenger.getBinder();//返回一个IBinder接口的实现类。
}
public void xxx(){
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
public class BootAdvActivity extends Activity{
String TAG = "BootAdvActivity";
private ServiceConnection mAdvServiceConnection;
private Messenger mServerMessenger;
private Handler mClientHandler = new ClientHandler();
private Messenger mClientMessenger = null;
private class ClientHandler extends Handler {//注意handler的使用的内存泄漏,这里仅仅是示例。
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constant.MSG_ADV_INFO_TO_BOOT:
Log.d(TAG,"receive info from server");
break;
default:
break;
}
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE); // 设置无标题
Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏
window.setFormat(PixelFormat.RGBA_8888); // 设置图片参数
setContentView(R.layout.boot_adv_activity);
initAdvLoadServer();//初始化服务
}
private void initAdvLoadServer() {
if(mClientMessenger == null){
mClientMessenger = new Messenger(mClientHandler);
}
Intent itService = new Intent(this, AdvLoadingService.class);
mAdvServiceConnection = new ServiceConnection() {//也可以采用implements
/**
* 绑定服务的时候被回调,在这个方法中获取绑定Service传递过来的IBinder对象,
* 通过这个IBinder对象,实现宿主和Service的交互。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取IBinder接口实现类。
mServerMessenger = new Messenger(service);
}
/**
* 当取消绑定的时候被回调。但正常情况下是不被调用的,当Service服务被意外销毁时,
* 例如内存的资源不足时这个方法才被调用。
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mServerMessenger = null;
}
};
bindService(itService, mAdvServiceConnection, Context.BIND_AUTO_CREATE);//绑定服务,自动创建。
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume" );
dealAdvLoadServer();
}
private void dealAdvLoadServer() {
if (mServerMessenger != null) {
Log.d(TAG, "dealAdvLoadServer");
Message msg = Message.obtain(null, Constant.MSG_ADV_INFO_FROM_BOOT);
msg.replyTo = mClientMessenger;
try {
//发送消息给服务端
mServerMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy ");
if(mAdvServiceConnection != null && mServerMessenger != null){//解除绑定
unbindService(mAdvServiceConnection);//注意一定要确保服务被绑定启动过。
}
}
}
3)、使用AIDL(Android 接口定义语言),由于Messenger是以串行的方式处理Client发来的消息,如果当前有大量消息同时发送到Server(服务端),以Messenger的方式处理便力不从心了,所以AIDL就上场了,但是使用该方式Server(服务端)必须具备多线程处理能力,并确保线程安全性。PS:Messenger是依靠AIDL实现的,只不过我们使用的时候是黑盒而已。
这里简单说明一下使用步骤:
(1)、创建 AIDL 。
①、创建实体类,实现Parcelable接口,以便序列化/反序列化。
②、新建aidl文件夹(和res/assets/java目录平级即可),在其中创建接口aidl文件(使用的包名要和java文件夹的包名一致)以及实体类的映射aidl文件(在其中声明映射的实体类名称与类型,注意这个.aidl的包名要和实体类包名一致)。
③、Make project,生成Binder的Java文件(build/generated/source/aidl目录寻找)。
(2)、Server(服务端) 。
①、创建Service,在其中创建上面生成的Binder对象实例,实现接口定义的方法。
②、在onBind()中返回。
(3)、Client(客户端)。
①、实现ServiceConnection接口,在其中拿到AIDL类(注意Client和Server都有(1)中的aidl文件)。
②、bindService()。
③、调用AIDL类中定义好的方法。
*************************************************************************************************************************************************
package com.xxx.xx.x;
interface IAdvLoadingService{
IBootManager getBootManager();//这里也可以使用其他的aidl文件,
String getBootAdv();//一个方法
}
//aidl接口类(.aidl文件),注意该文件所在位置包名要和java文件夹包名一致
*************************************************************************************************************************************************
package com.yyy.yy.y;
parcelable BootAdvBean;
//实体类的映射类(.aidl文件),注意这个.aidl的包名要和实体类包名一致。
*************************************************************************************************************************************************
系统生成的IAdvLoadingService.java文件,由于涉及部分私密代码这里就不在具体展示了;实体类也不在
BootAdvBean展示了,就是涉及到数据解析才会用到。
*************************************************************************************************************************************************
public class AdvLoadingService extends Service {
private String TAG = "AdvLoadingService";
/**
* 创建生成的本地 Binder 对象,实现AIDL制定的方法
*/
private IBinder mIBinder = new IAdvLoadingService.Stub() {
@Override
public String getBootAdv() throws RemoteException {
return "";//需要操作的方法
}
@Override
public IBootManager getBootManager() throws RemoteException {
return "";//其他的aidl(),
}
};
public AdvLoadingService() {
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
}
@Override
public IBinder onBind(Intent intent) {
return mIBinder;//返回一个IBinder接口的实现类。
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG, "onUnbind");
return super.onUnbind(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
public class BootAdvActivity extends Activity{
String TAG = "BootAdvActivity";
private ServiceConnection mAdvServiceConnection;
private IAdvLoadingService mAdvLoadServer;
private IBootManager mBootManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate");
requestWindowFeature(Window.FEATURE_NO_TITLE); // 设置无标题
Window window = getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); // 设置全屏
window.setFormat(PixelFormat.RGBA_8888); // 设置图片参数
setContentView(R.layout.boot_adv_activity);
initAdvLoadServer();//初始化服务
}
private void initAdvLoadServer() {
Intent itService = new Intent();
itService.setAction("com.....");
mAdvServiceConnection = new ServiceConnection() {//也可以采用implements
/**
* 绑定服务的时候被回调,在这个方法中获取绑定Service传递过来的IBinder对象,
* 通过这个IBinder对象,实现宿主和Service的交互。
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//获取IBinder接口实现类,转换成 AIDL,在不同进程会返回个代理。
mAdvLoadServer = IAdvLoadingService.Stub.asInterface(service);//
}
/**
* 当取消绑定的时候被回调。但正常情况下是不被调用的,当Service服务被意外销毁时,
* 例如内存的资源不足时这个方法才被调用。
*/
@Override
public void onServiceDisconnected(ComponentName name) {
mAdvLoadServer = null;
}
};
bindService(itService, mAdvServiceConnection, Context.BIND_AUTO_CREATE);//绑定服务,自动创建。
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume" );
dealAdvLoadServer();
}
private void dealAdvLoadServer() {
if (mAdvLoadServer != null) {
mAdvLoadServer.getBootAdv();//调用Service中实现的方法。
mBootManager = mAdvLoadServer.getBootManager()//获取其他adil的IBinder接口实现类
mBootManager.xxx();//其中的方法.
}
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy ");
if(mAdvServiceConnection != null && mAdvLoadServer != null){//解除绑定
unbindService(mAdvServiceConnection);//注意一定要确保服务被绑定启动过。
}
}
}
三、一些特殊的Service以及说明。
1)Local Service 与 Remote Service。
前者一般称之为”本地服务“,是指Client - Service同处于一个进程,后者一般称之为”远程服务“,一般是指Service处于单独的一个进程中。
2)Foreground Service(前台服务)。
通过Service.startForeground (int id, Notification notification)方法可以将此Service设置为前台服务。在UI显示上,notif-ication是一个状态的通知,而且该Service拥有更高的进程优先级,也可以直接notification通信。
3)IntentService。
这个service是系统提供给我们的一个特殊的Service,它默认实现了onBind(..)方法,直接返回null,并定义了抽象方法 onHandlerIntent(..),通过实现该方法去处理耗时任务,这里不用担心阻塞主线程的问题,也不用开启子线程去处理,因为on-HandlerIntent中的任务都是在子线程中处理的,另外当这个耗时任务处理完毕之后,IntentService将自动结束,无需人为调用方法使其结束,这里需要说明一下IntentService处理任务时,也是按照队列的方式一个个去处理,而非是以真正意义上的多线程并发方式处理任务的。
四、一些注意事项(有待补充)。
1)避免在Service中的onStartCommand()/onBind()方法中执行耗时操作,如果确实需要,可以使用IntentService或采用其他异步机制完成(不建议直接new线程)。
2)建议如果没有必要就以显示的方式启动或者绑定Service,并且不要为服务声明Intent Filter,保证应用的安全性。如果确实需要使用隐式调用,则可以为Service提供Intent Filter,但是注意一样要使用setPackage()方法指定包名,这样可以确保使用目标Service的准确性。
3)BroadcastReceiver中不可以使用bindService()方式启动服务,因为广播的生命周期很短。