AIDL
和Service
进行跨进程通讯,并总结。AIDL
和Java
比较类似,但更简单,因为AIDL
只是作为两个原本互不影响的进程之间相互“沟通”的工具,基本只做数据的传递,不使用AIDL做逻辑处理。
AIDL
支持的数据类型有:
byte、short、int、long、float、double、boolean、char
String、CharSequence、List、Map
.aidl
文件的interface类型(接口类,用于进程间互相调用、回调).aidl
文件的parcelable类型(数据类,需要配合同包路径的.java
数据类,这个.java
数据类需要实现Parcelable
序列化)特别说明:
String、CharSequence类型的参数的定向tag默认为in,并且只能是 in
AIDL
文件一般有两大类:数据类,接口类(包含两类:交互接口,回调接口)
自定义AIDL文件的存放路径,如 src/main/java/
包下任意位置,创建一个aidl
文件夹
并任意创建一个.aidl
文件
在build.gradle
中配置 aidl.srcDirs
在aidl文件夹中,任意创建一个aidl
文件,并点击Android Studio
工具栏“Build–>Make Project”或“clean Project”,等候编译完成,将会在 app\build\generated\source\aidl\debug\
下自动生成对映的.java
文件,此文件请勿做任何修改
至此,AIDL文件已可正常编译
模拟一个简单的音乐播放demo,但这里不会真正得实现音乐播放功能,只是实现App进程(客户端)
和单独的Service进程(播放服务端)
之间的交互和回调。
package com.sp.personal.music.aidl;
parcelable Music;
Parcelable
自动生成代码插件可通过“File–>Settings–>Plugins–>Browse repositories…”,输入搜索“Android Parcelable code generator”,安装即可。Music.java
需要和Music.aidl
在同一个包里Music.java
文件就不贴代码了,比较通常的一个数据类。供Service
进程(播放服务端)回调的接口
定义一个PlayCallback.aidl
文件
package com.sp.personal.music.aidl;
interface PlayCallback {
//state:1,onPrepare;2,onStart;3,onPlaying;4,onPause;5,onEnd
void stateNow(int state, String songId, int position);
}
供App进程(客户端)调用的接口AIDLPlayService.aidl
package com.sp.personal.music.aidl;
import com.sp.personal.music.aidl.Music;
import com.sp.personal.music.aidl.PlayCallback;
interface AIDLPlayService{
void prepare(in Music music);
void start();
void seekTo(String id, int position);
void pause(String id);
void addPlayCallback(PlayCallback playCallback);
void removePlayCallback(PlayCallback playCallback);
}
这里需要注意的是:
* import,是必须的,无论是否在同一个包中
* 自定义类型需要指定其参数“定向”,这里Music music
实例由App进程(客户端)
传递过来,则指定为in
用于播放音乐的单独进程的Service
,以及绑定AIDL
接口到Service
<service
android:name=".code.playctrl.PlayService"
android:process=":remote"/>
(使用remote
是自定义的,可以随意。冒号:
这个前缀是必须的,它将把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称)
public class PlayService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//将AIDLPlayService作为IBinder返回
return mIBinder;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
}
//创建一个RemoteCallbackList用来管理PlayCallback,用来通过Broadcast发送回调到客户端
RemoteCallbackList remoteCallbackList = new RemoteCallbackList<>();
//服务端用来接收客户端的请求
AIDLPlayService.Stub mIBinder = new AIDLPlayService.Stub() {
@Override
public void prepare(Music music) throws RemoteException {
//接收到准备播放的音乐对象
}
@Override
public void start() throws RemoteException {
//接收到客户端的请求,开始播放
}
@Override
public void pause(String id) throws RemoteException {
//接收到客户端的请求,暂停播放
//暂停播放的测试:暂停后客户端将收到已暂停的回调通知
pausePlay(id);
}
@Override
public void addPlayCallback(PlayCallback playCallback) throws RemoteException {
if (playCallback != null) {
//客户端设置回调,这里使用RemoteCallbackList注册这个PlayCallback
remoteCallbackList.register(playCallback);
}
}
@Override
public void removePlayCallback(PlayCallback playCallback) throws RemoteException {
if (playCallback != null) {
//客户端注销回调,这里使用RemoteCallbackList注销这个PlayCallback
remoteCallbackList.unregister(playCallback);
}
}
@Override
public void seekTo(String id, int position) throws RemoteException {
//接收到客户端的请求,播放到指定位置(或时间)
}
};
//举例,测试回调,将在客户端代码中打印
private void pausePlay(String id) {
//假如已知目前播放的位置是:position=100(秒)
int position = 100;
playCallback(4, id, position);
}
/**
*调用回调到客户端
* @param state 1,onPrepare;2,onStart;3,onPlaying;4,onPause;5,onEnd
* @param songId
* @param position
*/
private void playCallback(int state, String songId, int position) {
//准备开始调用最近注册的Callback并且返回,已注册playCallback数(在客户端注册),目前是1个
int N = remoteCallbackList.beginBroadcast();
for (int i = 0; i < N; i++) {
try {
remoteCallbackList.getBroadcastItem(i).stateNow(state, songId, position);
} catch (RemoteException e) {
e.printStackTrace();
}
}
//必须的,和beginBroadcast()成对出现
mPlayCallback.finishBroadcast();
}
}
public class PMApplication extends Application {
public static AIDLPlayService aidlPlayService;
@Override
public void onCreate() {
super.onCreate();
initPlayService();
}
private void initPlayService() {
Intent intent = new Intent(this, PlayService.class);
this.startService(intent);
this.bindService(intent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
aidlPlayService = AIDLPlayService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
aidlPlayService = null;
}
}, Context.BIND_AUTO_CREATE);
}
}
pause
方法测试回调try {
PMApplication.aidlPlayService.addPlayCallback(new PlayCallback.Stub() {
@Override
public void stateNow(int state, String songId, int position) throws RemoteException {
//测试是否收到回调,打印
Log.d("Play","state=" + state + ";songId=" + songId + ";position=" + position);
}
});
PMApplication.aidlPlayService.pause("123456");
} catch (RemoteException e) {
e.printStackTrace();
}
addPlayCallback
只能调用一次,如不需要使用该回调时,需要注销这个回调removePlayCallback
,否则可能导致回调的信息混乱或之前已完成的回调信息再次回调。其中的原因是RemoteCallbackList
中的消息机制,允许注册多个回调并存(请见RemoteCallbackList
源码)。以上经过测试,服务端可以收到客户端的请求,并可以回调给客户端数据,已完成了基本的跨进程AIDL
数据交互。如一般的音乐播放器完全可以按照这样的交互流程进行处理。