Service

Service

Service是Android应用程序的基本组件之一。跟Activity不同,Service一般没有提供用户界面,在后台运行。在实际的开发过程中,我们可以将一些任务(例如:从网络上下载文件、播放音乐等)封装到Service中,这样有几个好处:1)减少模块之间的耦合度;2)Service可以被外部应用程序调用,实现进程间通信。

Service启动后运行在宿主进程的主线程,Service不会创建自己的线程或者进程(除非你特别声明)。如果Service执行的任务耗时较长,阻塞到主线程,你需要在Service中创建一个线程,在新创建的线程中执行这些任务。阻塞主线程会造成用户界面僵死,影响用户体验,阻塞时间超过5秒,系统就会弹出ANR错误对话框。

组件启动一个Service后,Service将一直在后台运行,直到组件去终止Service或者Service自行退出。系统只会在内存资源不足的情况下,才可能杀死运行中的Service。

Service的运行模式

1)  Started模式

Started模式是组件调用startService(Intent)来启动Service,startService接受一个Intent对象。

下面是通过startService启动本地Service的例子。在Intent构造函数第二个参数中指定Service的class类型,第一个参数是上下文对象,在Activity启动Service时,第一个参数传入this。

Intent intent = new Intent(this, HelloService.class);
startService(intent);

在你创建的Service类中重写onStartCommand()方法。startService调用后,在Service类的内部,onStartCommand()回调函数将被调用,在onStartCommand()中,你可以根据传入的Intent值,执行不同的处理操作。

1 @Override
2 public int onStartCommand(Intent intent, int flags, int startId) {
3 handleCommand(intent);
4
5 // We want this service to continue running until it is explicitly
6 // stopped, so return sticky.
7
8 return START_STICKY;
9 }

如果你要启动远程的Service,Intent构造函数传入Intent动作(Action)的字符串,系统会查找已经安装的Service,启动满足条件的Service。

1 Intent it = new Intent("com.tony.han.TEST_SERVICE");
2
3 startService(it);

2)  Bound模式

Bound模式是外部组件(比如Activity)调用bindService(Intent, ServiceConnection, int)来启动Service,bindeService返回一个IBinder对象,外部组件使用这个IBinder对象与Service通信。由于IBinder特性,外部调用组件和Service组件可以存在于不同的进程中。

下面是使用bindeService启动Service的例子。Intent对象的构造与startService方式相同。

1 // Bind to LocalService
2
3 Intent intent = new Intent(this, LocalService.class);
4
5 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

在你定义的Service类中重写onBind()方法。bindService调用后,在Service类的内部,onBind()回调函数将被调用。

 1 // Binder given to clients
2 public class LocalBinder extends Binder {
3 LocalService getService() {
4 return LocalService.this;
5 }
6 }
7
8 @Override
9 public IBinder onBind(Intent intent) {
10 return mBinder;
11 }

客户端使用Bound模式的Service还需要实现ServiceConnection接口。bindService调用成功后,IBinder对象会通过ServiceConnection的回调函数onServiceConnected()传递给客户端,客户端程序将IBinder对象保存起来,后续就可以通过这个IBinder对象与Service通信。

 1     private ServiceConnection mConnection = new ServiceConnection() {
2 @Override
3 public void onServiceConnected(ComponentName className, IBinder service) {
4 LocalBinder binder = (LocalBinder) service;
5 mService = binder.getService();
6 mBound = true;
7 }
8 @Override
9 public void onServiceDisconnected(ComponentName arg)
10 {
11 mBound = false;
12 }
13 };

在你实现Service时,如果不想让客户端通过Bound模式与你的Service通信,在重写onBind()方法时返回null值。

3)  Started和Bound混合模式

Service可以同时以Started和Bound模式运行。外部组件调用了startService(Intent)启动Service后,也可以同时调用bindService()获取IBinder对象,与正在运行的Service通信,只要Service内部实现了onStartCommand()和onBind()回调函数。

创建Service的步骤

1)  创建一个类,继承Service类或者Service的子类

2)  重写一些必要的回调函数(如onStartCommand(), onBind()等)

3)  在AndroidManifest.xml文件的<Application>元素下添加<Service>子元素,在android:name属性指定Service的名字

如果外部组件要通过Intent动作来启动Service,需要在AndroidManifest.xml文件中为Service添加<intent-filter>。添加了<intent-filter>后,外部应用程序就可以通过指定Intent动作名称来启动这个Service,也就是说,Service对外可见。

<service android:name=".Service0325">
<intent-filter>
<action android:name="com.tony.han.TEST_SERVICE"></action>
</intent-filter>
</service>

如果你不想外部应用程序使用你的Service,可以添加android:exportd属性

<service android:name=".Service0325" android:exported=”false”>
</service>

关于onCreate()和onDestroy()方法

onCreate()

