浅谈Android中的Binder机制

Binder是一个灰常深入的话题,为什么说它深入,因为它不仅仅涉及到了应用层,还涉及到了linux驱动层,Binder的驱动层目录或者说设备驱动是/dev/binder,从一张图可以大概看出Binder总体一个工作机制。它由Client、Server、Service Manager和驱动程序Binder四个组件构成。


从IPC角度来看,这张图表示Binder是一种跨进程通信方式,Binder也可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder,从Android framework角度来说,Binder是Service Manager连接各种Manager(ActivityManager WindowManager等等)和相应ManagerService的桥梁,当然这是从源码角度,从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务的Binder对象,通过这个Binder对象,客户端可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

其中Service Manager,它是整个Binder机制的守护进程,用来管理开发者创建的各种Server,并且向Client提供查询Server远程接口的功能。Service Manager组件是用来管理Server并且向Client提供查询Server远程接口的功能,那么,Service Manager就必然要和Server以及Client进行通信了。我们知道,Service Manger、Client和Server三者分别是运行在独立的进程当中,这样它们之间的通信也属于进程间通信了,而且也是采用Binder机制进行进程间通信,因此,Service Manager在充当Binder机制的守护进程的角色的同时,也在充当Server的角色,然而,它是一种特殊的Server,Service Manager是成为Android进程间通信(IPC)机制Binder守护进程的过程是这样的:
1. 打开/dev/binder文件:open("/dev/binder", O_RDWR);
        2. 建立128K内存映射:mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
        3. 通知Binder驱动程序它是守护进程:binder_become_context_manager(bs);
        4. 进入循环等待请求的到来:binder_loop(bs, svcmgr_handler);


我个人从AIDL方面来说一下我对Binder机制的理解。也就是上面所说的Android应用层角度来分析Binder机制。

新建包com.qian.aidl,然后在包中新建三个文件,Book.java Book.aidl IBookManager.aidl代码如下

Book.java

package com.qian.aidl;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book() {

    }

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeInt(bookId);
        out.writeString(bookName);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public String toString() {
        return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
    }

}

Bool.aidl

package com.qian.aidl;

parcelable Book;

IBookManager.aidl

package com.qian.aidl;

import com.qian.aidl.Book;

interface IBookManager {
     List<Book> getBookList();
     void addBook(in Book book);
}

上面三个文件中,Book.java是一个表示图书信息的类,它实现了Parceable接口。Book.aidl是Book类在aidl中的声明。IBookManager.aidl是定义的一个接口,里面有两个方法,主要用于远程调用,分别是getBookList和addBook,其中 getBookList用于从远程服务端获取图书列表,addBook用于添加,此时在gen目录下已经自动生成了一个类 IBookManager.java。

代码如下,经过了稍微的格式转换。

package com.qian.aidl;
public interface IBookManager extends android.os.IInterface
{
	/** Local-side IPC implementation stub class. */
	public static abstract class Stub extends android.os.Binder implements com.qian.aidl.IBookManager {
		
