AIDL 傳遞對(duì)象(Parceable)暇赤、傳遞接口

通常心例,AIDL 支持以下類型。

  • Java 編程語(yǔ)言中的所有原語(yǔ)類型(如 int鞋囊、long契邀、char、boolean 等等)
  • String
  • CharSequence
  • List
    List 中的所有元素都必須是以上列表中支持的數(shù)據(jù)類型失暴、其他 AIDL 生成的接口或您聲明的可打包類型坯门。 可選擇將 List 用作“通用”類(例如微饥,List)。另一端實(shí)際接收的具體類始終是 ArrayList古戴,但生成的方法使用的是 List 接口欠橘。
  • Map
    Map 中的所有元素都必須是以上列表中支持的數(shù)據(jù)類型、其他 AIDL 生成的接口或您聲明的可打包類型现恼。 不支持通用 Map肃续,如 Map

注意事項(xiàng)

在 aidl 文件中,除了 Java 編程語(yǔ)言中的所有原語(yǔ)類型叉袍、String始锚、CharSequence、List喳逛、Map,其他在 AIDL 文件中用到的類瞧捌,你必須使用 import 語(yǔ)句導(dǎo)入,否則會(huì)報(bào)錯(cuò)润文。
當(dāng)你使用實(shí)現(xiàn)Parceable 的自定義類型的時(shí)候姐呐,當(dāng)其作為參數(shù)的時(shí)候,你必須為其制定是輸入或者是輸出參數(shù)典蝌。

in 表示輸入?yún)?shù)曙砂,即服務(wù)端可以修改該類型
out 表示輸出參數(shù),即客戶端可以修改該類型骏掀,客戶端不行
inout 表示客戶端和服務(wù)端都可以修改該類型

如: void onSuccess(int code,in MusicInfo musicInfo);

Server (服務(wù)端的實(shí)現(xiàn))

服務(wù)端主要有三個(gè)步驟

  • 將請(qǐng)求抽象成接口鸠澈,并編寫 aidl 文件;
  • 編寫一個(gè) Service截驮,實(shí)現(xiàn)接口款侵,處理客戶端的請(qǐng)求,并將 binder 返回回去侧纯;
  • 在 AndroidManifet 配置 Service新锈,將我們的 Service 暴露出去。
一眶熬、將請(qǐng)求抽象成接口妹笆,并編寫 aidl 文件

首先我們先來(lái)看一下 IPlayService aidl 文件,下面的代碼中娜氏,我們定義了一個(gè) play 方法拳缠,有兩個(gè)參數(shù),name 是代表歌曲的名字贸弥,IPlayListener 是一個(gè)接口窟坐。需要注意的是它不是一個(gè) java 類,是 aidl 文件 。這樣才能在服務(wù)端和客戶端之間傳遞

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);

}

要手動(dòng)添加 import xj.musicserver.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);
 }

要手動(dòng)添加 import xj.musicserver.MusicInfo;
接下來(lái)我們?cè)賮?lái)看一下我們的實(shí)體類 MusicInfo哲鸳,實(shí)現(xiàn)了 Parceable 接口

//下面是自定義的一個(gè)MusicInfo子類臣疑,實(shí)現(xiàn)了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();
    }

    //必須提供一個(gè)名為CREATOR的static final屬性 ,
    //該屬性需要實(shí)現(xiàn)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 方法讀寫的順序是一一對(duì)應(yīng)的徙菠。
這里有一點(diǎn)要提醒大家的是 AndroidStudio 中讯沈,我們通過(guò)插件會(huì)自動(dòng)幫我們生成 writeToParcel 方法及 CREATOR,通常 readFromParcel 方法是不會(huì)自動(dòng)生成的婿奔,需要我們自己手動(dòng)編寫缺狠,不然會(huì)編譯不過(guò)。

注意了萍摊,接下來(lái)我們需要寫一個(gè) MusicInfo.aidl 文件

package xj.musicserver;

// Declare any non-default types here with import statements

parcelable MusicInfo;

指定包名挤茄,并聲明 MusicInfo 是 parcelable,注意 parcelable 是小寫的 p冰木,不是大寫的 P穷劈。這是一個(gè)規(guī)范,google 官方指定需要的片酝。同時(shí) MusicInfo.aidl 和 MusicInfo.java 需要放置在同個(gè)包中。

image.png

二挖腰、第二步,編寫一個(gè) Service雕沿,實(shí)現(xiàn)接口,處理客戶端的請(qǐng)求猴仑,并將 binder 返回回去审轮;

