四大组件中,我们最熟悉的而且最常用的应该是Activity了,它的主要作用是提供用户交互的界面,那么如果有一些任务只需要在后台执行而不需要界面,那应该怎么办呢。今天总结归纳的是Service。
目录
一、Service概述
二、Service的基本用法
1.启动Service
2.绑定Service
三、Service生命周期
四、Service的几点说明
五、IntentService使用
六、相关参考
一、概述
首先我们看下官方对Service的介绍:
Service是一个可以在后台执行需要长时间运行的操作的应用组件,它没有用户界面。其它组件可以启动一个Service,之后即使用户切换到别的应用里,这个Service也将继续在后台运行。此外,一个组件可以与一个Service绑定来与之交互,甚至可以执行IPC进程间通信。服务可以在后台执行很多任务,比如处理网络事务,播放音乐,文件读写或者与一个内容提供者交互,等等。
Service主要有两种形式:启动状态和绑定状态。
启动状态(Started)
当一个应用组件比如activity通过调用startService()来启动一个服务的时候,服务便处于启动状态。一旦启动,服务可以在后台无限期地运行下去,即使当启动它的组件已经销毁。通常情况下,一个启动的service执行一个单一的操作并且不会返回任何结果给调用者。例如,服务可能通过网络下载或者上传一个文件,当操作完成,服务会自己停止。
绑定状态(Bound)
当一个应用组件通过调用bindService()来与一个服务绑定时,服务便处于绑定状态。一个绑定的服务提供了一个客户端-服务器端接口来允许组件与服务进行交互,发送请求,得到结果甚至通过IPC进程间通信来完成操作。只有当其它组件与服务绑定时,服务才会处于绑定状态。多个组件可以同时与服务绑定,但是当他们全部都解除绑定时,服务就会销毁。
二、Service的基本用法
对于Activity的使用,我们应该都比较熟悉了,一般是先新建一个继承Activity的自己的Activity,然后在里面写自己的逻辑,如果要启动一个Activity的话可以通过调用StartActivity()传入一个Intent对象来完成,在清单文件里进行Activity注册也是必不可少的一步。与之类似,Service基本用法也很简单,下面简单介绍启动Service的步骤,之后会有详细的介绍。
启动Service
- 新建Service
public class TestService extends Service {
private final String TAG = "TestService";
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "---onCreate()---");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "---onStartCommand()---");
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "---onDestroy()---");
}
}
-
注册Service
3.启动Service
从上面这张图可以看到,新建一个Intent对象并把它传给startService,这样就可以启动一个Service了,启动Service后如果想要停止它,可以使用stopService方法。
绑定Service
下面是组件与Service绑定的步骤。
- 首先我们修改一些之前的Service.
public class TestService extends Service {
private final String TAG = "TestService";
//实例化"粘合剂" 可返回给与Service绑定的组件
private MyBinder mMyBinder = new MyBinder();
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "---onCreate()---");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "---onStartCommand()---");
return super.onStartCommand(intent, flags, startId);
}
//绑定后调用此方法
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "onBind");
return mMyBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "---onDestroy()---");
}
//这里执行任务
public class MyBinder extends Binder {
public void startTask() {
Log.i(TAG, "Task starts...");
}
}
}
2.假设我们要在一个Activity里通过点击一个按钮来绑定Service,那么我们要先在Activity里新建一个ServiceConnection的实例。下面代码是在Activity里抽取出来的。
private TestService.MyBinder mMyBinder;
/**
* 服务连接
*/
private ServiceConnection mServiceConnection = new ServiceConnection() {
/**
* 当服务连接后 返回binder
* @param name
* @param binder
*/
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mMyBinder = (TestService.MyBinder) binder;
mMyBinder.startTask();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
3.这样我们在Activity里点击一个按钮来绑定Service的时候,可以调用BindService来完成,详细如下图。
启动后的Service可以用stopService来停止服务,绑定后的Service可以用unbindService来取消绑定。
Service用法小结
上面的基本用法其实展示了Service最基本的用法,包括启动Service,停止Service,绑定Service以及取消绑定Service,Service里的逻辑很简单,实际上在平时的开发过程中,在后台执行时应该会比较复杂,考虑的东西也比较多。
启动Service的逻辑其实比较简单,就是新建一个Intent对象,然后调用startService方法把对象传进去,之后就可以启动了,停止的时候也是传一个Intent对象给stopService方法。
绑定Service的话逻辑较为复杂,这里我们是将Activity与Service建立绑定。我们可以看到几点注意事项:
(1) 首先是在Service里,我们定义了一个继承Binder的子类,用来执行任务,Binder我觉得可以理解为"粘合剂",我们可以理解为它是其它组件和Service的之间的结合物,之后实例化这个Binder并在回调方法里返回这个Binder给绑定Service的组件,就可以通过它让组件控制Service了。
(2) 然后是在Activity里实例化了一个ServiceConnection,并重写了onServiceConnected方法,这个方法会在建立连接后返回Service的Binder,然后通过Binder执行Service里的任务。
(3) 绑定Service的时候,调用bindService方法,这里传入三个参数,一个是Intent对象,说明要与哪个Service绑定,一个是刚才的ServiceConnection实例,第三个参数是常量,我们这里传入BIND_AUTO_CREATE,表示绑定时是否自动创建Service。销毁绑定的Service的时候只需要在使用unbindService方法时传入参数ServiceConnection实例即可。
如果既点击了Start Service按钮,又点击了Bind Service按钮,这样的话不管是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。
三、Service生命周期
Service的生命周期比Activity的简单很多,但是更加要引起重视,毕竟Service可能是在用户不知情的情况下在后台运行的。下面这张图很清楚地为我们展示了两种不同的方式,Service的生命周期。
看这张图,我们可以初步对Service之前的几个回调方法有所了解,启动和绑定Service都会调用onCreate()方法来创建Service,如果是启动的话会调用onStartCommand()方法,如果是绑定的话会调用onBind()方法,当启动的Service停止后会调用onDestroy()方法销毁,绑定的Service在全部都解除绑定后调用onUnbind()方法,最后调用onDestroy()方法销毁。
四、Service的几点说明
A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.Service不是一个单独的进程,Service与它所在应用位于同一进程中。
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).Service不是一个线程,所以为了避免ANR错误(用户无响应),我们应该在Service里开启新的线程来处理耗时任务。
我们可以看到Service只是一个在后台运行的没有用户界面的组件,使用它做些耗时的任务时,是需要开启新线程的。这里如果有需要的话,可以详细去了解下IntentService这个类,它可以很方便地处理一些多线程问题。
五、IntentService
Service这个组件默认情况下仍然运行在应用的主线程中,所以如果我们要执行耗时或者阻塞操作,那么为了避免ANR,应该要在Service内创建一个新的线程。IntentService是Service类的一个子类,用来处理异步请求。
下面是一个IntentService使用示例:
//实现倒计时5秒的服务
public class MyIntentService extends IntentService {
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public MyIntentService() {
super("count down 5");
}
@Override
public void onCreate() {
super.onCreate();
Log.e("ssssss", "倒计时服务创建...");
}
@Override
protected void onHandleIntent(Intent intent) {
long endTime = System.currentTimeMillis() + 5 * 1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
} finally {
Log.e("ssssss", "倒计时结束...");
}
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("ssssss", "倒计时服务销毁...");
}
}
与普通Service一样,我们可以通过startService(Intent)方法传递请求给IntentService。通过查看源码可知,IntentService在onCreate()函数中通过HandlerThread单独开启一个线程来处理所有Intent请求对象所对应的任务,这样以免事务处理阻塞主线程。执行完所一个Intent请求对象所对应的工作之后,如果没有新的Intent请求达到,则自动停止Service;否则执行下一个Intent请求所对应的任务。
IntentService详解
官方建议我们使用IntentService来完成异步请求的处理,那么IntentService内部具体都做了哪些东西呢?大概如下:
创建一个独立于应用主线程的新的线程来执行所有传递给onStartCommand()方法中的请求。
创建一个工作队列,一次传递一个请求到onHandleIntent()方法中。
当所有请求处理完成后,停止服务,不需要我们手动调用stopSelf()。
提供onBind()方法的默认实现,并且返回空。
帮我们实现了将请求从onStartCommand()传递到工作队列,然后传递到onHandleIntent()。
为了对比地看出Service和IntentService在使用上的区别,下面展示一个使用Service的例子:
public class HelloService extends Service {
private Looper mServiceLooper;
private ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
long endTime = System.currentTimeMillis() + 5*1000;
while (System.currentTimeMillis() < endTime) {
synchronized (this) {
try {
wait(endTime - System.currentTimeMillis());
} catch (Exception e) {
}
}
}
// Stop the service using the startId, so that we don't stop
// the service in the middle of handling another job
stopSelf(msg.arg1);
}
}
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we're stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
}
}
六、相关参考
Service基本使用
Android Service完全解析,关于服务你所需知道的一切(上)Service高级用法
Android Service完全解析,关于服务你所需知道的一切(下)