一 AIDL简介
AIDL这几个缩写字母的意思很简单,安卓接口定义语言,它的作用在我们学习安卓基础四大组件之Service的时候肯定都有所涉猎,只是认知的程度不同而已。读者也是这样,当时只是会使用,然而为什么可以这样使用,它的原理是什么,也是说不出什么来,哈!原谅我学习的时候没有刨根问底。所以我们今天就来深818这个知识点。
AIDL即:Android Interface Definition Language,AIDL出现的原因:
Note: Using AIDL is necessary only
if
you allow clients from different applications to access your service
for
IPC and want to handle multithreading in your service.
“只有当你允许来自不同的客户端访问你的服务并且需要处理多线程问题时你才必须使用AIDL”
1.创建.aidl文件
interface ITestInterface {
String talk(String s);
}
类似于接口但是又有所不同,后缀名为.aidl,接收的参数类型为:所有基础类型(int, char, 等),String,List,Map,CharSequence等类,其他AIDL接口类型,所有Parcelable的类。在Eclipse中会在Gen中自动生成一个java文件,AS中需要手动sync project一下,会生成在app/generated/source/aidl/debug/aidl里面.
2.在我们的Service中使用它
public class TestService extends Service {
public final static String TAG = "TestService";
private IBinder binder = new ITestInterface.Stub() {
@Override
public String talk(String s) throws RemoteException {
Log.i(TAG, s);
return "你好啊,帅哥!";
}
};
@Overrid
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreat");
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
在上面的代码中,显示创建了一个ITestInterface.Stub()的对象,Stub看源码可以知道其实就是继承了Binder,实现ITestInterface接口,重写了talk方法,最后在Service onBind方法中返回它。
Activity中的代码
public class MainActivity extends Activity {
public final static String TAG = "MainActivity";
private ITestInterface myInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myInterface = ITestInterface.Stub.asInterface(service);
Log.i(TAG, "连接Service 成功");
try {
String s = myInterface.talk("我是Activity传来的字符串");
Log.i(TAG, "从Service得到的字符串:" + s);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "连接Service失败");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startAndBindService();
}
private void startAndBindService() {
Intent service = new Intent(MainActivity.this, TestService.class);
//startService(service);
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
这样一个简单的AIDL就已经写完了,接下来我们看看它的内部是怎么实现的。
</pre><p></p><pre>
三,原理
我们是谁先来看看系统为我们生成的Java文件:
public static abstract class Stub extends android.os.Binder implements aidl.ITestInterface {
..........
}
</pre><pre name="code" class="java">..........
public static aidl.ITestInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//检查Binder是不是在当前进程
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.ITestInterface))) {
return ((aidl.ITestInterface) iin);
}
return new aidl.ITestInterface.Stub.Proxy(obj);
}
}
asInterface()此方法就是我们在获取服务器中的IBinder时调用的方法,通过源码我们可以知道这个obj其实就是在Service中onBinder返回来的,在这个方法中会先判断obj是否为null,然后会在当前进程查询Binder是否就在当前进程。如果不在那么就会返回一个Proxy()一个代理对象。
下面就是Proxy类的源码:
private static class Proxy implements aidl.ITestInterface {
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.lang.String talk(java.lang.String s) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(s);
//传送数据到远程的
mRemote.transact(Stub.TRANSACTION_talk, _data, _reply, 0);
_reply.readException();
//接受从远端传回的数据
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_talk = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
通过源码我们可以知道,Proxy实现了我们的接口,并Service中返回的IBinder赋值给了成员变量mRemote,然后复写了talk方法,也就是说我们在asInterface中拿到的对象其实就是Proxy对象,调用的方法也是Proxy复写的方法。然后我们再看看talk()方法,首先在这个方法中获取了两个Parcel对象,从名称我们就可以看出一个用于写,一个用于接收读。第一步会先写入DESCRIPTOR也就是全类名,然后写入s也就是参数,接着调用transact方法传送数据,相应的服务器段会有一个onTransact()方法用于接收,其中第一个参数就是一个整数,其目的是用来标识方法,第二个参数就是Parcel _date,我们写入的数据,第三个参数Parcel _reply用于返回数据,第四个参数就是标识是否有返回值,0代表有返回值。下面就是OnTransact()方法的源码
@Overridepublic 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_talk: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
//取出参数
_arg0 = data.readString();
// 远程服务调用自己本地实现的方法获取返回值
java.lang.String _result = this.talk(_arg0);
reply.writeNoException();
//写入返回值
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact()方法的执行过称大致为:onTransact()接收到有数据传来时,首先会判断是哪个方法,然后取出参数然后调用方法并获取返回值,接着写入返回值,最后会返回true,并把数据发送出去。
所以我们在跨进程,在Activity中使用asInterface()获取Paroxy代理对象并调用talk()方法实际上是先使用Proxy的transact()方法发出数据,然后服务端的 onTransact()接收数据处理,并把返回值写入reply并返回给客户端,客户端拿到这个值并作为在Activity中调用talk()方法的返回值,所以一次完整的AIDL流程大致就是这样。
最后作者也是初学,有很多地方也有疏漏,以往和大家一起学习交流。