(以下代碼涉及到兩個(gè)接口的傳遞,過(guò)程大致如此辽俗,即自定義動(dòng)作IPlayListener接口從客戶端傳遞到服務(wù)端疾渣,服務(wù)端再將動(dòng)作傳遞到任務(wù)類MusicTask.IResultListener接口中。)

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;
}

這里我們所做的工作就是到數(shù)據(jù)庫(kù)里面查詢看是否有相應(yīng)的歌曲崖飘,如果有榴捡,通過(guò) aidl 回調(diào),告訴客戶端我們查找成功朱浴,調(diào)用 onSuccess 方法吊圾,沒(méi)有找到,調(diào)用客戶端的 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> {

    // 這里只貼出主要代碼项乒,詳細(xì)代碼可到文章的末尾下載。

    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);
    }
}
三梁沧、在 AndroidManifet 配置 Service檀何,將我們的 Service 暴露出去。
<service
    android:name=".PlayService"
    android:exported="true"
    android:process=":remote">
    <intent-filter>
        <action android:name="xj.musicserver.IPlayService"/>
    </intent-filter>
</service>

到這里我們服務(wù)端的配置就完成了

Client(客戶端) 的實(shí)現(xiàn)

實(shí)現(xiàn)客戶端大概需要幾個(gè)步驟:

  • 將服務(wù)端的 aidl 文件 copy 過(guò)來(lái),注意要放在同一個(gè)包下频鉴。
  • 通過(guò)服務(wù)端 Service 的 Action 啟動(dòng)栓辜, 當(dāng)啟動(dòng) Service 成功的時(shí)候,將服務(wù)端返回的 Binder 保存下來(lái)并轉(zhuǎn)化成相應(yīng)的實(shí)例砚殿。
  • 之后如果想與服務(wù)端通訊啃憎,通過(guò)保存下來(lái)的 Binder,即可調(diào)用服務(wù)端的方法似炎。
一辛萍、第一步:將服務(wù)端的 aidl 文件 copy 過(guò)來(lái),注意要放在同一個(gè)包下羡藐。

如下圖所示贩毕,我們將 IPlayListener.aidl,IPalyService.aidl,MusicInfo.aidl 和 MuicInfo.java copy 到客戶端


image.png
二、第二步:通過(guò)服務(wù)端 Service 的 Action 啟動(dòng)仆嗦, 當(dāng)啟動(dòng) Service 成功的時(shí)候辉阶,將服務(wù)端返回的 Binder 保存下來(lái)并轉(zhuǎn)化成相應(yīng)的實(shí)例。

(注意在 Android 5.0以后瘩扼,不能通過(guò)隱式 Intent 啟動(dòng) service谆甜,必須制定包名)
這里的 Action 是與服務(wù)端一一對(duì)應(yīng)的。

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";
三集绰、第三步:通過(guò)第二步保存下來(lái) 的 mIBinder规辱,與服務(wù)端進(jìn)行通訊。

當(dāng)我們調(diào)用 mIPlayService.play 方法的時(shí)候栽燕,服務(wù)端會(huì)去查找本地是否存在 丑八怪 這首歌罕袋,查找到的時(shí)候會(huì)回調(diào) onSuccess 方法,查找不到的時(shí)候會(huì)回調(diào) 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());
    }
};

全文結(jié)束浴讯。
感謝:
Android AIDL 傳遞對(duì)象(Parceable)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蔼啦,隨后出現(xiàn)的幾起案子榆纽,更是在濱河造成了極大的恐慌,老刑警劉巖捏肢,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掠河,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡猛计,警方通過(guò)查閱死者的電腦和手機(jī)唠摹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)奉瘤,“玉大人勾拉,你說(shuō)我怎么就攤上這事煮甥。” “怎么了藕赞?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵成肘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我斧蜕,道長(zhǎng)双霍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任批销,我火速辦了婚禮洒闸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘均芽。我一直安慰自己丘逸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布掀宋。 她就那樣靜靜地躺著深纲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪劲妙。 梳的紋絲不亂的頭發(fā)上湃鹊,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音镣奋,去河邊找鬼币呵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛唆途,可吹牛的內(nèi)容都是我干的富雅。 我是一名探鬼主播掸驱,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼肛搬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了毕贼?” 一聲冷哼從身側(cè)響起温赔,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎鬼癣,沒(méi)想到半個(gè)月后陶贼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡待秃,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年拜秧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片章郁。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡枉氮,死狀恐怖志衍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情聊替,我是刑警寧澤楼肪,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站惹悄,受9級(jí)特大地震影響春叫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泣港,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一暂殖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧爷速,春花似錦央星、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至廉沮,卻和暖如春颓遏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滞时。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工叁幢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人坪稽。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓曼玩,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親窒百。 傳聞我的和親對(duì)象是個(gè)殘疾皇子黍判,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344