Service在创建时会被调用一次,onCreate()方法的调用发生在onStartCommand()或onBind()钱。因此,可以在onCreate()做初始化的操作(如创建Service的工作线程等)。另外,如果Service已经在运行,另一组件启动这个Service时,onCreate()不会被调用。

onDestroy()

在Service销毁前被调用一次,与onCreate()对应。资源清理的工作(如终止Service的工作线程)可以在此方法中完成。

停止Service的运行

  跟Activity不同的是,Service需要自己管理其生命周期。在Started模式中,停止Service运行的方法是由Service自己调用stopSelf()/stopSelfResult(int)方法或者其他组件调用stopService()方法;在Bound模式中,当只有一个组件绑定了Service,调用unbindService后,Service被销毁,当多个外部组件同时绑定Service时,最后一个组件调用了unbindService()后,Service才被销毁。

  Service以Started模式启动后,只有Service自己调用stopSelf()/stopSelfResult(int)方法或者其他组件调用stopService()方法后,Service才会终止运行。也就是说,在Started和Bound混合模式下,假设组件A是以Started模式启动的Service,组件B以Bound模式绑定到Service上,当组件B调用unBindService()后,Service不会销毁。只有组件A调用了stopService()或Service自己调用了stopSelf()/stopSelfResult(int)后,Service才会销毁。

  多个组件以Started模式启动同一个Service,onStartCommand()会被调用多次,但是只有一个Service实例存在,只需要调用一次stopService()或者stopSelf(),Service就会停止运行。

关于IntentService

         在Service中创建一个线程执行耗时较长的操作,这样可以解决Service阻塞主线程的问题。引入了多线程,你需要编码时解决线程间同步的问题。Android提供了IntentService类用于简化开发多线程的Service的难度。

         IntentService是Service的子类,IntentService内部创建了一个工作线程来处理用户的请求(Intent),来自客户端的请求保存在队列中,工作线程每次从队列取出一个请求进行处理,当队列中的请求都处理完后,IntentService自动调用stopSelf()终止Service。

         IntentService在onHandleIntent(Intent)方法中处理用户的请求,onHandleIntent(Intent)在工作线程中调用。下面是使用IntentService实现Service的例子,我们只需要重写onHandleIntent(Intent)方法。

 1 public class HelloIntentService extends IntentService {
2 /**
3 * A constructor is required, and must call the super IntentService(String)
4 * constructor with a name for the worker thread.
5
*/
6
7 public HelloIntentService() {
8 super("HelloIntentService");
9 }
10
11 /**
12 * The IntentService calls this method from the default worker thread with
13 * the intent that started the service. When this method returns, IntentService
14 * stops the service, as appropriate.
15
*/
16
17 @Override
18 protected void onHandleIntent(Intent intent) {
19 // Normally we would do some work here, like download a file.
20 // For our sample, we just sleep for 5 seconds.
21 long endTime = System.currentTimeMillis() + 5*1000;
22 while (System.currentTimeMillis() < endTime) {
23 synchronized (this) {
24 try {
25 wait(endTime - System.currentTimeMillis());
26 } catch (Exception e) {
27 }
28 }
29 }
30 }
31 }

使用IntentService,我们一般情况下只需要重写IntentService中定义的onHandleIntent(Intent)方法即可。如果你想重写其他方法(如onCreate(),onStartCommand()等),在重写的方法中需要调用父类的方法。

1 @Override
2 public int onStartCommand(Intent intent, int flags, int startId) {
3 Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
4 return super.onStartCommand(intent,flags,startId);
5 }

关于onStartCommand()的返回值

         onStartCommand()返回int类型的数据,返回值告诉系统当Service被杀死后,系统应当怎样处理。

onStartCommand()返回值的意义:

START_NOT_STICKY

当Service被杀死后,系统不会重启Service。Service只在需要时才会启动。

START_STICKY

当Service被杀死后,系统自动重启Service,调用Service的onStartCommand(),传递Intent为null。因为传入的Intent为null,重新启动后的Service什么也不做,仅仅是等待下一次客户端的Intent。这种情况的例子是媒体播放器服务。

START_REDELIVER_INTENT

当Service被杀死后,系统自动重启Service,调用Service的onStartCommand()时传递被杀死前正在处理的Intent。也就是说,Service重启后继续上次的任务。这种情况的例子是下载文件服务。

在实际的开发中,我们需要根据业务需要来决定onStartCommand()的返回值。

关于Bound模式的IBinder对象

IBinder定义了远程对象的接口,是Android系统实现轻量级的、高效率的进程间通信的核心部分。这个接口定义了本地对象与远程对象进行通信的抽象协议,本地对象与远程对象可能在不同的进程中。一般情况下,我们实现IBinder接口时不用直接继承IBinder,继承Binder类就可以了。Binder类实现了IBinder接口。为了叙述方面,我们把实现了IBinder接口的对象简称为IBinder对象。简单的说,应用程序利用IBinder对象与远程对象通信。