		private static final java.lang.String DESCRIPTOR = "com.qian.aidl.IBookManager";
		/** Construct the stub at attach it to the interface. */
		public Stub() {
			this.attachInterface(this, DESCRIPTOR);
		}
		/**
		 * Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager interface,
		 * generating a proxy if needed.
		 */
		public static com.qian.aidl.IBookManager asInterface(android.os.IBinder obj) {
			if ((obj==null)) {
		
			return null;
			}
			android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
			if (((iin!=null)&&(iin instanceof com.qian.aidl.IBookManager))) {
				return ((com.qian.aidl.IBookManager)iin);
			}
			return new com.qian.aidl.IBookManager.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 {
			switch (code) {
				case INTERFACE_TRANSACTION: {
					reply.writeString(DESCRIPTOR);
					return true;
				}
				case TRANSACTION_getBookList: {
					data.enforceInterface(DESCRIPTOR);
					java.util.List<com.qian.aidl.Book> _result = this.getBookList();
					reply.writeNoException();
					reply.writeTypedList(_result);
					return true;
				}
				case TRANSACTION_addBook: {
					data.enforceInterface(DESCRIPTOR);
					com.qian.aidl.Book _arg0;
					if ((0!=data.readInt())) {
						_arg0 = com.qian.aidl.Book.CREATOR.createFromParcel(data);
					} else {
						_arg0 = null;
					}
					this.addBook(_arg0);
					reply.writeNoException();
					return true;
				}
			}
			return super.onTransact(code, data, reply, flags);
		}
		private static class Proxy implements com.qian.aidl.IBookManager
		{
			private android.os.IBinder mRemote;
			Proxy(android.os.IBinder remote) {
				mRemote = remote;
			}
			@Override 
			public android.os.IBinder asBinder() {
				return mRemote;
			}
			public java.lang.String getInterfaceDescriptor() {
				return DESCRIPTOR;
			}
			
			@Override 
			public java.util.List<com.qian.aidl.Book> getBookList() throws android.os.RemoteException {
				
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				java.util.List<com.qian.aidl.Book> _result;
				try {
					_data.writeInterfaceToken(DESCRIPTOR);
					mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
					_reply.readException();
					_result = _reply.createTypedArrayList(com.qian.aidl.Book.CREATOR);
				} finally {
				_reply.recycle();
				_data.recycle();
				}
				return _result;
			}
			@Override 
			public void addBook(com.qian.aidl.Book book) throws android.os.RemoteException {
				android.os.Parcel _data = android.os.Parcel.obtain();
				android.os.Parcel _reply = android.os.Parcel.obtain();
				try {
					_data.writeInterfaceToken(DESCRIPTOR);
					if ((book!=null)) {
						_data.writeInt(1);
						book.writeToParcel(_data, 0);
					} else {
						_data.writeInt(0);
					}
					mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
					_reply.readException();
				} finally {
					_reply.recycle();
					_data.recycle();
				}
			}
		}
		static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
		static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
	}
	public java.util.List<com.qian.aidl.Book> getBookList() throws android.os.RemoteException;
	public void addBook(com.qian.aidl.Book book) throws android.os.RemoteException;
}

代码结构大概是,首先是IBookManager类继承了IIterface这个接口,接着里面直接包含了一个内部类Stub,Stub里面又包含了一个Proxy类和Stub类中的一些方法和变量声明。变量为DESCRIPTOR,方法有asInterface,asBinder,onTransact。

OK,结构分析完了,下面看是分析Binder机制。Binder机制又和上述方法asInterface,asBinder,onTransact息息相关。

DESCRIPTOR

Binder的唯一标识,一般用当前Binder的类名表示,比如上述代码

private static final java.lang.String DESCRIPTOR = "com.qian.aidl.IBookManager";

asInterface(android.os.IBinder obj)

用于将服务端的Binder对象转换为客户端所需要的AIDL接口类型的对象,这种转换过程又是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身(Stub——>extends Binder)否则返回Stub.Proxy对象。也是一个Binder对象。

可以这么说,远程请求返回的是Binder对象,而Binder对象中则封装了我们所请求需要的一些参数在里面,通过强制类型转换即可获得相对的对象。

asBinder()

此方法用于返回当前的Binder对象。

onTransact

这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法处理。该方法的原型为:

public boolean onTransact(intcode, android.os.Parceldata, android.os.Parcel reply, int flags) throws android.os.RemoteException

服务端通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需的参数(如果目标方法有参数的话),然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话),onTransact方法的执行过程就是这样的。需要注意的是,如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们也不希望随便一个进程都能远程调用我们的服务。

Proxy#getBookList

这个方法运行在客户端,当客户端远程调用此方法时,他的内部实现是这样的:

1)首先创建该方法所需要的输入型Parce对象_data、输出型Parce对象_reply和返回值对象List;

2)然后把该方法的参数信息写入_data中(如果有参数)

3)接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;

4)然后服务端的onTransact方法会被调用,直到RPC过程返回,客户端挂起的线程继续执行,并从_reply中取出RPC过程的返回结果;

5)最后返回_reply的数据。

Proxy#addBook

这个方法运行在客户端,它的执行过程和getBookList是一样的,addBookList没有返回值,所以它不需要从_reply中取出结果。

IBookManager.aidl中声明了两个整形的变量:

static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
这两个id用于表示在transact过程中客户端请求的是哪个方法。

自动生成的IBookManager.java继承了IInterface这个接口,IIterface这个接口的源码中有这样一句注释,“ase class for Binder interfaces”,所有Binder接口的基类,那么首先它本身就是一个Binder对象,内部类Stub直接继承Binder,所以Stub也是Binder对象,Stub.Proxy继承了IBookManager,所以Stub.Proxy也是一个Binder对象。

当客户端和服务端位于同一进程中时,方法调用不会走跨进程的transact过程,当跨进程方法调用时,需要走transact过程,这个逻辑由Stub.Proxy来完成。


Binder的工作机制图如下所示:




Binder返回数据唤醒Clinet,返回的是Bidner对象,但是需要转换AIDL接口类型的对象,这个过程由asInterface方法完成


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值