可以看到,这里注册的就和我们原先注册的有一些区别了,主要是因为我们在这里要跨进程通信,所以在另外一个进程里面并没有我们的service的实例,此时必须要给其他的进程一个标志,这样才能让其他的进程找到我们的service。讲道理,其实这里的android:exported属性不设置也可以的,因为在有intent-filter的情况下这个属性默认就是true,对这个有些遗忘的同学可以再去看一下上一篇博文: Android中的Service:默默的奉献者 (1) 。
//客户端public class ActivityMessenger extends Activity { static final int MSG_SAY_HELLO = 1; Messenger mService = null; boolean mBound; private ServiceConnection mConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { //接收onBind()传回来的IBinder,并用它构造Messenger mService = new Messenger(service); mBound = true; } public void onServiceDisconnected(ComponentName className) { mService = null; mBound = false; } }; //调用此方法时会发送信息给服务端 public void sayHello(View v) { if (!mBound) return; //发送一条信息给服务端 Message msg = Message.obtain(null, MSG_SAY_HELLO, 0, 0); try { mService.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override protected void onStart() { super.onStart(); //绑定服务端的服务,此处的action是service在Manifests文件里面声明的 Intent intent = new Intent(); intent.setAction("com.lypeer.messenger"); //不要忘记了包名,不写会报错 intent.setPackage("com.lypeer.ipcserver"); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // Unbind from the service if (mBound) { unbindService(mConnection); mBound = false; } }}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960客户端就主要是发起与服务端的绑定,以及通过onServiceConnected()方法来过去服务端返回来的IBinder,借此构造Messenger,从而可以通过发送Message的方式与服务端进行交互。上面的例子其实并不完整,因为它只有客户端对服务端单方面的通信,而服务端没有发信息给客户端的功能——这显然是不合理的。而要实现这个其实也很简单,只要客户端里也创建一个Handler实例,让它接收来自服务端的信息,同时让服务端在客户端给它发的请求完成了之后再给客户端发送一条信息即可。
用Messenger来进行IPC的话整体的流程是非常清晰的,Message在其中起到了一个信使的作用,通过它客户端与服务端的信息得以互通。
1.2.3,通过AIDL
AIDL,即Android Interface Definition Language,Android接口定义语言。它是一种IDL语言,可以拿来生成用于IPC的代码。在我看来,它其实就是一个模板。为什么这样说呢?在我们的使用中,实际上起作用的并不是我们写的AIDL代码,而是系统根据它生成的一个IInterface实例的代码。而如果大家多生成几个这样的实例,然后把它们拿来比较,你会发现它们都是有套路的——都是一样的流程,一样的结构,只是根据具体的AIDL文件的不同有细微的变动。所以其实AIDL就是为了避免我们一遍遍的写一些千篇一律的代码而出现的一个模板。
那么如何使用AIDL来通过bindService()进行线程间通信呢?基本上有下面这些步骤:
服务端创建一个AIDL文件,将暴露给客户端的接口在里面声明在service中实现这些接口客户端绑定服务端,并将onServiceConnected()得到的IBinder转为AIDL生成的IInterface实例通过得到的实例调用其暴露的方法上面的描述其实比较抽象,基本上是那种看了也不知道怎么做的类型——这个如果要展开讲的话就又是长篇大论的了。基于这种考虑,这里只是简单的介绍一下AIDL这个东西,它的具体的语法,到底怎么来实现IPC,我会单独写一篇博客来叙述这方面的东西。
1.2.4,Messenger与AIDL的比较
首先,在实现的难度上,肯定是Messenger要简单的多——至少不需要写AIDL文件了(虽然如果认真的究其本质,会发现它的底层实现还是AIDL)。另外,使用Messenger还有一个显著的好处是它会把所有的请求排入队列,因此你几乎可以不用担心多线程可能会带来的问题。
但是这样说来,难道AIDL进行IPC就一无是处了么?当然不是,如果是那样的话它早就被淘汰了。一方面是如果项目中有并发处理问题的需求,或者会有大量的并发请求,这个时候Messenger就不适用了——它的特性让它只能串行的解决请求。另外,我们在使用Messenger的时候只能通过Message来传递信息实现交互,但是在有些时候也许我们需要直接跨进程调用服务端的方法,这个时候又怎么办呢?只能使用AIDL。
所以,这两种IPC方式各有各的优点和缺点,具体使用哪种就看具体的需要了——当然,能使用简单的就尽量使用简单的吧。
1.2.5,service的生命周期
当服务与所有客户端之间的绑定全部取消时,Android 系统便会销毁这个服务(除非还使用 onStartCommand() 启动了该服务)。因此,如果服务是纯粹的绑定服务,原则上我们是无需对其生命周期进行管理的—Android 系统会根据它是否绑定到任何客户端帮我们管理。但实际上,我们应该始终在完成与服务的交互时或 Activity 暂停时取消绑定,以便服务能够在未被占用时关闭。
如果你只需要在 Activity 可见时与服务交互,则可以在 onStart() 期间绑定,在 onStop() 期间取消绑定。如果你希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。但是注意,这意味着你的 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当你提高该进程的权重时,系统终止该进程的可能性会增加。通常情况下,切勿在 Activity 的 onResume() 和 onPause() 期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,我们应该使发生在这些转换期间的处理保持在最低水平。此外,如果我们的应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一次绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务。
此外,如果我们的服务已启动并接受绑定,则当系统调用 onUnbind() 方法时,如果我们想在客户端下一次绑定到服务时接收 onRebind() 调用(而不是接收 onBind() 调用),则可选择返回 true。onRebind() 返回空值,但客户端仍在其 onServiceConnected() 回调中接收 IBinder。下图说明了这种生命周期的逻辑: 
2,什么时候用startService什么时候用bindService?
这个其实可以通过它们的特点很轻松的得到结论:它们之间的主要区别其实体现在两点,能否交互,以及生命周期。所以很显然的,startService适合那种启动之后不显式停止它就永远在后台运行,并且不需要客户端与服务端交互的service。比方说一条专门拿来存数据到本地数据库的service,它就一直在后台等着有别的组件startService,然后把拿到的数据存入数据库,这就显然是用startService做的事情。而bindService呢,就适合那种可以交互的,可以掌控它什么时候停什么时候开始的。另外,如果有IPC的需求,那当然bindService是必不可少的了。
我们在上一篇博文里讲过,其实在大多数情况下,startService和bindService都是相辅相成的,它们并不是孤立的存在。比方说我这个时候要做一个音乐播放器,那么后台播放是肯定要的吧?总不能手机一熄屏音乐也没了。另外,控制音乐也是要的吧?什么上一首下一首播放暂停什么的。这不就强强联合了么?当然要注意的是,在这两种启动方式同时存在去启动一个service的时候,service的生命周期会发生变化,必须从两种方法的角度看service均停止才能真正停止。附上一张图: 
结语
有关“Android中的Service”的博文到这里就差不多结束了。这两篇基本上还是比较完整的介绍了service的方方面面的东西,但是也仅限于介绍了——里面的一些点如果讲的话就太过于深入了,这样很容易导致博文失去重心,这是我回顾以前写的一些博文得到的经验。以前有些文章,尤其是源码解析方面的,我喜欢寻根溯源刨根问底,所以会一直顺着源码往下挖掘,直到挖不动为止。这样的话,就我个人而言肯定是能得到很多收获的,但是写成博客之后可能会显得比较的晦涩,因为必须顺着我的思路,跟着我一路挖下去才能理解我的意思——而一篇好的博客应当是简洁明了的,能让观者轻松地有所收获的。
关于IBinder,Binder,Messenger,AIDL,IPC,等等等等,我后续会有一批关于它们的专题文章,在里面会有比较详细的讲解,包括源码解析之类的。
再次感谢Google官方文档,感谢鸿洋大哥以及郭神等大神——本文有部分内容参考了他们的一些文章。