您的位置:首页 > 大数据 > 人工智能

理解AIDL原理以及系统生成的源码

2016-12-16 15:31 465 查看
工具好用,但是会有一个缺点,就是会让你不得不依赖说明书。对于我这个记忆力很差的人来说,每次使用工具都要去翻说明书是一件极其痛苦的事情,而且还有很多坑会忘记怎么填。基于此,我很多时候会觉得不如自己去理解一下工具制造的方法,然后自己造一个好了,好像我的大脑是对这种东西印象特别深刻,所以下次遇到问题我写个工具直接用的效率,比查说明书再重新趟一遍坑要高不少。今天,我要把aidl这个工具给拆了。

Part1. 原理

讲到进程间通信的原理,要先提到一个COM的概念——Proxy/Stub结构(代理/存根结构) 上图:



说个好理解的例子:

你到自动取款机上去取款;你就是客户,取款机就是你的代理;你不会在乎钱具体放在那里,你只想看到足够或更多的钱从出口出来(这就是com的透明性)。你同银行之间的操作完全是取款机代理实现。(这里就是Activity)

你的取款请求通过取款机,传到另一头,银行的服务器(这里就是远程的Service),他也没有必要知道你在哪儿取钱,他所关心的是你的身份,和你取款多少。当他确认你的权限,就进行相应的操作,返回操作结果给取款机,取款机根据服务器返回结果,从保险柜里取出相应数量的钱给你。你取出卡后,操作完成。

取款机不是直接同服务器连接的,他们之间还有一个“存根”,取款机与存根通信,服务器与存根通信。从某种意义上说存根就是服务器的代理。这个存根的英文就会Stub,我们生成的aidl类里,就是Stub + Proxy,后面会看到。

Android就是在传统的C/S架构中加入了一层,实现IPC。

Part2. 源码分析

先看一个标准的通过工具生成的aidl类和它里面的javabean,大家都知道这个bean必须继承Parcelable接口,至于aidl类怎么生成,文件怎么写,如果你不知道我觉得这文章你不必往下看了:

/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: D:\\Develop\\workspaceForJob\\AndroidTest\\src\\com\\amuro\\aidl\\IPersonManager.aidl
*/
package com.amuro.utils;

public interface IPersonManager extends android.os.IInterface
{
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.amuro.aidl.IPersonManager
{
private static final String DESCRIPTOR = "com.amuro.aidl.IPersonManager";

/**
* Construct the stub at attach it to the interface.
*/
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.amuro.aidl.IPersonManager interface,
* generating a proxy if needed.
*/
public static com.amuro.aidl.IPersonManager asInterface(android.os.IBinder obj)
{
if ((obj == null))
{
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.amuro.aidl.IPersonManager)))
{
return ((com.amuro.aidl.IPersonManager) iin);
}
return new com.amuro.aidl.IPersonManager.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_save:
{
data.enforceInterface(DESCRIPTOR);
com.amuro.entity.Person _arg0;
if ((0 != data.readInt()))
{
_arg0 = com.amuro.entity.Person.CREATOR.createFromParcel(data);
}
else
{
_arg0 = null;
}
this.save(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.amuro.aidl.IPersonManager
{
private android.os.IBinder mRemote;

Proxy(android.os.IBinder remote)
{
mRemote = remote;
}

@Override
public android.os.IBinder asBinder()
{
return mRemote;
}

public String getInterfaceDescriptor()
{
return DESCRIPTOR;
}

@Override
public void save(com.amuro.entity.Person person) 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 ((person != null))
{
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else
{
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_save, _data, _reply, 0);
_reply.readException();
}
finally
{
_reply.recycle();
_data.recycle();
}
}
}

static final int TRANSACTION_save =
(android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

public void save(com.amuro.entity.Person person) throws android.os.RemoteException;
}


package com.amuro.entity;

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

public class Person implements Parcelable
{
private int id;
private String name;

public Person()
{
}

public Person(int id, String name)
{
this.id = id;
this.name = name;
}

public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

@Override
public int describeContents()
{
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags)
{// 把javanbean中的数据写到Parcel
dest.writeInt(this.id);
dest.writeString(this.name);
}

// 添加一个静态成员,名为CREATOR,该对象实现了Parcelable.Creator接口
public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>()
{
@Override
public Person createFromParcel(Parcel source)
{// 从Parcel中读取数据,返回person对象
return new Person(source.readInt(), source.readString());
}

@Override
public Person[] newArray(int size)
{
return new Person[size];
}
};
}


好,我们一点点来分析:

1.首先我们的接口要继承IInterface这个接口,这个接口干嘛的呢,看注释:Base class for Binder interfaces. When defining a new interface, you must derive it from IInterface。简单来说就是只要跨进程,就必须继承这个接口。

2.这个接口中除了我们定义的方法外,还帮我们生成了两个内部类,一个是Stub,一个是Proxy,stub是存根的意思,具体怎么理解不要纠结,这种东西意会就好了。先说结论,Stub是给服务端用的,Proxy是给客户端用的。事实也是这样,我们看一下Service和Activity中的代码。

public class TestService extends Service
{
@Nullable
@Override
public IBinder onBind(Intent intent)
{
return new IPersonManager.Stub()
{
@Override
public void save(Person person) throws android.os.RemoteException
{
//do your work
}
};
}
}


/**
* Created by Amuro on 2016/12/15.
*/
public class TestActivity extends Activity
{

ServiceConnection connection1 = new ServiceConnection()
{
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
IPersonManager manager = IPersonManager.Stub.asInterface(service);

try
{
manager.save(new Person());
}
catch (RemoteException e)
{
e.printStackTrace();
}

}

@Override
public void onServiceDisconnected(ComponentName name)
{

}
};

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);

findViewById(R.id.bt).setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Intent intent = new Intent(TestActivity.this, TestService.class);
bindService(intent, connection1, BIND_AUTO_CREATE);
}
});
}

