Binder核心机制分析揭秘跨进程得实现原理
前言
想写篇关于Binder的文章,可对其一无所知,无从下手。在阅读了大量的优秀文章后,心惊胆战的提笔,不怕文章被贻笑大方,怕的是误人子弟!望各位大佬抽空阅读本文的同时,能够对文章的知识点持怀疑态度,共同探讨,共同进步! 一、序列化日常开发中,通过Intent携带数据跳转Activity时,数据通常要通过实现Serializable或Parcelable接口,才能在被Intent所携带,而Serializable接口和Parcelabel接口主要是完成对象的序列化过程。将对象持久化到设备上或者网络传输同样也需要序列化。 1.Serializable 接口Serializable接口是Java所提供的,为对象提供标准的序列化和反序列化操作。通常一个对象实现Serializable接口,该对象就具有被序列化和反序列化的能力,而且几乎所有工作有系统自动完成。Serializable接口内serialVersionID可指定也可以不指定,其作用是用来判断序列化前和反序列化的类版本是否发生变化。该变量如果值不一致,表示类中某些属性或者方法发生了更改,反序列化则出问题。(静态成员变量和transient关键字标记的成员不参与序列化过程) 2.Parcelable 接口Parcelable 接口是Android所提供的,其实现相对来说比价复杂。实现该接口的类的对象就可以在Intent和Binder进行传递。 3.两者的区别Serializable是Java提供的接口,使用简单,但序列化与反序列化需要大量的IO操作,所以开销比较大。Parcelable是Android提供的序列化方法,使用麻烦当效率高。在Android开发中,将对象序列化到设备或者序列化后通过网络传输建议使用Serializable接口,其他情况建议是用Parcelable接口,尤其在内存的序列化上。例如Intent和Binder传输数据。 二、AIDL在Java层,想利用Binder进行夸进程的通信,那就得通过AIDL(Android 接口定义语言)了,AIDL是客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口,只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL,如果是在单应用(单进程),建议使用Messager。 1、AIDL支持的数据类型
自定义的Parcelable对象和AIDL接口必须显示导入到AIDL文件中。 数据的走向 Parcelable对象和AIDL接口在使用前必须标明数据的走向:
示例: void addUser(inout User user); 2、服务端的实现2.1、定义数据对象 public class User implements Parcelable { private String username; private String address; public User() { } public User(String username,String address) { this.username = username; this.address = address; } User(Parcel in) { readFromParcel(in); } //系统默认生成,反序列化过程,我们只需要要构造方法读取相关值就可以 public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; //系统默认生成,内容描述功能,几乎所有情况下都返回0, //仅仅当前存在文件描述符,才返回1 @Override public int describeContents() { return 0; } //序列化过程,通过一系列的write将值写到Parcel 对象 @Override public void writeToParcel(Parcel dest,int flags) { dest.writeString(username); dest.writeString(address); } @Override public String toString() { return username+":"+address; } public void readFromParcel(Parcel in){ username=in.readString(); address=in.readString(); } } 2.2、抽象服务端服务 下面代码通过建立UserManager.aidl文件,为客户端提供 addUser 和getUser 的能力。UserManager可以理解为,服务端和客户端的共同约定,两者能进行怎么样的交互
package com.gitcode.server; // 在这里要导入传递对象的类型,例如User import com.gitcode.server.User; interface UserManager { void addUser(inout User user); User getUser(int index); } 定义UserManager.aidl文件后,系统默认会生成UserManager.java文件。 UserManager.java的代码如下,为了减少篇幅,去掉了一些实现。 public interface UserManager extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements com.gitcode.server.UserManager { private static final String DESCRIPTOR = "com.gitcode.server.UserManager"; public Stub() { this.attachInterface(this,DESCRIPTOR); } public static com.gitcode.server.UserManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.gitcode.server.UserManager))) { return ((com.gitcode.server.UserManager) iin); } return new Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags) throws android.os.RemoteException { ...... } private static class Proxy implements com.gitcode.server.UserManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void addUser(com.gitcode.server.User user) throws android.os.RemoteException { ...... } @Override public com.gitcode.server.User getUser(int index) throws android.os.RemoteException { ..... } } static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void addUser(com.gitcode.server.User user) throws android.os.RemoteException; public com.gitcode.server.User getUser(int index) throws android.os.RemoteException; } 从上文可知,UserManager本身是一个接口,并继承IInterface接口。UserManager.java声明了 package com.gitcode.server; parcelable User; 在服务端中,服务一般以Service体现,定义UserServcie,继承Service。 public class UserService extends Service { private static final String TAG = "Server"; private List<User> list = new ArrayList<>(); @Override public void onCreate() { super.onCreate(); list.add(new User("GitCode","深圳")); } @Override public IBinder onBind(Intent intent) { Log.i(TAG,"on Bind"); return stub; } private UserManager.Stub stub = new UserManager.Stub() { @Override public void addUser(User user) throws RemoteException { list.add(user); Log.i(TAG,"add user:"+user); } @Override public User getUser(int index) throws RemoteException { Log.i(TAG,"get user,index:"+index); return list.size() > index && index >= 0 ? list.get(index) : null; } }; } 在AndroidManifest.xml文件声明Service,以两个组件形成单独的app来体现两个进程,通过AIDL进行数据交互。在客户端通过 <service android:name="com.gitcode.server.UserService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.gitcode.server.userservice"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service> 3、客户端的实现客户端主要是通过共同的约定(UserManger.aidl)向服务端进行请求,服务端响应客户端的请求。为了提高效率和减少出错,通过拷贝来实现客户端的AIDL文件。将服务端的aidl整个文件拷贝到客户端的main目录下,不做任何修改。 在客户端建立与服务端User类同包的目录,并将User类拷贝过来,不做任何修改。 在Activity中绑定服务端的Service,绑定成功后进行数据交互。 public class MainActivity extends AppCompatActivity { private static final String TAG = "Client"; private UserManager mUserManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toBindService(); } private void toBindService() { Intent intent = new Intent("com.gitcode.server.userservice"); intent.setPackage("com.gitcode.server"); bindService(intent,connection,Context.BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name,IBinder service) { mUserManager = UserManager.Stub.asInterface(service); try { User user = mUserManager.getUser(0); Log.e(TAG,user.toString()); mUserManager.addUser(new User("张三","北京")); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; } 运行效果: 客户端: 服务端: 4、小结客户端调用服务的方法,被调用的方法运行在服务端的的Binder线程池,同时客户端会被挂起,如果服务端方法执行耗时操作,就会导致客户端ANR,所以不要在客户端主线程访问远程服务方法。同时服务端不应该自己新建新建线程运行服务方法,因为方法会交由线程池处理,同时对数据也要做好并发访问处理。 三、BinderBinder是Android系统提供的一种IPC机制。由于Android是基于Linux内核,因此,除了Binder以外,还有其他的IPC机制,例如Socket,共享内存,管道和消息队列等。之所有不使用原有的 IPC机制,是因为使用Binder机制,能从性能、稳定性、安全性带来更好的效果。例如,Socket是一套通用的接口,传输速率低下,适合网络传输这种情况,而管道和消息队列需要数据的两次拷贝,共享内容难以管控等。而Binder对数据只需要一次拷贝,使用C/S架构,职责明确,容易维护和使用。 在Binder机制中,主要涉及到Client、Server、ServiceManger三个端,三者通过Binder进行跨进程通信,支持着Android这个大网络。它们的关系如下图。 Server Server进程需要注册一些Service到ServiceManger中,以对外告知其可提供的服务。例如上文AIDL中,会注册UserService,并为Client提供添加User和获取User的操作。注册的过程,Server进程就是客户端,而ServiceManger就是服务端。 Client 对Sever进程进行业务逻辑操作。通过Service的名称在ServiceManger查找对应的Service。 ServiceManager ServiceManger集中管理系统内的所有Service,服务通过注册Service到ServiceManger的查找表中,当Client根据Service名称请求ServiceManger在查找表中查询对应的Service。 图表示三者的C/S架构,例如Client查询向ServiceManger查询Service时,Client就是客户端,而ServiceManger就是服务端。而虚线则表示两者之间通过Binder进行进程间的通信,因此通过了解一条虚线的流程,就可以知道Binder的机制。 1、Service的注册过程通过Server进程的注册Service过程,可以了解到Binder机制的工作原理。 BpServcieManger和BnServcieManger是客户端与服务端进程业务层辑实现的封装,而BpBinder和BBinder是IPC机制的方式。此时Server进程是客户端,ServiceManger是服务端。 1.1 ProcessState 每个进程通过单例模式创建了唯一的ProcessState对象,在其构造器中,通过 open_driver() 方法打开了/dev/binder设备,相当于Server进程打开了与内核的Binder驱动交互的通道,并设置最大支持线程数为15。binder设备是Android在内核中为完成进程间通信而专门设置的一个虚拟设备。1.2 BpBinder和BBinder BpBinder与BBinder都是Android与Binder通信相关的代表,两者一一对应,都从IBinder派生而来。如果说BpBinder代表客户端,那么BBinder就代表服务端,一个BpBinder通过handler标识与对应的BBinder进行交互。在Binder系统,handler标识为0代表着ServiceManger所对应的BBinder。BpBinder与BBinder并没有直接操作ProcessState打开的binder设备。 1.3 BpServiceManger和BnserviceManger 两者继承至IServiceManger,与业务逻辑相关,可以说将业务层的逻辑架构到Binder机制上。BnserviceManger从IServiceManger BBinder派生而来,可直接参与Binder的通信,而BpServiceManger通过mRemote指向BpBinder。 1.4 注册相关Service 通过上文三小节,BpServiceManger对象实现对IServiceManger的业务函数,又有BpBinder作为通信代表,下面分析一下注册的过程。 将字符串名字和Service对象作为参数传到BpServiceManger对象的 addService() 函数,该方法将参数数据打包后传递给BpBidner的transact() 函数。业务层的逻辑到此就结束,主要作用是将请求信息打包交给通信层去处理。在BpBinder的 transact() 函数调用了IPCThreadState对象的 transact() 函数,所以说BpBinder本身没有参与Binder设备的交互。每个线程都有一个IPCThreadState对象,其拥有一个mOut、mIn的缓冲区,mOut用来存储转发Binder设备的数据,而mIn用来接收Binder设备的数据。通过ioctl 方式与Binder设备进行交互。
1.5 小结通过上文Service的注册过程,分析了Binder的机制。Binder只是通信机制,业务可以基于Binder机制,也可以基于其他IPC方式的机制,也就是上文为啥有BpServiceManger和BpBinder。Binder之所以复杂,是Android通过层层的封装,巧妙的将业务与通信融合在一起。主要还是设计理想很牛逼。 2、ServiceManger通过1小节的分析,是否应该也有一个类继承自BnServiceManger来处理远方请求呢?
3、ClientClient想要使用Server进程提供的Service,又该进行哪些步骤呢? 四、总结通过对Binder机制的学习,了解Android是如何通过层层封装将Binder机制集成要应用程序,对Binder机制有一个较深入的理解。可以通过第Java层AIDL的使用,加深对Binder机制的理解。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |