Service
一、基础知识
1、定义
服务,属于Android中的计算型组件
2、作用
提供需要在后台长期运行的服务(如复杂计算、下载等等)
3、特点
- 长生命周期的、没有用户界面、在后台运行、用户不手动关闭不会停止
- 从Context派生出来的,也有getResources(),getContentResolver()等方法
二、相关方法
1、Context相关方法
-
startService(Intent intent) ComponentName
启动一个Service,访问者和Service之间没有关联,即使访问者退出了,Service仍然运行 -
stopService (Intent intent) boolean
之后会自动调用内部方法:onDestory() -
bindService(Intent service, ServiceConnection conn, int flags) boolean
访问者与Service绑在了一起,访问者一旦退出,Service也将终止。conn:该参数是一个ServiceConnection(I)对象,用于监听访问者和Service之间的连接情况,若连接成功,将回调ServiceConnection对象的onServiceConnected(ComponentName name, IBinder service)
方法,当异常终止连接时,回调ServiceConnection对象的onServiceDisconnected(ComponentName name)
方法(若访问者主动调用unbindService(ServiceConnection conn)
断开连接,不会回调此方法),flags:指定绑定时若Service未创建是否自动创建,值:0或BIND_AUTO_CREATE
unbindService(ServiceConnection conn)
2、内部自动调用方法(生命周期方法)
-
onBind(Intent intent) IBinder
应用程序可以通过IBinder与Service组件进行通信,绑定该Service时回调该方法。
在绑定本地Service的情况下,onBind(Intent service)方法返回的IBinder对象会传给ServiceConnection对象的onServiceConnected(ComponentName name, IBinder service)的service参数,IBinder相当于一个代理人的角色,实现互相通信,所以onBind方法不应该返回一个null(一般返回一个继承Binder类的对象,可以操作Service中的内容,一般使用private class指定,里面有多个方法,要暴露什么方法,使用接口去定义),在onServiceConnected方法就可以使用该代理人。作用:暴露一些方法,改变服务的状态
-
onUnbind(Intent intent) boolean
当绑定在该Service上的所有客户端都断开连接时回调该方法 -
onStartCommand(Intent intent, int flags, int startId) int
调用startService(Intent)方法启动Service时回调该方法,会被调用多次 -
onCreate() void
第一次被创建时回调该方法,仅被调用一次 onDestroy() void
stopSelf()
3、onStartCommand(Intent intent, int flags, int startId)
intent :启动时,启动组件传递过来的Intent,如Activity可利用Intent封装所需要的参数并传递给Service
flags:表示启动请求时是否有额外数据。可选值有0,START_FLAG_REDELIVERY,START_FLAG_RETRY,0代表没有,它们具体含义如下:
a)START_FLAG_REDELIVERY
这个值代表返回值为START_REDELIVER_INTENT,而且在上一次服务被杀死前会去调用stopSelf()
方法停止服务。
b)START_FLAG_RETRY
该flag代表当onStartCommand调用后一直没有返回值时,会尝试重新去调用onStartCommand()。startId : 指明当前服务的唯一ID,与
stopSelfResult (int startId)
配合使用,stopSelfResult 可以更安全地根据ID停止服务。
实际上onStartCommand的返回值int类型才是最最值得注意的,它有三种可选值, START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:
START_STICKY
当Service因内存不足而被系统kill后,一段时间后内存再次空闲时,系统将会尝试重新创建此Service,一旦创建成功后将回调onStartCommand方法,但其中的Intent将是null,除非有挂起的Intent,如pendingintent,这个状态下比较适用于不执行命令、但无限期运行并等待作业的媒体播放器或类似服务。START_NOT_STICKY
当Service因内存不足而被系统kill后,即使系统内存再次空闲时,系统也不会尝试重新创建此Service。除非程序中再次调用startService启动此Service,这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。START_REDELIVER_INTENT
当Service因内存不足而被系统kill后,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand(),任何挂起 Intent均依次传递。与START_STICKY不同的是,其中的传递的Intent将是非空,是最后一次调用startService中的intent。这个值适用于主动执行应该立即恢复的作业(例如下载文件)的服务。
4、Service生命周期
4.1、单独调用
- startService()->onCreate()->onStartCommand()->onStop()->onDestory()
- bindService()->onCreate()->onBind()->onUnbind()->onDestory()
4.2、混合调用
- onCreate()->onStartCommand()->onBind()->onUnbind()->onRebind()
说明:混合调用时,要两两对应,不要相互嵌套(类似于html标签)
服务只能解绑一次,多次会报错
建议在Activity的onDestroy方法中解绑掉服务
startService用于保证服务的后台运行,bindService用于调用服务的方法
三、Service分类
1、本地Service
这是最普通、最常用的后台服务Service。
1.1、使用步骤
步骤1:新建子类继承Service类
需重写父类的onCreate()、onStartCommand()、onDestroy()和onBind()方法
步骤2:构建用于启动Service的Intent对象
步骤3:调用startService()启动Service、调用stopService()停止服务
步骤4:在AndroidManifest.xml里注册Service
属性说明:
-
android:name
Service的类名 -
android:label
Service的名字,若不设置,默认为Service类名 -
android:icon
Service的图标 -
android:permission
声明此Service的权限,提供了该权限的应用才能控制或连接此服务 -
android:process
表示该服务是否在另一个进程中运行(远程服务) 不设置默认为本地服务;remote则设置成远程服务 -
android:enabled
是否可用即是否可以被系统实例化 -
android:exported
是否能被其他应用隐式调用。 默认值是由service中有无intent-filter决定的,如果有intent-filter,默认值为true,否则为false。为false的情况下,即使有intent-filter匹配,也无法打开,即无法被其他应用隐式调用
2、可通信Service
实例Demo
步骤1:在新建子类继承Service类,并新建一个子类继承自Binder类、写入与Activity关联需要的方法、创建实例
public class MyService extends Service {
private MyBinder mBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("执行了onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
System.out.println("执行了onDestory()");
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
System.out.println("执行了onBind()");
//返回实例
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
System.out.println("执行了onUnbind()");
return super.onUnbind(intent);
}
//新建一个子类继承自Binder类
class MyBinder extends Binder {
public void service_connect_Activity() {
System.out.println("Service关联了Activity,并在Activity执行了Service的方法");
}
}
}
步骤2:在Activity通过调用MyBinder类中的public方法来实现Activity与Service的联系,即实现了Activity指挥Service干什么Service就去干什么的功能
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button startService;
private Button stopService;
private Button bindService;
private Button unbindService;
private MyService.MyBinder myBinder;
//创建ServiceConnection的匿名类
private ServiceConnection connection = new ServiceConnection() {
//重写onServiceConnected()方法和onServiceDisconnected()方法
//在Activity与Service建立关联和解除关联的时候调用
@Override
public void onServiceDisconnected(ComponentName name) {
}
//在Activity与Service解除关联的时候调用
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//实例化Service的内部类myBinder
//通过向下转型得到了MyBinder的实例
myBinder = (MyService.MyBinder) service;
//在Activity调用Service类的方法
myBinder.service_connect_Activity();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService = (Button) findViewById(R.id.startService);
stopService = (Button) findViewById(R.id.stopService);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService = (Button) findViewById(R.id.bindService);
unbindService = (Button) findViewById(R.id.unbindService);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
//点击启动Service
case R.id.startService:
//构建启动服务的Intent对象
Intent startIntent = new Intent(this, MyService.class);
//调用startService()方法-传入Intent对象,以此启动服务
startService(startIntent);
break;
//点击停止Service
case R.id.stopService:
//构建停止服务的Intent对象
Intent stopIntent = new Intent(this, MyService.class);
//调用stopService()方法-传入Intent对象,以此停止服务
stopService(stopIntent);
break;
//点击绑定Service
case R.id.bindService:
//构建绑定服务的Intent对象
Intent bindIntent = new Intent(this, MyService.class);
//调用bindService()方法,以此停止服务
bindService(bindIntent,connection,BIND_AUTO_CREATE);
//参数说明
//第一个参数:Intent对象
//第二个参数:上面创建的Serviceconnection实例
//第三个参数:标志位
//这里传入BIND_AUTO_CREATE表示在Activity和Service建立关联后自动创建Service
//这会使得MyService中的onCreate()方法得到执行,但onStartCommand()方法不会执行
break;
//点击解绑Service
case R.id.unbindService:
//调用unbindService()解绑服务
//参数是上面创建的Serviceconnection实例
unbindService(connection);
break;
default:
break;
}
}
}
3、前台Service
前台Service和后台Service(普通)最大的区别就在于:
-
前台Service在下拉通知栏有显示通知(如下图),但后台Service没有;
前台Service优先级较高,不会由于系统内存不足而被回收;后台Service优先级较低,当系统出现内存不足情况时,很有可能会被回收
//用法很简单,只需要在原有的Service类对onCreate()方法进行稍微修改即可
@Override
public void onCreate() {
super.onCreate();
System.out.println("执行了onCreat()");
//添加下列代码将后台Service变成前台Service
//构建"点击通知后打开MainActivity"的Intent对象
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
//新建Builer对象
Notification.Builder builer = new Notification.Builder(this);
builer.setContentTitle("前台服务通知的标题");//设置通知的标题
builer.setContentText("前台服务通知的内容");//设置通知的内容
builer.setSmallIcon(R.mipmap.ic_launcher);//设置通知的图标
builer.setContentIntent(pendingIntent);//设置点击通知后的操作
Notification notification = builer.getNotification();//将Builder对象转变成普通的notification
startForeground(1, notification);//让Service变成前台Service,并在系统的状态栏显示出来
}
4、使用场景
四、其他
1、Service和Thread的区别
Service和Thread之间没有任何关系,之所以有不少人会把它们联系起来,主要因为Service的后台概念
后台的定义:后台任务运行完全不依赖UI,即使Activity被销毁,或者程序被关闭,只要进程还在,后台任务就可以继续运行
一般来说,会将Service和Thread联合着用,即在Service中再创建一个子线程(工作线程)去处理耗时操作逻辑,如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//新建工作线程
new Thread(new Runnable() {
@Override
public void run() {
// 开始执行后台任务
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
class MyBinder extends Binder {
public void service_connect_Activity() {
//新建工作线程
new Thread(new Runnable() {
@Override
public void run() {
// 执行具体的下载任务
}
}).start();
}
}
2. IntentService
2.1、定义
Android里的一个封装类,继承四大组件之一的Service
2.2、作用
处理异步请求 & 实现多线程
2.1、使用场景
线程任务需按顺序在后台执行
- 最常见的场景:离线下载
- 不符合多个数据同时请求的场景:所有的任务都在同一个Thread looper里执行
2.1、使用步骤
步骤1:定义 IntentService的子类
需传入线程名称、复写onHandleIntent()方法
public class myIntentService extends IntentService {
/**
* 在构造函数中传入线程名字
**/
public myIntentService() {
// 调用父类的构造函数
// 参数 = 工作线程的名字
super("myIntentService");
}
/**
* 复写onHandleIntent()方法
* 根据 Intent实现 耗时任务 操作
**/
@Override
protected void onHandleIntent(Intent intent) {
// 根据 Intent的不同,进行不同的事务处理
String taskName = intent.getExtras().getString("taskName");
switch (taskName) {
case "task1":
Log.i("myIntentService", "do task1");
break;
case "task2":
Log.i("myIntentService", "do task2");
break;
default:
break;
}
}
@Override
public void onCreate() {
Log.i("myIntentService", "onCreate");
super.onCreate();
}
/**
* 复写onStartCommand()方法
* 默认实现 = 将请求的Intent添加到工作队列里
**/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("myIntentService", "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
Log.i("myIntentService", "onDestroy");
super.onDestroy();
}
}
步骤2:在Manifest.xml中注册服务
<service android:name=".myIntentService">
<intent-filter >
<action android:name="cn.scu.finch"/>
</intent-filter>
</service>
步骤3:在Activity中开启Service服务
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 同一服务只会开启1个工作线程
// 在onHandleIntent()函数里,依次处理传入的Intent请求
// 将请求通过Bundle对象传入到Intent,再传入到服务里
// 请求1
Intent i = new Intent("cn.scu.finch");
Bundle bundle = new Bundle();
bundle.putString("taskName", "task1");
i.putExtras(bundle);
startService(i);
// 请求2
Intent i2 = new Intent("cn.scu.finch");
Bundle bundle2 = new Bundle();
bundle2.putString("taskName", "task2");
i2.putExtras(bundle2);
startService(i2);
startService(i); //多次启动
}
}
测试结果
3、进程优先级
- 前台进程:某个Activity可见,获得焦点
- 可见进程:某个Activity可见,但是没有焦点
- 服务进程:有一个服务在后台运行
- 后台进程:没有任何服务,打开一个Activity然后最小化(容易被回收)
- 空进程:没有任何活动组件存在的进程(容易被回收)
4、AIDL
4.1、简介
为了实现跨进程通信(IPC),实现进程之间的数据交换
4.2、步骤
①:服务端创建.aidl文件
②:服务端创建Service,并在onBind中返回一个IBinder(接口对象(代理人))
IBinder binder = new IMyAidlInterface.Stub() {
@Override
public int add(int num1, int num2) throws RemoteException {
return num1 + num2;
}
};
③:客户端创建(复制)和服务端一样的.aidl文件(包名也必须一致)
④:客户端创建ServiceConnection的子类,并实现其onServiceConnected(ComponentName name, IBinder service),在方法中(service就是中间人)iMyAdil = IMyAidlInterface.Stub.asInterface(service)
,客户端可以使用服务端的方法了
⑤:客户端bindService
4.3、AIDL支持的数据类型(支持其实就是定义aidl的时候,参数可以使用的类型)
基本数据类型(除short),String,CharSequence,List(仅支持ArrayList),Map(仅支持HashMap),Parcelable
注意:
①:非基本数据类型,需要用in,out,inout指定数据的走向
②:复杂类型(如Book)必须实现Parcelable,且需要Book.aidl(内容parcelable Book;)—— 包名必须和Book.java相同,无论是否相同的包,都需要导入包
③:AIDL接口中只支持方法,不支持声明静态变量
参考文献
Android四大组件:Service史上最全面解析
Android 多线程 解析:IntentService(含源码解析)