Binder与Service 通信机制详解三 (服务端和客户端设计)

来源:互联网 发布:源码出售001zhan 编辑:程序博客网 时间:2024/06/06 07:34

          上一篇博客讲解了关于Service和Binder基本概念的理解与工作机制。或许有些人在头脑里有了大概基本的概念,或许有些人还一头雾水。不管怎样,都没关系,我们接着往下看。

          这一章基于对上一章节的理解来设计服务端和客户端。

         设计服务端

         设计服务端,在我们原来写过的自定义服务当中服务是继承Service的,在Service里面,系统帮我们封装实现好了许多重要的功能,所以我们要写一个服务还是得继承Service来进行的,当然对于厉害的程序员是可以基于Binder来扩展系统服务的。由于跨进程间通信的本质是基于Binder来完成的。所以从代码角度来讲设计服务端其实挺简单的。只需要继承Binder便可。代码如下:

public class MyService extends Binder{@Overridepublic boolean onTransact(int code, android.os.Parcel data,android.os.Parcel reply, int flags)                                                                      throws android.os.RemoteException {switch (code) {//code用于标识客户端期望调用服务端哪个函数,服务端和客户端双方需要约定好一个int值case 1001: {data.enforceInterface("MyService");//检验作用String str = data.readString();         //按照服务端和客户端约定好的参数存放顺序,读取客户端传递过来的数据reply.writeString("service reply");//回复给客户端的数据return true;}}return super.onTransact(code, data, reply, flags);}}
          设计客户端

          客户端就没什么说的了,上一节说过客户端和服务端通信是通过mRemote这个对象调用transact方法来完成的。也就是客户端transact函数发出消息,服务端的onTransact就可以接收到消息。代码如下:

public void sendMessage(){int code = 1000;Parcel _data = Parcel.obtain();//表示要传递给远程服务的包裹,远程服务所需的参数需要放入该包裹中Parcel _reply = Parcel.obtain();//用于存放服务端返回的结果try {_data.writeInterfaceToken("MyService");//对应服务端的标记_data.writeString(name);//用于向包裹中添加一个String变量,包裹中添加的内容是要有顺序的,需双方约定好mRemote.transact(Stub.TRANSACTION_setValue, _data, _reply,0);/*调用该函数后,客户端线程进入Binder驱动,Binder驱动会挂起当前线程,并向远程服务发送一条消息。消息包含了客户端传递过去的包裹,服务端拿到包裹后会进行拆解,也就是服务端的onTransact根据拆解出来的code调用对应的函数。执行完毕服务端再把执行结果放入客户端提供的reply包裹中。然后服务端向Binder驱动发送一个notify消息,从而使得客户端线程从Binder驱动代码区返回到客户端代码区。也就是mRemote.transact调用后会进入等待状态,当服务端/回复后,客户端程序才继续往下走。该函数的最后一个参数为0表示服务端执行完会返回一定的数据,为服务端不返回任何数据。*/{_reply.recycle();_data.recycle();}}


          自己设计服务端和客户端存在的问题

          到了这里服务端和客户端就设计完了,或许你此刻想继承Binder来设计自己的服务端了吧?但是告诉你,是不可以的,因为上述设计的服务端和客户端还存在两个问题没有解决

          question 1:客户端如何获得服务端的Binder对象的引用

          question 2:客户端在通过transact发送消息和服务端通过onTransact接收消息时双方需要约定好两件事情:

                            a:服务端如何知道参数在data数据中的位置(在上述代码注释中也反复说到要双方约定好)

                            b:服务端code标识

          那么我们该如何解决这两个问题呢?或许有些大神知道如何去处理,目前我还不知道如何自己单纯编码解决这两个问题。不过话又说回来了,我们也没必要自己去编码解决这两个问题。其实系统已经帮我们提供了解决方案。那么是如何解决的呢?那就是基于Service类,通过AIDL的方式去解决。

for question 1:我们说了那么多其实就是为了解决客户端和服务通信的问题。假设我在项目里自定义了一个服务MyService,那么我们如何获得MyService端的那个Binder引用呢?先回顾下(在第二章节说过,要解决客户端和MyService端(两者可以在同一个进程也可以不在)通信问题,创建MyService类时需要创建一个服务端的Binder引用,MyService端的Binder创建的时候,在Binder驱动里也会创建一个Binder引用mRemote。如果客户端和MyService端在同一个进程,那么我们需要获取的是MyService端的这个Binder,如果客户端和MyService不在同一个进程,那么客户端获取到的是Binder驱动层的mRemote引用)。好了,那么到底如何去获得这个Binder引用呢?

        我们启动Service的时候通常用bindService(Intent service, ServiceConnection conn,int flags)方式启动,startService就不说了,它获取不到Binder引用。

请注意下bindService的第二个参数,是个接口,onServiceConnected的第二个参数是用来接收服务端的那个Binder对象的。其原型是:

public interface ServiceConnection {    public void onServiceConnected(ComponentName name, IBinder service);    public void onServiceDisconnected(ComponentName name);}

那么调用bindService的时候,系统是如何调用的呢?我们代码跟进去发现bindService在ContextWrapper这个类中,然后bindService里面调用了

mBase.bindServiceAsUser(service, conn, flags, user);
而mBase其实是ContextImpl这个类里面,所有调用bindService实际处理是在ContextImpl这个类里面。ContextImpl或许大家在工程里找不到,其实它是个隐藏类,我们直接去看不到的,我们可以到sdk源码目录下搜索到这个类。那么在客户端调用bindService这个类正常启动MyService后,系统的ActivityManagerService(AmS)就会远程调用ActivityThread(这个就是当前客户端运行的UI线程)类中的ApplicationThread对象,调用的参数中会包含Service的Binder引用,然后ApplicationThread会回调bindService中的conn接口,把IBinder对象传递过来,不过我们发现传递过来的IBinder对象是个接口并不是Binder类型对象。其实我们是通过这个IBinder对象去找到Binder对象的,在后面章节讲解AIDL文件的时候会给大家说明白,它是如何获得的。好了,到了这里第一个问题就解决了。附上一张其工作流程图,大家一目了然。


for question 2:第二个我们要解决的问题是如何保证传递给服务端的包裹里面参数的顺序。其实android 的sdk已经为我们提供了解决方法。那就是aidl工具。该工具可以把一个aidl文件转换为一个Java文件,在该Java文件中同时重载了transact 和 onTransact方法。客户端和服务端都需要该java文件,于是就统一了存入和读取包裹的顺序。那么该AIDL文件生成的Java文件是如何工作的呢?其实我们从博客一到三讲了那么多,其工作机制都体现在了该AIDL生成的Java文件中了。

          好了,服务端和客户端的设计就讲到这里,那么下一篇,也是比较重要的一篇,我将结合源码,讲解下服务端和客户端是如何在通信的,也就是了解AIDL的工作机制。同时也是将前面讲的内容从文字体现到代码中来。


Binder与Service 通信机制详解一 (前言)

Binder与Service 通信机制详解二 (Binder与Service理解)

Binder与Service 通信机制详解三 (服务端和客户端设计)

Binder与Service 通信机制详解四 (源码分析AIDL工作机制)














0 0