android binder理解

Android中的Parcel是什么 
Parcel,翻译过来是“打包”的意思。打包干什么呢?是为了序列化。

    如果要在进程之间传递一个整数,很简单,直接传就是行了;如果要传一个字符串,就稍微复杂了点:需先分配一块可以容纳字符串的内存,然后将字符串复制到内 存中,再传递(新手可能问:为啥不直接把字符串的引用传过去呢?学过C/C++的地球人都知道:进程有自己的内存地址空间,一个进程中的1000地址可能 在另一个进程中是100000,java对象的引用跟本上还是内存地址);再如果要传递一个类的实例呢?也是先为类分配内存,然后复制一份再传递可以吗?我认为不可以,我至少可以找到一个理由:类中成员除了属性还有方法,即使属性能完整传过去,但还有方法呢?方法是独立于类对象存在的,所以到另一个进程中再引用同一个方法就要出错了,还是因为独立地址空间的原因。

    Android开发中,很经常在各activity之间传递数据,而跟据Android的设计架构,即使同一个程序中的Activity都不一定运行在同一个进程中,所以处理数据传递时你不能老假设两个activity都运行于同一进程,那么只能按进程间传递数据来处理,使之具有最广泛的适应性。

  那么到底如何在进程之间传递类对象呢?简单来说可以这样做:在进程A中把类中的非默认值的属性和类的唯一标志打成包(这就叫序列化),把这个包传递到 进程B,进程B接收到包后,跟据类的唯一标志把类创建出来,然后把传来的属性更新到类对象中,这样进程A和进程B中就包含了两个完全一样的类对象。


Android开发:什么是IBinder 
上回书简单描述了进程间传递类对象的原理,这回在讲Parcel之前,先要讲一个东西:IBinder。 
IBinder是什么呢?首先要明白,Android的远程调用(就是跨进程调用)就是通过IBinder实现的,下面是对android开发文档的翻译。 
IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口,而应该从Binder派生。 
IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact()。第一个方法使你可以向远端的IBinder对象发送发出调用,第二个方法使你自己的远程对象能够响应接收到的调用。IBinder的API都是同步执行的,比如transact()直到对方的Binder.onTransact()方法调用完成后才返回。调用发生在进程内时无疑是这样的,而在进程间时,在IPC的帮助下,也是同样的效果。 
通过transact()发送的数据是Parcel,Parcel是一种一般的缓冲区,除了有数据外还带有一些描述它内容的元数据。元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中,如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。(总结:像内部变量一样,调用的是同一个变量。)
系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC调用。例如:当一个IPC从进程A发到进程B,A中那个发 出调用的线程(这个应该不在线程池中)就阻塞在transact()中了。进程B中的交互线程池中的一个线程接收了这个调用,它调用 Binder.onTransact(),完成后用一个Parcel来做为结果返回。然后进程A中的那个等待的线程在收到返回的Parcel后得以继续执
行。实际上,另一个进程看起来就像是当前进程的一个线程,但不是当前进程创建的。(过程!重点!)
Binder机制还支持进程间的递归调用。例如,进程A执行自己的IBinder的transact()调用进程B的Binder,而进程B在其 Binder.onTransact()中又用transact()向进程A发起调用,那么进程A在等待它发出的调用返回的同时,还会用 Binder.onTransact()响应进程B的transact()。

总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什 么区别。 
当操作远程对象时,你经常需要查看它们是否有效,有三种方法可以使用: 
1 transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。 
2 如果目标进程不存在,那么调用pingBinder()时返回false。 
3 可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient,在IBinder代表的进程退出时被调用。

要实现IBinder来支持远程调用,应从Binder类派生一个类。Binder实现了IBinder接口。但是一般不需要直接实现此类,而是跟据你的 需要由开发包中的工具生成,这个工具叫aidl。你通过aidl语言定义远程对象的方法,然后用aidl工具生成Binder的派生类,然后就可使用之。 然而,可是,但是,当然,你也可以直接从Binder类派生以实现自定义的RPC(Remote Procedure Call)调用,或只是实例化一个原始的Binder对象直接作为进程间共享的令牌 来使用。


