Android开发艺术探索的代码尝试----AIDL篇
前言
本次主要记录Android中IPC方式之一,AIDL的使用。本文内容没有涉及AIDL的源码内容。
提示:以下是本篇文章正文内容,下面案例可供参考
一、AIDL是什么?
AIDL是**IPC(进程间通信方式)**的一种。 AIDL偏向于底层,它的优点是支持并发,应用面广。但是代码的编写比较繁琐。
二、本次代码的实现目标
- 创建一个Activity作为进程1
- 创建一个Service作为进程2
- Service中有一个变量mode,作为我们的通信目标。
- 所以Activity需要获取和修改 mode(Activity向Service通信)
- Service需要通知Activity,mode已被修改。(Service向Activity通信)
三、实现步骤
基本配置
Activity
- 默认的Activity模板
- 3个Button:绑定服务、获取mode、改变mode
Service
- 默认的Service模板
- 配置一个mode变量作为通信目标
配置多进程环境
关键词:Android:process字段
在AndroidManiFest文件中修改Activity和Service的配置,添加Android:process字段,process的值表示进程名。
<application
...
<service
android:name=".Serivice.IPCService"
android:enabled="true"
android:exported="true"
android:process=":secondProcess"/>
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
...
</application>
编写AIDL文件
一个接口需要对应一个AIDL文件。
故,我们需要两个接口。一个给Activity使用,一个给Service使用。
在编写完AIDL文件后,我们需要点击build,从而生成java文件。本质上我们是使用这个java文件。 AIDL文件的编写规范:
IMyAidlInterface.aidl
package com.example.demo.AIDL;
import com.example.demo.AIDL.IAidlInterface_Listener;
interface IMyAidlInterface {
void registerListener(IAidlInterface_Listener listener);
void changeMode(int mode);
int getMode();
IAidlInterface_Listener.aidl
package com.example.demo.AIDL;
interface IAidlInterface_Listener {
void onSecondProcessChanged();
}
在Service中实现get和change的AIDL接口
将AIDL接口中的Stub转为Service中的binder,并实现接口方法
Stub是AIDL接口的内部类,继承了IBinder
public void init(){
Mode=0;
mBinder1 = new IMyAidlInterface.Stub() {
@Override
public void registerListener(IAidlInterface_Listener listener) throws RemoteException {
mListener = listener;
}
@Override
public void changeMode(int mode) throws RemoteException {
Mode=mode;
mListener.onSecondProcessChanged();
Log.d(TAG, "changeMode: mode ="+Mode);
}
@Override
public int getMode() throws RemoteException {
return Mode;
}
};
mBinder2 = new mBinder();
}
在Activity中获取AIDL接口的实例并实现Listener的AIDL接口
关键点:使用stub的asInterface()将IBinder转为AIDL接口实例,此时可以调用接口方法。
- 绑定Service后获取到Service传来的Binder,利用该Binder获取到AIDL的接口实例(具体如何获取见总结)。
- 实现Listener的AIDL接口,实例化Listener,通过Service的接口 传输 这个Listener。
服务绑定
private IMyAidlInterface iMyAidlInterface=null;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
try {
mode = iMyAidlInterface.getMode();
iMyAidlInterface.registerListener(mListener);注册监听器
Log.d(TAG, "连接服务成功: 注册监听");
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
监听器
private IAidlInterface_Listener mListener = new IAidlInterface_Listener.Stub() {
@Override
public void onSecondProcessChanged() throws RemoteException {
Log.d(TAG, "onSecondProcessChanged: ");
}
};
通信测试
Activity Log
- 点击Bind
进程1 进程2 - 获取及修改mode
总结
代码尝试到此处,如果对IPC没有深入了解的话,想必会有如下几个疑问:
两个进程间如何传递对象?(本例中传递了监听器)
本质上并没有传递对象,两个进程间的对象不是同一个对象(hashcode不一致)。因为多进程可以看作是两个程序在运行,对于android而言,他会为每一个进程分配虚拟机和内存空间。 那么这个对象是如何传递的?—是依靠序列化和反序列化。 所以这里一个前提,两个进程的代码中需要有一样的aidl包结构,显而易见这是为了序列化和反序列化。
既然进程间传递的对象不是同一个对象,如何保证它能实现需要的功能?(本例中的监听器实例)
这个问题可以转化为----一个实例化的接口对象,在经过序列化和反序列化后还能不能调用重写的接口方法。 答案是,能。
|