Android的进阶学习(四)--AIDL的使用与理解
我最初接触aidl
的时候,就感觉这个好难,一下一大堆的代码就出来了。总觉得这种东西会用就行,懂它什么的因该是大神们的事,但是知其然,不知其所以然,用起来总是觉得怪怪的,所以就决定慢慢理一下它。
其实一步一步看懂,也很简单的。
aidl的使用
最常见的aidl
的使用就是Service
的跨进程通信了,那么我们就写一个Activity
和Service
的跨进程通信吧。
首先,我们就在AS里面新建一个aidl
文件(ps:现在AS建aidl
不要求和java包名相同了):
package aidl;
interface IMyInterface {
String getInfor(String s);
}
可以看到,在这里面我们就一个方法getInfor(String s)
,接受一个字符串参数,然后返回一个字符串,恩,相当的简单。
接着你sync project
一下就可以在app/generated/source/aidl/debug/aidl
里面发现由aidl
文件生成的java
文件了。点进去一看,可能你也被这个貌似'庞大'的类给吓住了,那现在我们就先不管它了。
然后就看看Service
:
public class MyService extends Service {
public final static String TAG = "MyService";
private IBinder binder = new IMyInterface.Stub() {
@Override
public String getInfor(String s) throws RemoteException {
Log.i(TAG, s);
return "我是 Service 返回的字符串";
}
};
@Overrid
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreat");
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
这里我们写了一个Service
,看一下也比较简单。先new
了一IMyInterface.Stub()
并把它向上转型成了IBinder
,最后在onBind
方法中返回回去。可能你注意到了,在IMyInterface.Stub()
的内部我们重写getInfor(String s)
方法,没错这就是我们 aidl
文件中定义的接口。
对了,因为我们希望看到的是跨进程通信,所以我们把MyService
定义成新启动一个进程:
<service
android:name=".server.MyService"
android:process="com.mathiasluo.remote" />
定义为启动在新进程中,只需要在AndroidMainfest.xml
中声明是加上一个process
属性即可,不过这里有两个地方值得注意:1.组件默认的进程名就是包名;2.定义新的进程名的时候需要以包的形式(eg: com.luo.aidl)。
接着,我们继续向下看:
public class MainActivity extends AppCompatActivity {
public final static String TAG = "MainActivity";
private IMyInterface myInterface;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myInterface = IMyInterface.Stub.asInterface(service);
Log.i(TAG, "连接Service 成功");
try {
String s = myInterface.getInfor("我是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, MyService.class);
//startService(service);
bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
由于主要是Service
和Activity
间的通信,所以为了让代码整洁就没有写UI
了。
在onCreate(Bundle savedInstanceState)
中,我们调用了自己定义的一个方法startAndBindService()
,这个方法里面我们生成了一个Intent
,然后 bindService
了这个Intent
传入了三个参数分别是Intent
、ServiceConnection
、Flag
。
Intent
我们就不用说了,我们看看后面两个参数:
在Activity
中,我们new
了一个ServiceConnection
并实现了他的两个方法onServiceConnected
、onServiceDisconnected
。在onServiceConnected
中我们通过IMyInterface.Stub.asInterface(service)
把传来的IBinder
转换成了我们定义的IMyInterface
。然后我们调用了getInfor
方法,传递了个字符串和获取从MyService
传来的字符串,并且打印了Log
。
对于我们传的Context.BIND_AUTO_CREATE
的意思就是说:如果绑定Service的时候,Service还没有被创建,就创建它。当然,这里还有很多Flag
,我也不一一说了。
然后,我们的编码就完成了,开始运行测试一下把!
、
Paste_Image.png Paste_Image.png看一下运行结果,在这两个不同的进程
中都得到了我们想要的结果,所以,一个用aidl
实现的跨进程通信就这样完成了。当然我们的demo(这次不能拼错了)中,getInfor
没有任何逻辑,你也可以加一些逻辑去执行一些复杂的操作。
aidl的理解
现在我们会使用aidl
了,那我们还不去掀开它神秘的面纱。
Service中的IBinder
还记得我们在MyService
中利用new IMyInterface.Stub()
向上转型成了IBinder
然后在onBind
方法中返回的。那我们就看看IMyInterface.Stub
吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
}
可以看到,Stub
是IMyInterface
中的一个静态抽象类,继承了Binder
,并且实现了IMyInterface
接口。这也就解释了我们定义IMyInterface.Stub
的时候为什么需要实现IMyInterface
中的方法了,也说明了为什么我们可以把IMyInterface.Stub
向上转型成IBinder
了。
Activity中的IMyInterface
在Activity
中,通过ServiceConnection
连接MyService
并成功回调onServiceConnected
中我们把传回来的IBinder
通过IMyInterface.Stub.asInterface(service)
转换成为IMyInterface
,那就来看看这里是如何转换的吧:
public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//检查Binder是不是在当前进程
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
return ((aidl.IMyInterface) iin);
}
return new aidl.IMyInterface.Stub.Proxy(obj);
}
}
首先,我们因该明白的是,传回来的IBinder
就是我们在Service
的onBind( )
方法所return
的IBinder
,然后我们调用Stub
中的静态方法asInterface
并把返回来的IBinder
当参数传进去。
在asInterface
方法中,首先判断了传进来的IBinder
是不是null
,如果为null
就返回一个null
;接着就判断传进来的IBinder
是不是就在当前进程里面,如果是的话就直接返回IMyInterface
,不是的话就返回IMyInterface.Stub.Proxy(obj)
。这里我觉得需要明白的是:直接返回的IMyInterface
是实现了定义的接口方法getInfor
的。因为在IMyInterface.Stub
中所实现的。当然如果是在同一进程中,那么我们调用IMyInterface
的方法时就是在本地调用方法,直接调用就可以了。
如果没在同一进程,就会返回IMyInterface.Stub.Proxy(obj)
:
private static class Proxy implements aidl.IMyInterface {
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 getInfor(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_getInfor, _data, _reply, 0);
_reply.readException();
//接受从远端传回的数据
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
在Proxy
中,我们首先把Service
连接成功返回的IBinder
它的内部变量mRemote
,这里在提一下,这里得IBinder
还是是MyService
中onBind
所返回的。然后,当我们调用IMyInterface
的方法的时候,其实就是调用的Proxy
的方法了,这也是为什么这个类叫做Porxy
的原因了。
当调用IMyInterface.getInfor(String s)
,我们就看Proxy
中的getInfor
,先获取了两个Parcel
对象 _data
、_data
,从变量名就可以看出,一个是传送数据的,另一个则是接受返回数据的。接着,向_data
中写入了DESCRIPTOR
(也就是这个类的全名),再写入了方法参数。然后就到了最重要的一步了,
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
这里我们调用了IBinder
的transact
方法,来把数据传给远端的服务器。然后在我们远程的MyService
中,里面的Stub
中就会回调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_getInfor: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
//取出参数
_arg0 = data.readString();
// 远程服务调用自己本地实现的方法获取返回值
java.lang.String _result = this.getInfor(_arg0);
reply.writeNoException();
//写入返回值
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
onTransact
方法是在Stub
的内部实现的。
先看一下它的四个参数:
code
:每个方法都有一个int
类型的数字用来区分(后面中的swicth),在我们例子中也就是我们Proxy中的Stub.TRANSACTION_getInfor
。
data
:传过来的数据,其中包含我们的参数,以及类的描述。
reply
:传回的数据,我们要写入是否发生了Exception
,以及返回值
flags
:该方法是否有返回值 ,0
表示有返回值。
调用onTransact
就表示有数据传来,首先就会通过swicth
判断是哪个方法,然后取出方法参数,调用本地实现的方法获取返回值,写入返回值到reply
。最后,返回true
,才会把数据发送出去,发挥false
就不会把结果返回给Activity
了。这里也就是说,只有返回true
,我们Proxy
中才能接受从远端传回的数据
。
//传送数据到远程的
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
_reply.readException();
//接受从远端传回的数据
_result = _reply.readString();
注意:Service
也是把数据发送出来,让客户端接受的。
Service
发出了数据,客户端接收到了,就会一层一层返回去。所以,当我们简单的调用IMyInterface
的getInfor
时候,先是Proxy
的transact
发送出数据,然后服务端的onTransact
接受并处理传来的数据,再把处理得到的数据写入返回值并发送给客户端,客户端读取值后就成为调用方法的返回值返回了。
然后aidl
的基本原理就是这样了,看明白了aidl
,才发现原来aidl
不过就是帮我们生成了那些数据写入,传送,读取的方法而已。所以,我们自己写一个不要aidl
的跨进程通信也是很容易的。
最后
我一个苦逼的大学生,而且专业还不对口,希望写写东西,加强理解与记忆,同时也希望自己的理解能帮到更多的人。所以,有什么错误的,写得不好的,希望指出,共同进步。