Android基础之Binder

本文详细介绍了Android中的进程间通信(IPC)机制,包括Binder、AIDL、Messenger和ContentProvider的使用方式及原理。深入探讨了序列化与反序列化在IPC中的作用,并分析了多进程模式下的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、IPC

进程间通信。多个进程间的数据交互

进程一般是指一个可执行的单元,在PC或移动设备上指一个程序或应用。

线程是CPU最小的调度单元,是一种有限的系统资源

一个进程中可以包含多个线程

如果将一个耗时操作放在主线程,就会造成ANR

Android中进行进程间通信最特色的就是使用Binder,同样也可以使用Socket进行进程间通信

Android可以通过给四大组件指定android:process属性就可以开启多进程模式了

使用多进程会造成:

1、静态成员和单例模式失效

这是因为每个进程会有自己独立的虚拟机,在内存中会分配不同的地址空间,因此在访问同一个类的对象会产生不同的副本,这样该类的对象值发生变化时,对其他进程中的该类对象的值不受影响。

2、线程同步机制失效

原因和1类似

3、SharedPreferences的可靠性下降

SharedPreferences不支持并发写操作,会导致一定几率的数据丢失

4、Application会多次创建

当一个组件跑在一个新的进程中时,系统就会给新的进程分配独立的虚拟机,就是启动一个应用的过程

二、序列化和反序列化

序列化是指把Java对象转换为字节序列的过程

反序列化是指把字节序列恢复为Java对象的过程

用途:

1、实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里)

2、通过序列化对象在网络中传递对象。

3、通过序列化对象在进程间传递对象
serializable、parcelable

1、serializable接口

是Java提供的一个序列化接口。实现起来比较简单,只需要在类的声明的时候实现该接口,同时指明一个serialVersionUID标识即可。

serialVersionUID是用来辅助序列化和反序列化的,Java对象在序列化的过程中会将当前类的

serialVersionUID写到文件中,只有序列化后的数据的serialVersionUID与当前类的serialVersionUID一致才能进行反序列化。

2、parcelable接口

Android提供的新的序列化接口。

Android的Parcelable的设计初衷是因为Serializable效率过慢,为了在程序内不同组件间以及不同Android程序间(AIDL)高效的传输数据而设计,这些数据仅在内存中存在,Parcelable是通过IBinder通信的消息的载体。

实现Parcelable接口主要可以分为一下几步:

<1> implements Parcelable。

 <2> 重写writeToParcel方法,将你的对象序列化为一个Parcel对象,即:将类的数据写入外部提供的Parcel中,打包需要传递的数据到Parcel容器保存,以便从Parcel容器获取数据。

<3> 重写describeContents方法,内容接口描述,默认返回0即可。

<4> 实例化静态内部对象CREATOR实现接口Parcelable.Creator 。

3、Parcelable和Serializable的区别:

<1> 在使用内存的时候Parcelable比Serializable的性能高。
     
<2> Serializable在序列化的时候会产生大量的临时变量,从而引起频繁的GC(内存回收)。

<3> Parcelable不能使用在将对象存储在磁盘上这种情况,因为在外界的变化下Parcelable不能很好的保证数据的持续性。

<4> 在读写数据的时候,Parcelable是在内存中直接进行读写,而Serializable是通过使用IO流的形式将数据读写入在硬盘上

<5> Parcelable是以Ibinder作为信息载体的.在内存上的开销比较小,因此在内存之间进行数据传递的时候,Android推荐使用Parcelable

三、Binder

Binder是Android的一个类,实现了IBinder接口。是Android中的一种跨进程通信的方式。

在Android开发中,大量使用到了系统Service,比如媒体播放、各种传感器以及WindowManagerService等等。

调用系统服务的过程:

在Android开机启动过程中,Android会初始化系统的各种Service,并将这些Service向ServiceManager注册(即让ServiceManager管理),
	 
客户端想要得到具体的Service直接向ServiceManager查询,得到具体的Service引用,然后通过这个引用向具体的服务端发送请求,服务端执行完成后就返回结果给客户端。

在Android开发中主要用于Service中,包括AIDL和Messenger。其中普通service中的Binder是不涉及进程间通信的。

1、使用AIDL实现进程间通信

