什么是Binder
Binder是Android中特有的一种跨进程通讯的方式。但我们在平时的开发过程中,可能很少用的。而Binder的整个体系结构又尤为复杂,一般很难通过网上的一两篇博客,就能把Binder吃透,我们需要通过源码及Binder的一些架构原理,来进行研究。后面的章节我们将主要通过3个部分来由浅至深来了解Binder。首先我们先看在实际的开发中怎么来实现Binder通讯,接着分析Binder框架的原理,最后结合源码进行分析。
为什么感觉Binder很陌生?
- 项目业务简单,不涉及多进程通讯
- 涉及多进程通讯,只简单用AIDL,没深入了解
为什么要学习Binder?
Binder作为Android核心的跨进程通讯方式。如果我们要研究Android的源码,Binder是一道需要跨过去的坎。我们都知道系统的各种服务运行在SystemServer进程中,我们应用与系统的各种交互都涉及到跨进程通讯。
例如最简单的启动一个Activity、启动一个Service。到例如使用系统的网络、硬件、等各种Service,其实都涉及到跨进程通讯。只是系统为我们做好了各种封装调用而已。
所以如果你只希望一直停留在应用层的业务开发,其实你可能一直永远都不知道Binder,但是一旦你开始了解Android的源码,那么你总会与Binder相遇。
Android为什么使用Binder作为主要进程间通讯机制?
- 安全性:Binder机制从协议本身就支持对通信双方做身份校检,安全性高。传统的进程通信方式对于通信双方的身份并没有做出严格的验证,只有在上层协议上进行架设;比如Socket通信ip地址是客户端手动填入的,都可以进行伪造
- 性能:socket作为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通信和本机上进程间的低速通信,Binder其实通过Binder驱动在内核区域进行了数据的传输,性能高
如何实现一个Binder通讯?
- 在项目中新建一个aidl
// IBookManager.aidl
package com.jd.test.myapplication;
import com.jd.test.myapplication.Book;
// Declare any non-default types here with import statements
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Book> getBooks();
void addBook(in Book book);
}
- 创建一个在独立进程的Service
<service android:name=".BookManagerService"
android:process=":remote"/>
public class BookManagerService extends Service {
private CopyOnWriteArrayList<Book> mBookList=new CopyOnWriteArrayList<>();
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1,"Android"));
mBookList.add(new Book(2,"IOS"));
}
private Binder mBinder=new IBookManager.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List<Book> getBooks() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
- 另外一个进程,启用远程的Service,并调用接口方法,进行通讯
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent=new Intent(this,BookManagerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection=new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager manager=IBookManager.Stub.asInterface(iBinder);
try {
List<Book> books=manager.getBooks();
System.out.println("books:"+books);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
}
只能说so easy。Android提供了优秀的API,使得我们可以很方便的通过AIDL实现进程间的通讯。貌似我们实现了进程间通讯,但是连Binder的身影都没看到,这也就是上面的Binder对很多童鞋都很陌生的原因。
Binder的原理
我们定义了AIDI后,默认系统都会生成一个集成了IInterface的接口的类,eclipse默认是在gen目录下。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: G:\\Source\\Demo\\MyApplication\\app\\src\\main\\aidl\\com\\jd\\test\\myapplication\\IBookManager.aidl
*/
package com.jd.test.myapplication;
// Declare any non-default types here with import statements
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.jd.test.myapplication.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.jd.test.myapplication.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.jd.test.myapplication.IBookManager interface,
* generating a proxy if needed.
*/
public static com.jd.test.myapplication.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.jd.test.myapplication.IBookManager))) {
return ((com.jd.test.myapplication.IBookManager)iin);
}
return new com.jd.test.myapplication.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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getBooks:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.jd.test.myapplication.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.jd.test.myapplication.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.jd.test.myapplication.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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.jd.test.myapplication.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.jd.test.myapplication.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.jd.test.myapplication.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_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.util.List<com.jd.test.myapplication.Book> getBooks() throws android.os.RemoteException;
public void addBook(com.jd.test.myapplication.Book book) throws android.os.RemoteException;
}
这个类也就是我们在onServiceConnected中使用的接口。在这个IBookManager中我们有几个关键的类Stub、Proxy,也见到了久违的Binder。那么纠结Binder是怎么样来进行间通讯的呢?下面我们先通过一个示例图来简单描述一下该流程。
IInterface结构分析
首先变量DESCRIPTOR定义了接口和对应方法的唯一标示。因为Binder其实是一种底层的通讯方式,Google工程师将Binder包装成了一种对象的引用。所以这里的标识是告诉底层的Binder驱动,我的Binder引用标识,对应的方法标识。这样Binder驱动才能在进程间进行装换。
Proxy:实现了IBookManager接口,这个代理类是往Binder驱动里面写数据,通过调用Binder的transact方法往Binder驱动写Parcel数据。可以理解为告诉Binder驱动我们要调用什么方法
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
Stub:继承了Binder,实现了IBookManager接口。Binder驱动调用远程方法成功后,要回调告诉执行的结果。通过回调onTransact方法。将Binder驱动中的Parcel数据转换为我们的回调数据。
IBinder引用是什么时候注册到了Binder驱动中呢?
我们知道Stub继承了Binder,那么当Stub实例化的时候,这个时候Binder无参构造被调用,执行了一个native 的init方法。这个时候向Binder驱动注册了IBinder的引用
public Binder() {
init();
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
}
private native final void init();
transact方法(往Binder驱动写数据)最后调用为BinderProxy代理类的transact
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
return transactNative(code, data, reply, flags);
}
public native boolean transactNative(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
onTransact方法 (Binder驱动回调数据)
根据定义的常量标识,解析Parcel数据,回调本地方法
@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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getBooks: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.jd.test.myapplication.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.jd.test.myapplication.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.jd.test.myapplication.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
Binder应用层源码实现流程分析
我们了解了Binder通讯的一些基础原理后,通过应用层的调用来追踪整个执行的流程。下面我们通过一个表格索引来描述Binder通讯的结构
索引 | 调用的类间关系 | 说明 |
---|---|---|
1 | Activity:bindService | 执行服务的绑定 |
2 | Context:bindService | 基类的的服务绑定抽象方法 |
3 | ContextImpl:bindService | Context实现类的方法 |
4 | ContextImpl:bindServiceCommon | 进行一些Intent校验等,调用ActivityManagerNative.getDefault().bindService |
5 | ActivityManagerService:bindService | 进行一些合法 非空的校验 |
6 | ActiveServices:bindServiceLocked | 创建Service,对服务进行缓存记录,同时回调了connection方法 |
7 | ActiveServices:requestServiceBindingLocked | 校验服务进程是否存在,调用ApplicationThread的scheduleBindService |
8 | ApplicationThread:scheduleBindService | ApplicationThread是ActivityThread的内部类,该方法发送了一个Message sendMessage(H.BIND_SERVICE, s); |
9 | ActiviThread:handleBindService | 校验Service是否存在,执行AMS的publishService方法 |
10 | ActivityManagerService:publishService | 校验及调用ActiveServices的publishServiceLocked方法 |
11 | ActiveServices:publishServiceLocked | 执行Binder的restoreCallingIdentity方法 |
总结
- 本文对Bidner做了一些整体的介绍,主要是基于应用层的流程进行分析,如果要彻底搞清楚Bidner,可能还需阅读Binder驱动的源码及Bidner的协议等
- Binder在Android体系中,有着非常重要的地位,是核心的IPC方式。如果希望学习Android源码,Binder是一道需要越过去的坎。
网友评论