Service(服务)是一个一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可由其他应用组件启动(如 Activity),服务一旦被启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。例如,服务可以处理网络事物、播放音乐、执行文件 I/O 或与内容提供程序交互,而这一切均可在后台进行。
Service 基本上分为两种形式:
无论应用处于启动状态还是绑定状态,抑或处于启动并且绑定状态,任何应用组件均可像使用 Activity 那样通过调用 Intent 来使用服务(即使此服务来自另外一个应用)。不过,可以通过清单文件将服务声明为私有服务,并阻止其他应用访问。
通过创建 Service 子类(或使用它的一个现有子类)来创建服务。在现实中,需要重写一些回调方法,已处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务。
onStartCommand()
当另一个组件(如 Activity)通过调用 startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。在服务工作完成后,需要通过调用 stopSelf() 或 stopService() 来停止服务。(如果只提供绑定服务,则无需实现此方法)。
onBind()
当另一个组件香通过调用 bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的现实中,您必须通过返回 IBinder 提供一个接口,提供客户端用来与服务进行通信。请务必实现此方法,但如果并不希望允许绑定,则应返回 null。
onCreate()
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用 onStartCommand() 或 onBind() 之前)。如果服务已经在运行,则不会调用此方法。
onDestroy()
当服务不在使用且当被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的监听器、接收器等。这是服务接收的最后一个调用。
如同 Activity(以及其他组件)一样,必须在应用的清单文件中声明所有服务。
要声明服务,请添加元素作为元素的子元素。例如:
- <manifest ...>
- ...
- <application ...>
- <service android:name=".ExampleService"
- android:enabled=["true" | "false"]
- android:exported=["true" | "false"]
- android:isolatedProcess=["true" | "false"]
- android:icon="drawable resource"
- android:lable="string resource"
- android:name="string"
- android:permission="string"
- android:process="string">
- ...
- </service>
- </application>
- </manifest>
- Intent intent = new Intent(this, TestService.class);
- startService(intent);
onStartCommand(Intent intent, int flag, int startId)
实际上 onStartCommand 的返回值 int 类型才是最最值得注意的,它有三种可选值,START_STICKY,START_NOT_STICKY,START_REDELIVER_INTENT,它们具体含义如下:
由于每次启动服务(调用 startService)时,onStartCommand 方法都会被调用,因此我们可以通过该方法使用 Intent 给 Service 传递所需要的参数,然后在 onStartCommand 方法中处理的事件,最后根据需求选择不同的 Flag 返回值,以达到对程序更友好的控制。
绑定服务是 Service 类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现 onBind() 回调方法。该方法返回的 IBinder 对象定义了客户端用来与服务进行交互的编程接口。
绑定到已启动服务
正如服务文档中所述,您可以创建同时具有已启动和绑定两种状态的服务。 也就是说,可通过调用 startService() 启动该服务,让服务无限期运行;此外,还可通过调用 bindService() 使客户端绑定到服务。
如果您确实允许服务同时具有已启动和绑定状态,则服务启动后,系统 "不会" 在所有客户端都取消绑定时销毁服务。 为此,您必须通过调用 stopSelf() 或 stopService() 显式停止服务。
尽管您通常应该实现 onBind() 或 onStartCommand(),但有时需要同时实现这两者。例如,音乐播放器可能发现让其服务无限期运行并同时提供绑定很有用处。 这样一来,Activity 便可启动服务进行音乐播放,即使用户离开应用,音乐播放也不会停止。 然后,当用户返回应用时,Activity 可绑定到服务,重新获得回放控制权。
请务必阅读管理绑定服务的生命周期部分,详细了解有关为已启动服务添加绑定时该服务的生命周期信息。
客户端可通过调用 bindService() 绑定到服务。调用时,它必须提供 ServiceConnection 的实现,后者会监控与服务的连接。bindService() 方法会立即无值返回,但当 Android 系统创建客户端与服务之间的连接时,会对 ServiceConnection 调用 onServiceConnected(),向客户端传递用来与服务通信的 IBinder。
多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的 onBind() 方法来检索 IBinder。系统随后无需再次调用 onBind(),便可将同一 IBinder 传递至任何其他绑定的客户端。
当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非 startService() 也启动了该服务)。
实际上我们必须提供一个 IBinder 接口的实现类,该类用以提供客户端用来与服务进行交互的编程接口,该接口可以通过三种方法定义接口:
前面描述过,如果我们的服务仅供本地应用使用,不需要跨进程工作,则可以实现自有 Binder 类,让客户端通过该类直接访问服务中的公共方法。其使用开发步骤如下
注意:
扩展 Binder 类实现的 Service 端
- private LocalBinder binder = new LocalBinder();
- /**
- *创建Binder对象,返回给客户端即Activity使用,提供数据交换接口
- */
- public class LocalBinder extends Binder{
- //声明当前对象this
- return LocalService.this;
- }
- /**
- *把Binder类返回给客户端
- */
- @Nullable
- @Override
- public IBinder onBind(Intent intent){
- return binder;
- }
扩展 Binder 类客户端实现
- /**
- *ServiceConnection代表与服务的链接,它只有两个方法,
- *onServiceConnected和onServiceDisconnected,
- *前者是在操作者的链接一个服务成功时被调用,而后者是在服务崩溃或者被杀死导致的连接中断时被调用
- */
- private ServiceConnection conn;
- private LocalService mService;
- conn = new ServiceConnection(){
- @Override
- public void onServiceConnected(ComponentName name, IBinder binder){
- //获取Binder
- LocalService.LocalBinder binder = (LocalService.LocalBinder) binder;
- mService= binder.getService();
- }
- @Override
- public void onServiceDisconnected(ComponentName name){
- mService= null;
- }
- }
- //开启绑定
- Intent intent = new Intent(this, LocalService.class);
- bindService(intent, conn, Service.BIND_AUTO_CREATE);
如需让服务与远程进程通信,则可使用 Messenger 为您的服务提供接口。利用此方法,您无需使用 AIDL 便可执行进程间通信 (IPC)。
以下是 Messenger 的使用方法摘要:
这样,客户端并没有调用服务的 "方法"。而客户端传递的 "消息"(Messenger 对象)是服务在其 Handler 中接收的。
- /** Command to the service to display a message */
- static final int MSG_SAY_HELLO = 1;
- /**
- * Handler of incoming messages from clients.
- */
- class IncomingHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_SAY_HELLO:
- Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
- break;
- default:
- super.handleMessage(msg);
- }
- }
- }
- /**
- * Target we publish for clients to send messages to IncomingHandler.
- */
- final Messenger mMessenger = new Messenger(new IncomingHandler());
- /**
- * When binding to the service, we return an interface to our messenger
- * for sending messages to the service.
- */
- @Override
- public IBinder onBind(Intent intent) {
- Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
- return mMessenger.getBinder();
- }
请注意,服务就是在 Handler 的 handleMessage() 方法中接收传入的 Message,并根据 what 成员决定下一步操作。
客户端只需根据服务返回的 IBinder 创建一个 Messenger,然后利用 send() 发送一条消息。例如,以下就是一个绑定到服务并向服务传递 MSG_SAY_HELLO 消息的简单 Activity:
- /** Messenger for communicating with the service. */
- Messenger mService = null;
- /** Flag indicating whether we have called bind on the service. */
- boolean mBound;
- /**
- * Class for interacting with the main interface of the service.
- */
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- // This is called when the connection with the service has been
- // established, giving us the object we can use to
- // interact with the service. We are communicating with the
- // service using a Messenger, so here we get a client-side
- // representation of that from the raw IBinder object.
- mService = new Messenger(service);
- mBound = true;
- }
- public void onServiceDisconnected(ComponentName className) {
- // This is called when the connection with the service has been
- // unexpectedly disconnected -- that is, its process crashed.
- mService = null;
- mBound = false;
- }
- };
- public void sayHello(View v) {
- if (!mBound) return;
- // Create and send a message to the service, using a supported 'what' value
- Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
- try {
- mService.send(msg);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
此示例并未说明服务如何对客户端作出响应。如果您想让服务作出响应,则还需要在客户端中创建一个 Messenger。然后,当客户端收到 onServiceConnected() 回调时,会向服务发送一条 Message,并在其 send() 方法的 replyTo 参数中包含客户端的 Messenger。
Messenger 方式进行进程间通信的原理图:
当服务与所有客户端之间的绑定全部取消时,Android 系统便会销毁服务(除非还使用 onStartCommand() 启动了该服务)。因此,如果是纯粹的绑定服务,则无需对其生命周期进行管理——Android 系统会根据它是否绑定到任何客户端代管理。
如果选择实现 onStartCommand() 回调方法,则必须显式停止五福,因为系统现在已将服务视为已启动。在此情况下,服务将一直运行到其通过 stopSelf() 自行停止,或其他组件调用 stopService() 为止,无论其是否绑定到任何客户端。
此外,如果服务启动并接受绑定,当系统调用 onUnbind() 方法时,如果在客户端下一次绑定到服务时接收 onRebind() 调用,则可选择返回 true。onRebind() 返回空值,但客户端仍在其 onServiceConnected() 回调中接收 IBinder。
绑定服务生命周期的逻辑图:
虽然服务的状态有启动和绑定两种,但实际上一个服务可以同时是这两种状态,也就是说,它既可以是启动服务(以无限期运行),也可以是绑定服务。有点需要注意的是 Android 系统仅会为一个 Service 创建一个实例对象,所以不管是启动服务还是绑定服务,操作的是同一个 Service 实例,而且由于绑定服务或者启动服务执行顺序问题将会出现以下两种情况:
以上两种情况显示出启动服务的优先级确实比绑定服务高一些。不过无论 Service 是处于启动状态还是绑定状态,或处于启动并且绑定状态,我们都可以像使用 Activity 那样通过调用 Intent 来使用服务 (即使此服务来自另一应用)。 当然,我们也可以通过清单文件将服务声明为私有服务,阻止其他应用访问。最后这里有点需要特殊说明一下的,由于服务在其托管进程的主线程中运行(UI 线程),它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。 这意味着,如果服务将执行任何耗时事件或阻止性操作(例如 MP3 播放或联网)时,则应在服务内创建新线程来完成这项工作,简而言之,耗时操作应该另起线程执行。只有通过使用单独的线程,才可以降低发生 "应用无响应"(ANR) 错误的风险,这样应用的主线程才能专注于用户与 Activity 之间的交互, 以达到更好的用户体验。
服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
?? 无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。
?? 服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。
?? 对于启动服务,有效生命周期与整个生命周期同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在 onUnbind() 返回时结束。
虽然可以通过以上两种情况管理服务的生命周期,但是我们还必须考虑另外一种情况,也就是启动服务与绑定服务的结合体,也就是说,我们可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则 stopService() 或 stopSelf() 不会真正停止服务。因此在这种情况下我们需要特别注意。
既然有隐式启动,那么就会有显示启动,那就先来了解一下什么是隐式启动和显示启动。
- //显式启动
- Intent intent = new Intent(this, ForegroundService.class);
- startService(intent);
- final Intent serviceIntent = new Intent();
- serviceIntent.setAction("com.android.ForegroundService");
- startService(serviceIntent);
主要原因我们可以从源码中找到,这里看看 Android 4.4 的 ContextImpl 源码中的 validateServiceIntent(Intent service), 可知如果启动 service 的 intent 的 component 和 package 都为空并且版本大于 KITKAT 的时候只是报出一个警报, 告诉开发者隐式声明 intent 去启动 Service 是不安全的.
而在 android5.0 之后呢?我们这里看的是 android6.0 的源码如下
从源码可以看出如果启动 service 的 intent 的 component 和 package 都为空并且版本大于 LOLLIPOP(5.0) 的时候, 直接抛出异常,该异常与之前隐式启动所报的异常时一致的。那么该如何解决呢?
- final Intent serviceIntent = new Intent();
- serviceIntent.setAction("com.android.ForegroundService);
- serviceIntent.setPackage(getPackageName());//设置应用包名
- startService(serviceIntent);
- public static Intent getExplicitIntent(Context context, Intent implicitIntent) {
- // Retrieve all services that can match the given intent
- PackageManager pm = context.getPackageManager();
- List resolveInfo = pm.queryIntentServices(implicitIntent, 0);
- // Make sure only one match was found
- if (resolveInfo == null || resolveInfo.size() != 1) {
- return null;
- }
- // Get component info and create ComponentName
- ResolveInfo serviceInfo = resolveInfo.get(0);
- String packageName = serviceInfo.serviceInfo.packageName;
- String className = serviceInfo.serviceInfo.name;
- ComponentName component = new ComponentName(packageName, className);
- // Create a new intent. Use the old one for extras and such reuse
- Intent explicitIntent = new Intent(implicitIntent);
- // Set the component to be explicit
- explicitIntent.setComponent(component);
- return explicitIntent;
- }
- Intent mIntent=new Intent();//辅助Intent
- mIntent.setAction("com.android.ForegroundService");
- final Intent serviceIntent=new Intent(getExplicitIntent(this,mIntent));
- startService(serviceIntent);
来源: http://www.bubuko.com/infodetail-1870106.html