Android开发:什么是Parcel(2) 
上回书解释了IBinder,这回详细解释一下Parcel,以下是对android sdk 文档的翻议: 
Parcel是一个容器,它主要用于存储序列化数据,然后可以通过Binder在进程间传递这些数据(要了解为什么要序列化,请参 考:http://blog.csdn.net/nkmnkm/archive/2011/05/28/6451699.aspx)。

1、Parcel可以包 含原始数据类型(用各种对应的方法写入,比如writeInt(),writeFloat()等)

2、可以包含Parcelable对象,它还包含了一个活 动的IBinder对象的引用,这个引用导致另一端接收到一个指向这个IBinder的代理IBinder。 

注:Parcel不是一般目的的序列化机制。这个类被设计用于高性能的IPC传输。因此不适合把Parcel写入永久化存储中,因为Parcel中的数据类型的实现的改变会导致旧版的数据不可读。

  Parcel的一坨一坨的API用于解决不同类型数据的读写。这些函数们主要有六种类型。

1原始类

这类方法们主要读写原始数据类型。它们是:writeByte(byte), readByte(), writeDouble(double), readDouble(), writeFloat(float), readFloat(), writeInt(int), readInt(), writeLong(long), readLong(), writeString(String), readString(). 大多数其它数据的操作都是基于这些方法。

2原始数组类

这类方法用于读写原始数据组成的数组。在向数组写数据时先写入数组的长度再写入数据。读数组的方法可以将数据读到已存在的数组中,也可以创建并返回一个新数组。它们是:

writeBooleanArray(boolean[]), readBooleanArray(boolean[]), createBooleanArray() 
writeByteArray(byte[]), writeByteArray(byte[], int, int), readByteArray(byte[]), createByteArray() 
writeCharArray(char[]), readCharArray(char[]), createCharArray() 
writeDoubleArray(double[]), readDoubleArray(double[]), createDoubleArray() 
writeFloatArray(float[]), readFloatArray(float[]), createFloatArray() 
writeIntArray(int[]), readIntArray(int[]), createIntArray() 
writeLongArray(long[]), readLongArray(long[]), createLongArray() 
writeStringArray(String[]), readStringArray(String[]), createStringArray(). 
writeSparseBooleanArray(SparseBooleanArray), readSparseBooleanArray(). 
3 Parcelable类 
Parcelable为对象从Parcel中读写自己提供了极其高效的协议。你可以使用直接的方法 writeParcelable(Parcelable, int) 和 readParcelable(ClassLoader) 或 writeParcelableArray(T[], int) and readParcelableArray(ClassLoader) 进行读写。这些方法们把类的信息和数据都写入Parcel,以使将来能使用合适的类装载器重新构造类的实例。

还有一些方法提供了更高效的操作Parcelable们的途径,它们是:writeTypedArray(T[], int), writeTypedList(List), readTypedArray(T[], Parcelable.Creator) and readTypedList(List, Parcelable.Creator)。这些方法不会写入类的信息,取而代之的是:读取时必须能知道数据属于哪个类并传入正确的 Parcelable.Creator来创建对象而不是直接构造新对象。(更加高效的读写单个Parcelable对象的方法是:直接调用
Parcelable.writeToParcel()和Parcelable.Creator.createFromParcel())

4 Bundles类

Bundles是一种类型安全的Map型容器,可用于存储任何不同类型的数据。它具有很多对讀写数据的性能优化,并且它的类型安全机制避免了当把它的数据封送到Parcel中时由于类型错误引起的BUG的调试的麻烦,可以使用的方法为: writeBundle(Bundle), readBundle(), and readBundle(ClassLoader)。

5 活动对象类

Parcel的一个非同寻常的特性是读写活对象的能力。对于活动对象,它们的内容实际上并没有写入,而是仅写入了一个令牌来引用这个对象。当从Parcel中读取这个对象时,你不会获取一个新的对象实例,而是直接得到那个写入的对象。有两种活动对象可操作:

Binder对象。它是Android跨进程通讯的基础。这种对象可被写入Parcel,并在读取时你将得到原始的对象或一个代理对象(可以想象:在进程内时得到原始的对象,在进程间时得到代理对象)。可以使用的方法们是: writeStrongBinder(IBinder), writeStrongInterface(IInterface), readStrongBinder(), writeBinderArray(IBinder[]), readBinderArray(IBinder[]), createBinderArray(),
writeBinderList(List), readBinderList(List), createBinderArrayList()。

FileDescriptor对象。它代表了原始的Linux文件描述符,它可以被写入Parcel并在读取时返回一个 ParcelFileDescriptor对象用于操作原始的文件描述符。ParcelFileDescriptor是原始描述符的一个复制:对象和fd 不同,但是都操作于同一文件流,使用同一个文件位置指针,等等。可以使用的方法 是:writeFileDescriptor(FileDescriptor), readFileDescriptor()。

6无类型容器类

    一类final方法,用于读写标准的java容器类。这些方法们是:writeArray(Object[]), readArray(ClassLoader), writeList(List), readList(List, ClassLoader), readArrayList(ClassLoader), writeMap(Map), readMap(Map, ClassLoader), writeSparseArray(SparseArray), readSparseArray(ClassLoader)。

什么是Pending intent 
Pending描述了一个Intent和intent的动作。这个类的实例是用getActivity(Context,int,Intent,int) 方法和getBroadcast(Context,int,Intent,int)和 getService(Context,int,Intent,int)方法创建的。创建出的pending intent可以交给其它程序,于是它们可以在以后的某个时间以你的名义执行intent中所描述的动作。
 

通过给于一个PendingIntent,你可以使其它程序像你自己一样执行你所指定的操作(具有相同的权限和身份)。所以,你需要小心的创建 PendingIntent:通常,你最基本的应该明确设置你的相关组件的名字,以保证将来intent是被发送给它而不是其它地方。
 

一个PendingIntent本是只是简单地引用一个由系统维护的一个令牌,这个令牌描述了用于取得PendingIntent的原始数据。这表示即使 拥有这个PendingIntent的进程关闭了,但这个PendingIntent对于那些收到它的进程依然有效。如果创建这个 PendingIntent的程序又重新运行并重新获取同一个类型的PendingIntent(相同的操作,action,数据,类型和组件以及相同的 标志),那么获取的还是这个PendingIntent,并且这个程序可以用cancel()来删除这个PendingIntent。
上一篇:多线程归并排序(摘自githhub)


下一篇:学习笔记TF053:循环神经网络,TensorFlow Model Zoo,强化学习,深度森林,深度学习艺术