一、 概述
Service是Android中的一个类,是四大组件之一。后台执行长时间的操作可以使用service实现,Service并不与用户产生UI交互。其他的应用组件可以启动Service,即便用户切换了其他应用,启动的Service仍可在后台运行。一个组件可以与Service绑定并与之交互,甚至是跨进程通信(IPC)。例如,一个Service可以在后台执行网络请求、播放音乐、执行文件读写操作或者与 content provider交互等。二、 Service使用场景
1、用于长期执行某些操作,一般与UI线程没有交互。比如上传/下载网络文件;2、跨进程通信,比如A进程中的Service被B进程调用。
三、 Service生命周期
管理服务的生命周期(从创建到销毁)主要有以下两种情况:
1、启动服务
该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行。此外,其他组件也可以通过调用 stopService() 来停止服务。服务停止后,系统会将其销毁。2、绑定服务
该服务在另一个组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务,服务不必自行停止运行。四、 用法
1、 两种启动方式
(1) Started
其他组件调用startService()方法可以启动一个Service,接着,Service会回调onStartCommand()生命周期方法。startService()方法中传入一个Intent参数,用于显式指定目标Service的名字,并携带data以供Service使用,该Intent参数将回传至onStartCommand()方法中。比如Activity需要向在线数据库上传数据,可以调用startService()启动一个Service,并将数据传入Intent的data中,接着,onStartCommand()方法会接收这个Intent并开启一个线程将数据上传至网络,当数据上传完成后,该Service将停止并被destroy。
public class StartServiceExample extends Service {
private LongRunning workThread = new LongRunning();
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("StartServiceExample", "flags = " + flags + ", startId = " + startId);
workThread.start();
return START_REDELIVER_INTENT;
}
@Override
public void onDestroy() {
super.onDestroy();
}
/**
* 耗时操作
*/
class LongRunning extends Thread{
@Override
public void run() {
//耗时业务处理
}
}
}
onStartCommand中返回值介绍:
①START_NOT_STICKY
表示当Service运行的进程被Android系统强制杀掉之后,不会重新创建该Service,如果想重新实例化该Service,就必须重新调用startService来启动。使用场景:表示当Service在执行工作中被中断几次无关紧要或者对Android内存紧张的情况下需要被杀掉且不会立即重新创建这种行为也可接受的话,这是可以在onStartCommand返回值中设置该值,如在Service中定时从服务器中获取最新数据。
②START_STICKY
表示Service运行的进程被Android系统强制杀掉之后,Android系统会将该Service依然设置为started状态(即运行状态),但是不再保存onStartCommand方法传入的intent对象,然后Android系统会尝试再次重新创建该Service,并执行onStartCommand回调方法,这时onStartCommand回调方法的Intent参数为null,也就是onStartCommand方法虽然会执行但是获取不到intent信息。使用场景:如果你的Service可以在任意时刻运行或结束都没什么问题,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY,比如一个用来播放背景音乐功能的Service就适合返回该值。
③START_REDELIVER_INTENT
表示Service运行的进程被Android系统强制杀掉之后,与返回START_STICKY的情况类似,Android系统会将再次重新创建该Service,并执行onStartCommand回调方法,但是不同的是,Android系统会再次将Service在被杀掉之前最后一次传入onStartCommand方法中的Intent再次保留下来并再次传入到重新创建后的Service的onStartCommand方法中,这样我们就能读取到intent参数。使用场景:如果我们的Service需要依赖具体的Intent才能运行(需要从Intent中读取相关数据信息等),并且在强制销毁后有必要重新创建运行,那么这样的Service就适合返回START_REDELIVER_INTENT。
(2) Bound
其他组件调用bindService()方法绑定一个Service。通过绑定方式启动的Service是一个client-server结构,该Service可以与绑定它的组件进行交互。一个bound service仅在有组件与其绑定时才会运行,多个组件可与一个service绑定,service不再与任何组件绑定时,该service会被destroy。serviceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
BindServiceExample.SimpleBinder sBinder = (BindServiceExample.SimpleBinder)service;
}
};
findViewById(R.id.btnBind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bindService(new Intent(MainActivity.this, BindServiceExample.class), serviceConnection, Context.BIND_AUTO_CREATE);
isBind = true;
}
});
findViewById(R.id.btnUnbind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(isBind){
unbindService(serviceConnection);
isBind = false;
}
}
});
public class BindServiceExample extends Service {
public class SimpleBinder extends Binder {
public BindServiceExample getService(){
return BindServiceExample.this;
}
public int add(int a, int b){
return a + b;
}
}
public BindServiceExample.SimpleBinder sBinder;
@Override
public void onCreate() {
super.onCreate();
sBinder = new BindServiceExample.SimpleBinder();
}
@Override
public IBinder onBind(Intent intent) {
return sBinder;
}
}
2、 配置文件
android:name | 服务类名 |
android:label | 服务的名字,如果此项不设置,那么默认显示的服务名则为类名 |
android:icon | 服务的图标 |
android:permission | 申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务 |
android:process | 表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字 |
android:enabled | 表示是否能被系统实例化,为true表示可以,为false表示不可以,默认为true |
android:exported | 表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为false |
五、 使用Service完成进程内通信
同一进程下,Service和Activity的连接可以用ServiceConnection来实现。需要实现一个新的ServiceConnection,重写onServiceConnected和onServiceDisconnected方法。调用bindService方法执行绑定,传入一个选择了要绑定的Service的Intent(显式或隐式)和一个你实现了的ServiceConnection实例。一旦连接建立,你就能通Service的接口onBind()得到serviceBinder实例进而得到Service的实例引用。一旦Service对象找到,就能得到它的公共方法和属性。
serviceConnection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
unbindService(serviceConnection);
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
BindServiceExample.SimpleBinder sBinder = (BindServiceExample.SimpleBinder)service;
}
}
六、 使用Service完成进程间通信
Service通过AIDL实现进程间通信,其他应用需要调用Service只需要把aidl文件拷贝到自己工程的src目录下,并绑定服务即可得到IBinder对象,通过IBinder对象可以实现与Service的交互。后续会在介绍AIDL时补全示例。
七、 Service和IntentService的区别与联系
1、IntentService实现
IntentService继承于Service,若Service不需要同时处理多个请求,那么使用IntentService将是最好选择:只需要重写onHandleIntent()方法,该方法接收一个回传的Intent参数,可以在该方法内进行耗时操作,因为它默认开启了一个子线程,操作执行完成后也无需手动调用stopSelf()方法,onHandleIntent()会自动调用该方法。在onCreate()里面初始化一个HandlerThread,每次调用onStartCommand的时候,通过mServiceHandler发送一个消息,消息中包含我们的intent。然后在该mServiceHandler的handleMessage中去回调onHandleIntent(intent)。
2、使用IntentService注意事项
(1)默认在子线程中处理回传到onStartCommand()方法中的Intent;(2)在onHandleIntent()方法中处理按时间排序的Intent队列,所以不用担心多线程问题;
(3)IntentService是单个worker thread,任务需要排队,不适合大多数的多任务情况;
(4)当所有请求处理完成后,自动停止service,无需手动调用stopSelf()方法;
(5)默认实现了onBind()方法,并返回null;
(6)默认实现了onStartCommand()方法,并将回传的Intent以序列的形式发送给onHandleIntent(),只需重写onHandleIntent()方法并处理Intent即可。
3、example
// MyIntentService.java
public class MyIntentService extends IntentService {
public static final String ACTION_UPLOAD_IMG = "com.example.hejin.intentservice.action.UPLOAD_IMAGE";
public static final String EXTRA_IMG_PATH = "com.example.hejin.intentservice.extra.IMG_PATH";
public MyIntentService (){
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_UPLOAD_IMG.equals(action)) {
final String path = intent.getStringExtra(EXTRA_IMG_PATH);
handleUploadImg(path);
}
}
}
private void handleUploadImg(String path) {
try {
//模拟上传耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyIntentService.class);
intent.setAction(MyIntentService.ACTION_UPLOAD_IMG);
intent.putExtra(MyIntentService.EXTRA_IMG_PATH, imagePath);
MainActivity.this.startService(intent);
}
});
八、 Service和Thread的区别与联系
1、 概念
Thread 是程序执行的最小单元,它是分配CPU的基本单位,android系统中UI线程也是线程的一种,当然Thread还可以用于执行一些耗时异步的操作。Service是Android的一种机制,服务是运行在主线程上的,它是由系统进程托管。它与其他组件之间的通信类似于client和server,是一种轻量级的IPC通信,这种通信的载体是binder,它是在linux层交换信息的一种IPC,而所谓的Service后台任务只不过是指没有UI的组件罢了。
2、 任务
线程一般指的是工作线程(即后台线程),而主线程是一种特殊的工作线程,它负责将事件分派给相应的用户界面小工具,如绘图事件及事件响应,因此为了保证应用 UI 的响应能力主线程上不可执行耗时操作。如果执行的操作不能很快完成,则应确保它们在单独的工作线程执行。Service 则是android系统中的组件,一般情况下它运行于主线程中,因此在Service中是不可以执行耗时操作的,否则系统会报ANR异常,之所以称Service为后台服务,大部分原因是它本身没有UI,用户无法感知(当然也可以利用某些手段让用户知道),但如果需要让Service执行耗时任务,可在Service中开启单独线程去执行。
3、 使用场景
当要执行耗时的网络或者数据库查询以及其他阻塞UI线程或密集使用CPU的任务时,都应该使用工作线程(Thread),这样才能保证UI线程不被占用而影响用户体验。在应用程序中,如果需要长时间的在后台运行,而且不需要交互的情况下,使用服务。比如播放音乐,通过Service+Notification方式在后台执行同时在通知栏显示着。