一 场景需求
这里假设我要写一个音乐服务,如果我仅仅提供简单的播放和停止功能,不是用java对象的话是没有问题的,我们很容易就将一个接口转化为aidl文件了。但是如果方法的参数或返回值包含自定义对象的话在你将一个接口类改为aidl文件时就会报错。就像在我写的demo中,我的系统服务提供了获得播放列表的功能,由于每首歌会有许多信息,因此我把它包装成了对象,这时该怎么处理呢。
注:我定义aidl的方法都是先写一个java接口类,然后将.java的后缀该为.aidl,让后再将.aidl文件中的public修饰符去掉。
二 服务端编写
编写服务端暴漏的方法的接口类
public interface MusicServiceAidl{
public List<Song> getMusicList();
publicvoid playSong(int index);
public void changeSong(int flag);
public void pauseSong();
}
改写为.aidl文件
interface MusicServiceAidl{
List<Song> getMusicList();
void playSong(int index);
/**
* 传入一个整型的参数,为0则切换为当前的歌曲,小于0则切换到上一首,大于0则切换到下一首
* @param flag
*/
void changeSong(int flag);
void pauseSong();
}
这时打开gen目录,就会看到自动生成的MusicServiceAidl 类了,这个类中有个内部类stub,他继承了Binder对象,实现了你上面定于的接口
public static abstract class Stub extends android.os.Binder implements com.MusicPlayer.aidl.MusicServiceAidl
在服务端的service对象中我们会复写onBind方法,在这个方法中要返回一个IBinder对象,这个IBinder对象不再是继承Binder类了,而是实现上面生成的MusicServiceAidl的内部类Stub。
@Override
public IBinder onBind(Intent intent) {
binder = new MusicIBinder(this);
return binder;
}
在上面的接口中,getMusicList()返回的是一个Song对象的集合,但是,我们虽然定义了Song对象,上面生成的接口类仍然报错,这个就是aidl传输对象需要解决的问题。
我们让我们写的Song对象实现Percelable接口
实现未实现的方法
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(_id);
dest.writeString(title);
dest.writeString(artist);
dest.writeString(path);
dest.writeLong(duration);
dest.writeLong(size);
}
编写与Song同名的aidl文件(一定要与传递的对象同名)
在文件中只写一句代码
parcelable Song;
这时MusicServiceAidl.aidl文件就不再报错了,
但是在gen目录下生成的MusicServiceAidl.java却还是报错,仔细查看发现是缺少CREATOR对象,这时就在Song对象中添加CREATOR对象
public static Creator<Song> CREATOR = new Creator<Song>() {
@Override
public Song[] newArray(int size) {
return new Song[size];
}
@Override
public Song createFromParcel(Parcel source) {
long _id = source.readLong();
String title = source.readString();
String artist = source.readString();
String path = source.readString();
long duration = source.readLong();
long size = source.readLong();
Song song = new Song();
song.set_id(_id);
song.setTitle(title);
song.setArtist(artist);
song.setPath(path);
song.setDuration(duration);
song.setSize(size);
return song;
}
};
上面红色标注的地方的writeToParcel和createFromParcel方法读写顺序必须要一致。
至此服务端的代码就写完了。
三客户端代码
将服务端编写的aidl和要传递的对象带着包名一起复制到客户端的代码中。报名一定不要改。
注意:客户端是通过服务端的service的action绑定的。
Intent service = new Intent();
service.setAction("com.archermind.ACTION_MUSIC_PLAYER");
bindService(service, conn, Context.BIND_AUTO_CREATE);
在绑定服务时,要传递一个ServiceConnection对象,对象定于如下。
private MusicServiceAidl musicService;
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
musicService = (MusicServiceAidl)MusicServiceAidl.Stub.asInterface(service);
try {
playList = musicService.getMusicList();
} catch (RemoteException e) {
e.printStackTrace();
}
lv.setAdapter(myAdapter);
lv.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
try {
musicService.playSong(position);
} catch (RemoteException e) {
e.printStackTrace();
}
play.setText("暂停");
isPlaying = true;
}
});
}
};
注意:不要直接爆service强转成MusicService,而是调用MusicServiceAidl.Stub.asInterface(service)方法获得。
得到了接口对象了,就可以调用接口里的方法了。
playList = musicService.getMusicList();