@Override
protected void onDestroy()
{
super.onDestroy();
unbindService(connection);
}
}


3.回来我们继续看源码,先看Stub,Stub继承了Binder,所以具有了跨进程能力。

1)首先看构造函数,里面调用了attachInterface方法,这个方法传入了两个参数,一个是IInterface,也就是IPersonManager本身,一个是DESCRIPTOR,是一个包名,同时也起到标识的作用,只有transact和onTransact方法中parcel对象的一致时才能成功调用。这也是为什么我们要求Server端和Client端aidl存放目录结构一致的原因,不一致的话这个自动生成DESCRIPTOR也会不一样;

2)第二个方法是系统为我们自己生成的,asInterface,这个方法非常重要,我看可以看到客户端是通过这个方法拿到一个“IPersonManager”的,到底拿到的是个啥呢,看源码:

public static com.amuro.aidl.IPersonManager asInterface(android.os.IBinder obj)
{
if ((obj == null))
{
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.amuro.aidl.IPersonManager)))
{
return ((com.amuro.aidl.IPersonManager) iin);
}
return new com.amuro.aidl.IPersonManager.Stub.Proxy(obj);
}


可以看到这里的逻辑是,asInterface根据传入的binder来判断当前是否是跨进程,如果跨进程则返回一个Proxy对象,如果不是跨进程则直接返回当前接口实现类(相当于调用本地接口),这个结论我相信很多人都知道,但源码里是怎么运行得到这个结果的呢?这里我们可以顺便看下queryLocalInterface这个方法的源码:

Binder中:

public IInterface queryLocalInterface(String descriptor)
{
if (mDescriptor.equals(descriptor))
{
return mOwner;
}
return null;
}


BinderProxy中:

public IInterface queryLocalInterface(String descriptor)
{
return null;
}


这里也就能确定客户端拿到的一定是一个Proxy了,因为ServiceConnection返回的IBinder就是一个BinderProxy,queryLocalInterface后必然返回null,分支必然走到返回Proxy的那段代码。

3)asBinder是IInterface的回调方法,这里返回自己就行了。

4)下面是最重要的onTransact方法,这个方法会在客户端调用tansact方法时被回调,先放着,等讲Proxy之后再一起分析。

4.下面来看Proxy,这个类学过模式的童鞋一眼就能看出来是一个代理模式,里面放的IBinder就是客户端拿到的BinderProxy,主要看save方法:

@Override
public void save(com.amuro.entity.Person person) 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 ((person != null))
{
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else
{
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_save, _data, _reply, 0);
_reply.readException();
}
finally
{
_reply.recycle();
_data.recycle();
}
}


首先准备好两个Parcel对象,一个存放传参,一个存放结果。

首先在data中写入DESCRIPTOR,确保对象正确,然后传参不为null的话,写入1并调用传参的writeToParcel方法来把具体类的数据写入data,最后就调用mRemote的transact方法,注意这个方法的第一个参数,其本质就是个方法flag,因为我们一个接口往往具有多个方法,所以需要flag作为唯一标识,这个flag在哪里被用到呢?没错就是Stub中的onTransact方法。

5.回去看Stub中的onTransact方法:

@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_save:
{
data.enforceInterface(DESCRIPTOR);
com.amuro.entity.Person _arg0;
if ((0 != data.readInt()))
{
_arg0 = com.amuro.entity.Person.CREATOR.createFromParcel(data);
}
else
{
_arg0 = null;
}
this.save(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}


看到switch的case分支的时候,刚才那个方法的flag就对上号了吧。没错,最终就是这里被回调,TRANSACTION_save分支会首先根据proxy之前在data中写入的int值来判断传参是否为空,如果不是,会调用我们自己写Parcel类时里面的CREATOR的createFromParcel方法,其本质,就是生成了一个内存不一样但内部数据一样的Person对象,最后把这个对象传给了我们真正的save方法,执行回调。

其实中Proxy的transact到Stub的onTransact的过程中,android系统帮我们做了很多很复杂的事情,有兴趣的朋友可以追到native层去看transactNative方法,这里就不赘述了。

Part3:知道原理有啥用

最直观的作用就是可以让我们跳出工具,自己去实现一个aidl的接口,在例如SDK这样只允许使用代码的条件下。

其实最重要的是,在我们app运行的过程中会有大量的跨进程调用,无论四大组件甚至是window的操作,熟悉aidl源码可以让我们更好的理解这些源码的调用过程,理解操作系统的运行机理和设计思想,这都是一个优秀的程序员必须具备的素质。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  aidl 源码 binder
相关文章推荐