在前面我们已经说过,实现Bound模式的Service的关键是实现onBind()方法,onBind()方法要返回一个IBinder对象,问题的关键是怎么实现IBinder对象。

Android为我们提供了三种方法实现IBinder对象。

1)  继承Binder类

如果你开发的应用程序客户端和Service在同一进程中,不涉及跨进程通信。你可以定义一个类继承Binder类,在onBind()方法中返回这个类的实例。前面说过,Binder实现了进程间通信,这里使用Binder是否合适?Binder能够实现进程间通信,进程内的通信更不是问题。

这种方法是推荐的方式,除非你需要实现进程间通信。

2)  使用Messenger

如果你开发的应用程序客户端与Service在不同进程,可以使用Messenger。Service中实现Handler类,Handler中处理客户端发来的Message,Messenger与Handler关联。onBind()方法返回的IBinder对象由Messenger提供。

3)  使用AIDL

利用AIDL定义Service的接口,通过Android SDK的相应工具为你自动生成Binder类。前面介绍的Messenger与AIDL方式本质上是相同的。利用AIDL更加灵活,也更加复杂。本文不对AIDL方式详细说明。

下面是继承Binder类实现Service的实例代码。定义LocalBinder类继承Binder,在LocalBinder中定义一个接口getService(),getService()直接返回Service的引用。在onBind()方法中直接返回LocalBinder对象的引用。

 1 public class LocalService extends Service {
2 // Binder given to clients
3 private final IBinder mBinder = new LocalBinder();
4 // Random number generator
5 private final Random mGenerator = new Random();
6
7
8 /**
9 * Class used for the client Binder. Because we know this service always
10 * runs in the same process as its clients, we don't need to deal with IPC.
11 */
12 public class LocalBinder extends Binder {
13 LocalService getService() {
14 // Return this instance of LocalService so clients can call public methods
15 return LocalService.this;
16 }
17 }
18
19
20 @Override
21 public IBinder onBind(Intent intent) {
22 return mBinder;
23 }
24
25
26 /** method for clients */
27 public int getRandomNumber() {
28 return mGenerator.nextInt(100);
29 }
30 }

由于客户端与Service在同一进程中,客户端在ServiceConnection的回调函数中获得IBinder对象后,强制转换成LocalBinder对象,就可以调用LocalBinder获得Service对象。

 1 /** Defines callbacks for service binding, passed to bindService() */
2 private ServiceConnection mConnection = new ServiceConnection() {
3
4 @Override
5 public void onServiceConnected(ComponentName className, IBinder service) {
6 // We've bound to LocalService, cast the IBinder and get LocalService instance
7 LocalBinder binder = (LocalBinder) service;
8 mService = binder.getService();
9 mBound = true;
10 }
11
12 @Override
13 public void onServiceDisconnected(ComponentName arg0) {
14 mBound = false;
15 }
16 };

下面是使用Messenger的例子。在onBind()方法中,通过调用mMessenger.getBinder()获得IBinder对象。

 1 public class MessengerService extends Service {
2 /** Command to the service to display a message */
3 static final int MSG_SAY_HELLO = 1;
4
5
6 /**
7 * Handler of incoming messages from clients.
8 */
9 class IncomingHandler extends Handler {
10 @Override
11 public void handleMessage(Message msg) {
12 switch (msg.what) {
13 case MSG_SAY_HELLO:
14 Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
15 break;
16 default:
17 super.handleMessage(msg);
18 }
19 }
20 }
21
22 /**
23 * Target we publish for clients to send messages to IncomingHandler.
24 */
25 final Messenger mMessenger = new Messenger(new IncomingHandler());
26
27 /**
28 * When binding to the service, we return an interface to our messenger
29 * for sending messages to the service.
30 */
31 @Override
32 public IBinder onBind(Intent intent) {
33 Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
34 return mMessenger.getBinder();
35 }
36 }

在客户端的代码中,利用得到的IBinder对象创建Messenger对象,客户端利用Messenger与Service进行通信。

 1 private ServiceConnection mConnection = new ServiceConnection() {
2 public void onServiceConnected(ComponentName className, IBinder service) {
3 // This is called when the connection with the service has been
4 // established, giving us the object we can use to
5 // interact with the service. We are communicating with the
6 // service using a Messenger, so here we get a client-side
7 // representation of that from the raw IBinder object.
8 mService = new Messenger(service);
9 mBound = true;
10 }
11
12 public void onServiceDisconnected(ComponentName className) {
13 // This is called when the connection with the service has been
14 // unexpectedly disconnected -- that is, its process crashed.
15 mService = null;
16 mBound = false;
17 }
18 };
原文地址:https://www.cnblogs.com/tonyhan/p/2416782.html