Binder是Android基于Linux的一种独特的IPC机制。所谓IPC,就是跨进程通信。线程是CPU调度的基本单位,而进程则是向系统申请资源的基本单位。同一个进程中的各个线程是可以相互访问内存的,因为这些线程中的变量都是在堆栈中的。例如在Java中,多个线程可以set/get同一个变量,当然,这会引起线程同步问题。进程之间是不能直接相互访问变量的,也就是不能直接通信,需要使用一些IPC机制。常见的IPC有以下几种类型
共享内存:利用内存缓存区,将需要共享的数据写入内存缓存中。这里每次通信有两次数据读写过程,如A需要访问B进程的内存,则需要先将B的数据写到内存缓存区,然后再将数据写到进程A的内存区。 管道:可以理解为系统让两个文件读写同一个特殊的文件,将共享的数据在文件中储存 套接字:类似于网络通信 Android由于相当于嵌入式设备,对性能和内存要求较高,因此Android系统内置了一套Binder机制。Binder的底层技术原理是Linux的内存映射技术,也即用户进程可以和内核进程共享同一块内存,这样就减少了内存读写的操作数。Binder是一种C/S结构,访问方是Client,被访问的进程是Server。C和S通过Binder Driver作为桥梁,通过ServiceManager拿到系统的Binder服务。
这里还涉及到面向对象编程中的对象调用问题,例如A进程需要调用B进程中的某个对象做个操作。由于只是共享了内存而无法直接共享对象(不是同一个进程,无法保证运行的是同一份代码)。与Web开发中的RPC类似,Binder使用了代理模式,A进程需要调用B进程中的某个类,就是A进程中生成一个B进程中对象的代理对象,当发现A和B是同一个进程时,就直接调用,如果不是,则走Binder的IPC。
Android开发中的Binder模式具体有一个调用接口、一个接口的Stub抽象类,一个Proxy静态类。Android提供了AIDL,将自动生成这三个类,并放到同一个外部类中,这样可以避免Proxy和Stub的类名重复。
以下列实例为例:BookManager接口支持addBook和getBooks两个方法
/**
-
Server能够提供的服务 */ interface BookManager: IInterface { @Throws(RemoteException::class) fun addBook(book: Book?)
@Throws(RemoteException::class) fun getBooks(): List? } BookManager接口继承于IInterface接口,表示这个接口的实现是在另外一个进程中,并可提供服务。
BookManagerStub是BookManager接口驻留在Server进程的一个对象,也就是这个BookManagerStub是在Server进程中new出来的,在BookService中的onBind返回这个BookManagerStub的对象,BookClientActivity连接Service的回调中,把Service返回的BookManagerStub对象通过BookManagerStub的asInterface转换成BookManager对象,如果获取的BookManagerStub的对象是和Client同进程的,就直接用,否则就构造一个BookManagerProxy,用来与Server跨进程通信。
abstract class BookManagerStub: Binder(), BookManager {
companion object {
const val DESCRIPTIOR = "BookManager"
const val TRANSACTION_ADD_BOOK = 0
const val TRANSACTION_GET_BOOK = 1
fun asInterface(binder: IBinder?): BookManager? {
if (binder == null) {
return null
}
val inter = binder.queryLocalInterface(DESCRIPTIOR)
return (inter as? BookManager)?: BookManagerProxy(binder)
}
}
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
when(code) {
IBinder.INTERFACE_TRANSACTION -> {
reply?.writeString(DESCRIPTIOR)
return true
}
TRANSACTION_ADD_BOOK -> {
data?.enforceInterface(DESCRIPTIOR)
var book:Book? = null
if (data?.readInt() != 0) {
book = Book.CREATOR.createFromParcel(data)
}
addBook(book)
reply?.writeNoException()
return true
}
TRANSACTION_GET_BOOK -> {
data.enforceInterface(DESCRIPTIOR)
val result = getBooks()
reply?.writeNoException()
reply?.writeTypedList(result)
return true
}
}
return super.onTransact(code, data, reply, flags)
}
override fun asBinder(): IBinder {
return this
}
}
class BookService: Service() {
companion object {
const val TAG = "BookService"
}
private val books = arrayListOf<Book>()
private val bookManager = object :BookManagerStub() {
override fun addBook(book: Book?) {
if (book == null) {
return
}
books.add(book)
Log.d(TAG, "addBook")
}
override fun getBooks(): List<Book>? {
return books;
}
}
override fun onBind(intent: Intent?): IBinder? {
return bookManager
}
}
class BookClientActivity: Activity() {
companion object {
const val TAG = "BookClientActivity"
}
private var bookManager: BookManager? = null
private var isConnected: Boolean = false
private val serviceConnection = object: ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
isConnected = false
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
isConnected = true
bookManager = BookManagerStub.asInterface(service)
}
}
fun toBindService() {
val intent = Intent(this, BookService::class.java)
intent.setAction("com.benson.android.aidl.server")
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
} 其中BookManagerProxy是在Client进程中,而BookManagerStub在Server进程中,BookManagerProxy对BookManager的方法的实现中,使用IBinder的transact方法向Server进程写数据并得到结果,将结果解析成这次调用的返回值。其实这里的这个IBinder对象,就是Server端中new出来的BookManagerStub对象,但如果是同一进程,就是真实的对象,而不是同进程,则是Binder机制让两个进程共享的对象(对象是内存+方法集)。通过打印Log,我发现BookService中的BookManagerStub对象和BookManagerProxy中的IBinder对象,其实不是同一个对象。当然了,两个进程中怎么可能存在同一个对象。我猜这是Binder机制通过一些共享内存,将两个对象之间的差异屏蔽了,让我们调用Client中的对象就像在访问Server中的对象一样。
BookManagerProxy中调用了transct方法就会触发BookManagerStub中的onTransact方法,这里可以通过code来判断是调用的哪个方法,从data中取出Client发来的数据,将调用结果通过replay传回去。
Binder机制虽然是C/S模型,但不存在绝对的C端和S端,按理解应该是主动调的一端就是C端,而被调并且返回结果的一端就是S端。
发布于 28 分钟前