<1> 创建MyBook.java、MyBook.aidl、IMyBookManager.aidl三个文件

MyBook实现parcelable接口

MyBook.aidl中声明了MyBook对象:parcelable MyBook;

IMyBookManager.aidl中是一个接口,包含addMyBook、getBookList两个方法

文件创建完成后build项目就会在app/build/generated/source/aidl文件夹下生成一个IMyBookManager.java文件。IMyBookManager.java类继承了IInterface接口,它自己也还是一个接口。它的内部首先声明了在IMyBookManager.aidl中 定义了的两个方法以及声明了两个整型的id用来标识这两个方法,这两个id用于标识在transact过程中客户端所请求的到底是哪个方法。接着声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端处于同一个进程时,方法调用不会走跨进程的transact过程,而当两者不在同一进程,方法调用就会走transact过程,这个逻辑是由Stub的内部代理类Proxy完成的。这个接口的核心实现就是内部的内部类Stub和Stub内部的代理类Proxy。

<2> 创建远程服务端service类MyBookManagerService

在该类中使用new IMyBookManager.Stub()创建一个Binder对象,并在onBind()方法将该Binder对象返回。然后再AndroidManifest中注册该服务,并为服务指定进程

<3> 客户端进程Activity实现

通过bindService的形式将service绑定到Activity中。然后new ServiceConnection对象在该对象的onServiceConnected(Component className, IBinder service)方法中使用 IMyBookManager.Stub.asInterface(service)来获取IMyBookManager对象,然后通过该对象就可以调用到service进程中的方法addMyBook、getBookList。asInterface(binder)方法用于将服务端的Binder对象转换成客户端所需要的AIDL接口类型的对象,如果客户端和服务端是同一进程,那么就返回的是服务端的Stub对象本身,否则返回的就是系统封装的Stub.proxy对象。代码如下:

public static com.test.demo.test.apiDemo.service.aidl.demo_1.IMyBookManager asInterface(android.os.IBinder obj){
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.test.demo.test.apiDemo.service.aidl.demo_1.IMyBookManager))) {
                return ((com.test.demo.test.apiDemo.service.aidl.demo_1.IMyBookManager)iin);
            }
            return new com.test.demo.test.apiDemo.service.aidl.demo_1.IMyBookManager.Stub.Proxy(obj);
}

在多进程中使用IMyBookManager调用getBookList其实就是调用的是proxy对象对象的Proxy.getBookList方法,由此看来Proxy.getBookList方法是运行在客户端,接着会调用transact方法来发起远程请求,同时当前线程会挂起;然后服务端Binder线程中的onTransact方法就会被调用,直到返回结果,当前线程才会继续执行,由此可以看出 onTransact方法是运行在服务端的Binder线程池中的,它是通过参数code来确定客户端请求的目标方法的。

注意:从上面的分析可以得出当客户端发起请求时,当前线程就会被挂起直到服务端返回数据为止,所以如果一个远程方法很耗时,那么就不能再UI线程中发起远程请求。

2、使用Messenger实现进程间通信

Messenger也成为信使,可以在不同进程中传递Message对象。它的底层实现依然是AIDL,对AIDL进行了封装。

<1> 服务端进程

服务端创建一个Service来处理客户端请求,同时创建一个Handler来接受客户端的消息,并通过该handler来new Messenger(handler)对象,然后在onBind()方法中使用Messenger.getBinder()方法返回Binder对象。然后再AndroidManifest中注册该服务,并为服务指定进程

<2> 客户端进程

通过bindService的形式将service绑定到Activity中。然后new ServiceConnection对象在该对象的onServiceConnected(Component className, IBinder service)方法中使用IBinder对象创建一个new Messenger(service)对象,然后就可以通过该对象的send()方法发送对象了,发送消息的类型是Message对象。

使用上面的步骤就完成了客户端向服务端进程传递消息,如果需要服务端能够回应客户端,那么同样在客户端需要创建一个Handler对象,并通过该handler来创建一个Messenger对象,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端在handler中通过这个replyTo参数就可以拿到客户端传递过来的Messenger对象,就可以使用该Messenger对象发送消息给客户端了。

3、使用ContentProvider实现进程间通信

ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,和Messenger一样,它的底层同样是Binder

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值