最近在看android开发指南,感觉有些细节知识还是挺耐人寻味的,特在此记录一下:
一、IntentService
这是一个Service的子类,该子类使用线程处理所有启动请求,一次一个.这是不使用服务处理多任务请求的最佳选择.你需要做的只是实现onHandleIntent()方法即可.可以为每个启动请求接收到intent,放到后台工作即可.
需要注意一点的是,IntentService所有的请求被处理后服务停止,所以你永远都不必调用stopSelf()函数.参考IntentService源码中的方法:
@Override public void handleMessage(Message msg) { onHandleIntent((Intent)msg.obj); stopSelf(msg.arg1); }
二、onStartCommand的返回值
这个可能大家遇到的比较多,再说一下吧,onStartCommand()方法必须返回一个整型变量.这个变量描述可能已被系统停止的服务,如果被停止,系统会接着继续服务,返回值必须是如下几个常量之一:
- START_NOT_STICKY
如果系统在onStartCommand()返回后停止服务,系统不会重新创建服务,除非有等待的意图需要处理.如果想避免运行不需要的服务,或者让应用可以轻松的启动未完成的任务,这是一个安全的选择。
- START_STICKY
如果系统在onStartCommand()返回后停止服务,系统重新创建服务并且调用onStartCommand()函数,但是不会传送最后一个意图。相反,系统用一个空意图调用onStartCommand(),除非还有想启动服务的意图,这种情况下,这些意图会被传递.这很适合多媒体播放的情况(或类似服务),这种情况下服务不执行命令,但是会无限运行下去,等待处理任务。
- START_REDELIVER_INTENT
如果系统在onStartCommand()返回后停止服务,系统使用用最后传递给service的意图,重新创建服务并且调用onStartCommand()方法, 任何未被处理的意图会接着循环处理。所以用服务处理像下载之类的可能需要立即重启的任务,非常合适。
三、停止服务
一旦调用了stopSelf()或stopService(),系统会尽快销毁服务。
但是,当前如果服务在onStartCommand()方法中同时处理多个请求,那么当你正在处理这样的启动请求时,肯定不应该停止服务,因为很有可能在停止服务时刚好收到一个新的请求(在第一个请求的结尾停止服务会终止第二个请求).
要避免这个问题,你可以使用stopSelf(int)方法确保服务被停止,当调用stopSelf(int),你需要传递给请求启动的ID(这个ID传递给onStartCommand()方法)给对应的服务.如果在可以调用stopSelf(int)之前,服务接收到一个新的启动请求,服务就不会匹配,也就不会停止。
四、绑定服务
当创建一个提供了绑定的服务时,你必须提供一个IBinder来提供程序接口,用户可以用这个接口与服务交互。有三种方法可以用来定义这个接口:
- 扩展binder类
如果你的服务只对你自己的应用程序私用,并且作为客户在同一个进程中运行(这种情况很常见),你应该通过扩展Binder类来创建你的接口并且从onBind()运行一个实例。客户接收Binder并直接使用它来接入Binder实现,甚至Service中可用的公共方法。
// binder given to clients private final IBinder mBinder =new LocalBinder(); public class LocalBinder extends Binder { LocalService getService() { return LocalService.this; } }
当服务对于你的应用程序几乎是运行于后台时,这是首选技术。这种情况下,唯一一个你不建立自己接口的原因是你的服务被其他应用程序所用或使用了多个分开的进程。
- 使用一个消息传递器Messenger
如果你想让你的接口在多个不同的进程间工作,你可以为服务创建一个带有Messenger的接口。在这种情况下,服务将定义一个Handler来响应不同类型的Message对象。这个Handler是Messenger的基础,它可以与客户共享一个IBinder,允许客户使用Message对象向服务发送指令。以此,客户可以定义一个属于自己的Messenger,这样,服务就可以把消息传递回来。
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); break; } } } // messenger is a reference of handler final Messenger mMessenger = new Messenger(new IncomingHandler()); @Override public IBinder onBind(Intent intent) { Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show(); return mMessenger.getBinder(); }
这是最简单的执行进种间通信(IPC)的方法,因为Messenger队列所有的请求到一个单独的线程当中,所以你不需要设计你的服务为线程安全(thread-safe)
- 使用AIDL
AIDL(Android接口定义语言-Android Interface Definition Language)执行了把一个对象分解到操作系统能理解的基元,并安排它们到各个进程间来完成IPC等所有工作。前文中提到的技术,使用一个Messenger(消息传送器)就是基于AIDL和其下面的结构。如前所述,Messenger创建了一个队列,把所有的请求都放在一个线程中,所以服务一次只接收一个请求。然而,如果想你的服务同时接收多个请求,那么你可以直接创建AIDL。在这种情况下,你的服务必须有能力执行多个线程,并且为线程安全。
注意:只有您允许来自不同应用的客户端访问您的IPC服务并且您希望在服务中处理多线程,使用AIDL才是必要的。如果您不需要使用并发的IPC访问不同的应用,您应该通过继承Binder来创建您的接口,或者,如果您确实需要使用IPC,但是不需要处理多线程,那请继承Messenger来实现您的接口。总之,在实现一个AIDL之前,请确保您已经理解了以上绑定服务。
@Override public IBinder onBind(Intent intent) { return mBinder; } private final IRemoteService.Stub mBinder = new IRemoteService.Stub() { public void registerCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.register(cb); } public void unregisterCallback(IRemoteServiceCallback cb) { if (cb != null) mCallbacks.unregister(cb); } };
这三种绑定服务的Demo地址:https://github.com/xuwt/BoundService
最后说一下,aidl文件中如果引用了一个parcelable对象,需要在aidl文件中体现出来,如
package com.xuwt.boundservice; parcelable Person;