赞
踩
在上一篇文章中 Android AIDL 教程 (一)—— 简单的示例,我们介绍了怎样使用 AIDL 进行进程间的通讯,并简单写了一个 Demo,今天,让我们一起来学习怎样在 AIDL 中传递对象。
回顾,在上一篇博客中,我们讲到 AIDL 支持以下类型。
其实 aidl 传递对象就是通过 Parceable 将对象序列化。
下面我们以客户端到服务端搜索是否有相应的歌曲为例子讲解。
在上一篇博客已经说到,服务端主要有三个步骤
首先我们先来看一下 IPlayService aidl 文件,下面的代码中,我们定义了一个 play 方法,有两个参数,name 是代表歌曲的名字,IPlayListener 是一个接口。需要注意的是它不是一个 java 类,是 aid 文件l 。这样才能在服务端和客户端之间传递
package xj.musicserver;
// Declare any non-default types here with import statements
import xj.musicserver.IPlayListener;
interface IPlayService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void play(String name,IPlayListener iPlayListener);
}
package xj.musicserver; // Declare any non-default types here with import statements import xj.musicserver.MusicInfo; interface IPlayListener { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void onError(int code); void onSuccess(int code,in MusicInfo musicInfo); }
接下来我们再来看一下我们的实体类 MusicInfo,实现了 Parceable 接口
//下面是自定义的一个MusicInfo子类,实现了Parcelable public class MusicInfo implements Parcelable { private long id; private String title; private String album; private int duration; private long size; private String artist; private String url; private String displayName; public MusicInfo(long id, String title, String album, int duration, long size, String artist, String url, String displayName) { this.id = id; this.title = title; this.album = album; this.duration = duration; this.size = size; this.artist = artist; this.url = url; this.displayName = displayName; } public MusicInfo(){ } protected MusicInfo(Parcel in) { id = in.readLong(); title = in.readString(); album = in.readString(); duration = in.readInt(); size = in.readLong(); artist = in.readString(); url = in.readString(); displayName = in.readString(); } //必须提供一个名为CREATOR的static final属性 该属性需要实现android.os.Parcelable.Creator<T>接口 public static final Creator<MusicInfo> CREATOR = new Creator<MusicInfo>() { @Override public MusicInfo createFromParcel(Parcel in) { return new MusicInfo(in); } @Override public MusicInfo[] newArray(int size) { return new MusicInfo[size]; } }; public MusicInfo(long id, String title) { this.id=id; this.title=title; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(id); dest.writeString(title); dest.writeString(album); dest.writeInt(duration); dest.writeLong(size); dest.writeString(artist); dest.writeString(url); dest.writeString(displayName); } public void readFromParcel(Parcel reply) { id=reply.readLong(); title=reply.readString(); album=reply.readString(); duration=reply.readInt(); size=reply.readLong(); artist=reply.readString(); url=reply.readString(); displayName=reply.readString(); } }
接下来看 writeToParcel 和 readFromParcel 方法,需要注意的是 writeToParcel 和 readFromParcel 方法读写的顺序是一一对应的。
这里有一点要提醒大家的是 AndroidStudio 中,我们通过插件会自动帮我们生成 writeToParcel 方法及 CREATOR,通常 readFromParcel 方法是不会自动生成的,需要我们自己手动编写,不然会编译不过。
@Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(id); dest.writeString(title); dest.writeString(album); dest.writeInt(duration); dest.writeLong(size); dest.writeString(artist); dest.writeString(url); dest.writeString(displayName); } public void readFromParcel(Parcel reply) { id=reply.readLong(); title=reply.readString(); album=reply.readString(); duration=reply.readInt(); size=reply.readLong(); artist=reply.readString(); url=reply.readString(); displayName=reply.readString(); }
注意了,接下来我们需要写一个 MusicInfo.aidl 文件
package xj.musicserver;
// Declare any non-default types here with import statements
parcelable MusicInfo;
指定包名,并声明 MusicInfo 是 parcelable,注意 parcelable 是小写的 p,不是大写的 P。这是一个规范,google 官方指定需要的。同时 MusicInfo.aidl 和 MusicInfo.java 需要放置在同个包中。
关于怎样在 AndroidStudio 中配置 aidl 的可以参考我的这一篇博客。AndroidStudio 引用 aidl 文件的两种方法
IPlayService.Stub mIPlayService=new IPlayService.Stub() { @Override public void play(String name, final IPlayListener iPlayListener) throws RemoteException { MusicTask musicTask = new MusicTask(getApplicationContext(), name, ""); musicTask.setIResultListener(new MusicTask.IResultListener() { @Override public void onSuccess(MusicInfo musicInfo) { try { iPlayListener.onSuccess(0,musicInfo); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onFail(int code, MusicInfo musicInfo) { try { iPlayListener.onError(0); } catch (RemoteException e) { e.printStackTrace(); } } }); musicTask.execute(); } }; @Nullable @Override public IBinder onBind(Intent intent) { LogUtil.i(TAG, "onBind: intent = " +intent.toString()); return mIPlayService; }
这里我们所做的工作就是到数据库里面查询看是否有相应的歌曲,如果有,通过 aidl 回调,告诉客户端我们查找成功,调用 onSuccess 方法,没有找到,调用客户端的 onError 方法。
package xj.musicserver; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; /** * @author meitu.xujun on 2017/10/17 * @version 0.1 */ public class MusicTask extends AsyncTask<Void,Void,Integer> { // 这里只贴出主要代码,详细代码可到文章的末尾下载。 public MusicTask(Context context, String name, String artist){ mContext = context.getApplicationContext(); mName = name; mArtist = artist; } @Override protected Integer doInBackground(Void... params) { LogUtil.i(TAG,"doInBackground: mName="+mName +" mArtist"+mArtist); mResult = ""; ContentResolver contentResolver = mContext.getContentResolver(); Cursor cursor; if (TextUtils.isEmpty(mArtist)) { cursor = contentResolver.query(contentUri, projection, where_title, new String[]{getFixName(mName)},null); }else{ cursor=contentResolver.query(contentUri, projection, where_title_and_artist, new String[]{getFixName(mName),getFixName(mArtist)},null); if(cursor==null || cursor.getCount()<=0){ cursor = contentResolver.query(contentUri, projection, where_title, new String[]{getFixName(mName)},null); } } if(cursor==null || cursor.getCount()<=0){ return RESULT_FAIL_MUSIC_NULL; } int displayNameCol = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME); int albumCol = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM); int idCol = cursor.getColumnIndex(MediaStore.Audio.Media._ID); int durationCol = cursor.getColumnIndex(MediaStore.Audio.Media.DURATION); int sizeCol = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE); int artistCol = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST); int urlCol = cursor.getColumnIndex(MediaStore.Audio.Media.DATA); int titleCol = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE); mMusicInfos = new ArrayList<>(); String songName=""; while (cursor.moveToNext()){ songName = cursor.getString(titleCol); MusicInfo musicInfo = getMusicInfo(cursor, displayNameCol, albumCol, idCol, durationCol, sizeCol, artistCol, urlCol,titleCol); mMusicInfos.add(musicInfo); if(songName.equals(mName)){ mResult =mName; mMusicInfo=musicInfo; break; } } if(mMusicInfo==null){ mMusicInfo =mMusicInfos.get(0); } return RESULT_SUCUESS; } @Override protected void onPostExecute(Integer result) { super.onPostExecute(result); Log.i(TAG, "onPostExecute: result =" +result); if(mIResultListener==null){ return; } if(result==RESULT_SUCUESS){ mIResultListener.onSuccess(mMusicInfo); }else{ mIResultListener.onFail(result,mMusicInfo); } } ----- public void setIResultListener(IResultListener IResultListener) { mIResultListener = IResultListener; } public interface IResultListener{ void onSuccess(MusicInfo musicInfo); void onFail(int code, MusicInfo musicInfo); } }
<service
android:name=".PlayService"
android:exported="true"
android:process=":remote">
<intent-filter>
<action android:name="xj.musicserver.IPlayService"/>
</intent-filter>
</service>
到这里我们服务端的配置就完成了
在上一篇博客的时候,我们有讲到实现客户端大概需要几个步骤:
第一步:将服务端的 aidl 文件 copy 过来,注意要放在同一个包下。
如下图所示,我们将 IPlayListener.aidl,IPalyService.aidl,MusicInfo.aidl 和 MuicInfo.java copy 到客户端
第二步:通过服务端 Service 的 Action 启动, 当启动 Service 成功的时候,将服务端返回的 Binder 保存下来并转化成相应的实例。
这里的 Action 是与服务端一一对应的。
case R.id.btn_start_service:
LogUtil.i(TAG,"onButtonClick: btn_start_service=");
Intent intent = new Intent(ACTION);
intent.setPackage(XJ_MUSICSERVER);
bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
public static final String ACTION = "xj.musicserver.IPlayService";
public static final String XJ_MUSICSERVER = "xj.musicserver";
第三步:通过第二步保存下来 的 mIBinder,与服务端进行通讯。
当我们调用 mIPlayService.play 方法的时候,服务端会去查找本地是否存在 丑八怪 这首歌,查找到的时候会回调 onSuccess 方法,查找不到的时候会回调 onError 方法。
case R.id.btn_contact: LogUtil.i(TAG,"onButtonClick: btn_contact="); if(mIPlayService!=null){ mIPlayService.play("丑八怪", mPlayListener); } IPlayListener.Stub mPlayListener=new IPlayListener.Stub(){ @Override public void onError(int code) throws RemoteException { LogUtil.i(TAG,"onError: code = "+code); } @Override public void onSuccess(int code, MusicInfo musicInfo) throws RemoteException { LogUtil.i(TAG,"onSuccess: code = "+code+ " musicInfo" + musicInfo.toString()); } };
到此这篇博客为止。
最后的最后,卖一下广告,欢迎大家关注我的微信公众号,扫一扫下方二维码或搜索微信号 stormjun,即可关注。 目前专注于 Android 开发,主要分享 Android开发相关知识和一些相关的优秀文章,包括个人总结,职场经验等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。