手寫AIDL 了解binder在java層的實現(xiàn)

AIDL是什么

Android 接口定義語言 (AIDL) 與您可能使用過的其他接口語言 (IDL) 類似贤牛。您可以利用它定義客戶端與服務均認可的編程接口六水,以便二者使用進程間通信 (IPC) 進行相互通信。在 Android 中盆耽,一個進程通常無法訪問另一個進程的內存轰绵。因此,為進行通信昌执,進程需將其對象分解成可供操作系統(tǒng)理解的原語烛亦,并將其編組為可供您操作的對象。編寫執(zhí)行該編組操作的代碼較為繁瑣懂拾,因此 Android 會使用 AIDL 為您處理此問題煤禽。

AIDL的用處

Android 接口定義語言 (AIDL) 會將對象分解成原語,操作系統(tǒng)可通過識別這些原語并將其編組到各進程中來執(zhí)行 IPC委粉。對于之前采用 Messenger 的方法而言呜师,其實際上是以 AIDL 作為其底層結構。如上所述贾节,Messenger 會在單個線程中創(chuàng)建包含所有客戶端請求的隊列汁汗,以便服務一次接收一個請求。不過栗涂,如果您想讓服務同時處理多個請求知牌,則可直接使用 AIDL。在此情況下斤程,您的服務必須達到線程安全的要求角寸,并且能夠進行多線程處理菩混。

手寫AIDL

1.定義binder服務端所提供的功能接口。定義服務端的功能為提供人員的增加與刪除扁藕,并提供所有人員列表()沮峡。

/**
 * 這個類用來定義服務端具有什么樣的能力,繼承自IInterface才就有跨進程傳輸?shù)幕A能力
/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.  Iinterface的說明
 */
 */
public interface PersonManager extends IInterface {
    /**
     * 添加人數(shù)
     *
     * @throws RemoteException
     */
    void addPerson(PersonBean personBean) throws RemoteException;

    /**
     * 刪除人數(shù)
     *
     * @throws RemoteException
     */
    void deletePerson(PersonBean personBean) throws RemoteException;

    /**
     * 獲取人數(shù)
     *
     * @throws RemoteException
     */
    List<PersonBean> getPersons() throws RemoteException;
}

PersonBean 是人員信息的model亿柑,我只寫了一個name屬性邢疙,跨進程傳輸則必須實現(xiàn)Parcelable相關接口。為了后面的刪除功能還需要重寫equle和hashcode方法望薄。

/**
 * 實現(xiàn)了parcelable接口的實體類疟游,可用于跨進程傳輸
 */
class PersonBean() : Parcelable {
    var name: String? = null;

    constructor(parcel: Parcel) : this() {
        name = parcel.readString()
    }

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
    }

    override fun describeContents(): Int {
        return 0
    }

    override fun toString(): String {
        return "PersonBean(name=$name)"
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as PersonBean

        if (name != other.name) return false

        return true
    }

    override fun hashCode(): Int {
        return name?.hashCode() ?: 0
    }

    companion object CREATOR : Parcelable.Creator<PersonBean> {
        override fun createFromParcel(parcel: Parcel): PersonBean {
            return PersonBean(parcel)
        }

        override fun newArray(size: Int): Array<PersonBean?> {
            return arrayOfNulls(size)
        }
    }

}

2.添加proxy代理類。我的理解是模擬服務端的所有功能痕支,讓客戶端感受不到調用的是遠程還是本地的服務功能颁虐。可以看見PersonManagerProxy實現(xiàn)了PersonManager
接口卧须,所以就實現(xiàn)了相應的方法另绩,而在構造方法中傳入了IBinder,實際上的跨進程傳輸?shù)膶嶓w故慈。addPerson板熊,deletePerson,getPersons察绷,三個方法中可以看見實際調用的方法是IBinder的transact方法干签。PersonManagerProxy只是進行了一些參數(shù)方法的封裝。

/**
 * personmanager/binder在本地的遠程代理類拆撼。
 */
public class PersonManagerProxy implements PersonManager {
    /**
     * 遠程binder對象
     */
    IBinder remote;
    private static final String DESCRIPTOR = "com.study.stydyfirst.server.PersonManager";

    /**
     * 構造方法傳入ibinder
     *
     * @param remote
     */
    public PersonManagerProxy(IBinder remote) {
        this.remote = remote;
    }

    public String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    @Override
    public void addPerson(PersonBean personBean) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (personBean != null) {
                data.writeInt(1);
                personBean.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            remote.transact(PersonManagerStub.TRANSAVTION_addperson, data, replay, 0);
            replay.readException();
        } finally {
            replay.recycle();
            data.recycle();
        }
    }

    @Override
    public void deletePerson(PersonBean personBean) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (personBean != null) {
                data.writeInt(1);
                personBean.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            remote.transact(PersonManagerStub.TRANSAVTION_deleteperson, data, replay, 0);
            replay.readException();
        } finally {
            replay.recycle();
            data.recycle();
        }
    }

    @Override
    public List<PersonBean> getPersons() throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel replay = Parcel.obtain();
        List<PersonBean> result;
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            remote.transact(PersonManagerStub.TRANSAVTION_getpersons, data, replay, 0);
            replay.readException();
            result = replay.createTypedArrayList(PersonBean.CREATOR);
        } finally {
            replay.recycle();
            data.recycle();
        }
        return result;
    }

    @Override
    public IBinder asBinder() {
        return remote;
    }
}

3.添加PersonManagerStub類容劳。跨進程傳輸?shù)腂inder對象闸度。從代碼中可以看見PersonManagerStub 繼承自Binder竭贩,實現(xiàn)了PersonManager 接口的方法。說明:

  • 說明他就是一個binder類莺禁,具有跨進程傳輸?shù)哪芰?/li>
  • 他的實現(xiàn)類必須實現(xiàn)我們定義的遠程服務端具備的添加留量,刪除,列表獲取等能力
    繼續(xù)分析下PersonManagerStub 的其他代碼哟冬,
  • 構造方法中 this.attachInterface(this, DESCRIPTOR);的作用就是向BinderService注冊Binder服務楼熄。只有注冊了binder,客戶端才能查詢到有這個binder對象浩峡,并使用它可岂。
  • asInterface方法,將一個binder對象轉換為PersonManager 這個我們定義的接口翰灾。通過binder.queryLocalInterface(DESCRIPTOR);方法缕粹,判斷是否返回代理對象稚茅。
  • onTransact方法。就是在剛才PersonManagerProxy類中調用的方法就到這了平斩。將客戶端傳輸?shù)臄?shù)據(jù)在這里進行實際處理并通過跨進程傳輸?shù)竭h程service那里
/**
 * 繼承自binder實現(xiàn)了personmanager的方法亚享,說明它可以跨進程傳輸,并可進行服務端相關的數(shù)據(jù)操作
 */
public abstract class PersonManagerStub extends Binder implements PersonManager {
    private static final String DESCRIPTOR = "com.study.stydyfirst.server.PersonManager";

    public PersonManagerStub() {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static PersonManager asInterface(IBinder binder) {
        if (binder == null)
            return null;
        IInterface iin = binder.queryLocalInterface(DESCRIPTOR);//通過DESCRIPTOR查詢本地binder绘面,如果存在則說明調用方和service在同一進程間虹蒋,直接本地調用
        if (iin != null && iin instanceof PersonManager)
            return (PersonManager) iin;
        return new PersonManagerProxy(binder);//本地沒有,返回一個遠程代理對象
    }
    @Override
    public IBinder asBinder() {
        return this;
    }

    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;

            case TRANSAVTION_getpersons:
                data.enforceInterface(DESCRIPTOR);
                List<PersonBean> result = this.getPersons();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;

            case TRANSAVTION_addperson:
                data.enforceInterface(DESCRIPTOR);
                PersonBean arg0 = null;
                if (data.readInt() != 0) {
                    arg0 = PersonBean.CREATOR.createFromParcel(data);
                }
                this.addPerson(arg0);
                reply.writeNoException();
                return true;
            case TRANSAVTION_deleteperson:
                data.enforceInterface(DESCRIPTOR);
                PersonBean personBean = null;
                if (data.readInt() != 0) {
                    personBean = PersonBean.CREATOR.createFromParcel(data);
                }
                this.deletePerson(personBean);
                reply.writeNoException();
                return true;

        }
        return super.onTransact(code, data, reply, flags);
    }

    public static final int TRANSAVTION_getpersons = IBinder.FIRST_CALL_TRANSACTION;
    public static final int TRANSAVTION_addperson = IBinder.FIRST_CALL_TRANSACTION + 1;
    public static final int TRANSAVTION_deleteperson = IBinder.FIRST_CALL_TRANSACTION + 2;
}

AIDL的相關類已經編寫完畢飒货,下面只需要遠程service實現(xiàn)相應方法,客戶端調用峭竣,就能測試該手寫AIDL的跨進程傳輸能力了
遠程進程service類塘辅,這里使用前臺服務通知來顯示人員信息,便于直觀查看跨進程傳輸結果皆撩,可以看見我們在service中實例化了一個PersonManagerStub對象扣墩,并實現(xiàn)了相關方法。并通過notification顯示personss的數(shù)據(jù)信息扛吞,并通過service的onBind方法將binder對象傳輸給客戶端

/**
 * 遠程service
 */
class PersonManagerService : Service() {
    /**
     * 創(chuàng)建一個數(shù)組用于管理人員  var 聲明可變變量呻惕,val聲明不可變變量 相當于final
     */
    private var personss: ArrayList<PersonBean> = ArrayList();
    val CHANNEL_ID = "personmanager";
    val notificationId = 1;
    override fun onCreate() {
        super.onCreate()
        createNotificationChannel()
        //初始化通知欄,
        showNotification()

    }

    /**
     * 將當前人數(shù)顯示通過通知欄顯示出來
     */
    fun showNotification() {
        var builder = NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("0fdg")
            .setStyle(
                NotificationCompat.BigTextStyle()
                    .bigText("當前的人數(shù)是:" + personss.size + "人員信息:" + personss.toString())
            )
            .setSmallIcon(R.mipmap.ic_launcher)//不設置smallicon滥比,文字信息不生效亚脆,我服了
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        with(NotificationManagerCompat.from(this)) {
            // notificationId is a unique int for each notification that you must define
            //notify(notificationId, builder.build())
            startForeground(notificationId, builder.build())
        }
    }

    /**
     * 首先創(chuàng)建通知渠道,再進行通知顯示
     */
    private fun createNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not in the support library
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = getString(R.string.channel_name)
            val descriptionText = getString(R.string.channel_description)
            val importance = NotificationManager.IMPORTANCE_DEFAULT
            val channel = NotificationChannel(CHANNEL_ID, name, importance).apply {
                description = descriptionText
            }
            // Register the channel with the system
            val notificationManager: NotificationManager =
                getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    override fun onBind(intent: Intent): IBinder {
//        TODO("Return the communication channel to the service.")

        return binder
    }

    private val binder = object : PersonManagerStub() {
        override fun addPerson(personBean: PersonBean?) {
            if (personBean != null) {
                personss.add(personBean)
                Log.d("Server", "添加" + personBean.name)
                showNotification()
            };
        }

        override fun deletePerson(personBean: PersonBean?) {
            personss.remove(personBean)
            Log.d("Server", "刪除");

            showNotification()
        }

        override fun getPersons(): List<PersonBean> {
            Log.d("Server", "刪除")

            return persons;
        }
    }
}

本地clientActivity,簡單service啟動與遠程建立鏈接盲泛,并傳輸數(shù)據(jù)濒持,本地通過PersonManagerStub.asInterface(service)方法直接將binder對象轉換為PersonManager即可直接調用遠程方法進行數(shù)據(jù)傳輸

/**
 * 本地client
 */
class ClientActivity : AppCompatActivity() {

    /** The primary interface we will be calling on the service.  */
    private var mService: PersonManager? = null

    /**
     * 是否綁定的標志
     */
    private var isBound = false

    /**
     * Class for interacting with the main interface of the service.
     */
    private val mConnection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            Log.d("client", "鏈接成功====");
            mService = PersonManagerStub.asInterface(service)//將service轉成遠程服務代理對象,并調用他的方法
            isBound = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null
            isBound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_client)
        // Bind to LocalService
        addListener()
    }

    override fun onDestroy() {
        super.onDestroy()
        if (isBound) {
            unbindService(mConnection)
        }
    }

    private fun addListener() {
        btn_add.setOnClickListener {
            var person = PersonBean()
            person.name = "劉德華"
            mService?.addPerson(person)
        }
        btn_delete.setOnClickListener {
            var person = PersonBean()
            person.name = "劉德華"
            mService?.deletePerson(person)
        }
        btn_bind.setOnClickListener {
            Toast.makeText(baseContext, "綁定服務", Toast.LENGTH_SHORT).show();
            Intent(this, PersonManagerService::class.java).also { intent ->
                bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
                //startService(intent)
            }
        }
    }
}

以上就是java層面的client-binder-servi寺滚,跨進程的一個傳輸流程柑营。并通過手寫方式實現(xiàn)了AIDL的所有功能。 項目demo地址https://github.com/92123748/AIDLTest

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末村视,一起剝皮案震驚了整個濱河市官套,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蚁孔,老刑警劉巖奶赔,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勒虾,居然都是意外死亡纺阔,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門修然,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笛钝,“玉大人质况,你說我怎么就攤上這事〔C遥” “怎么了结榄?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長囤捻。 經常有香客問我臼朗,道長,這世上最難降的妖魔是什么蝎土? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任视哑,我火速辦了婚禮,結果婚禮上誊涯,老公的妹妹穿的比我還像新娘挡毅。我一直安慰自己,他們只是感情好暴构,可當我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布跪呈。 她就那樣靜靜地躺著,像睡著了一般取逾。 火紅的嫁衣襯著肌膚如雪耗绿。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天砾隅,我揣著相機與錄音误阻,去河邊找鬼。 笑死晴埂,一個胖子當著我的面吹牛堕绩,可吹牛的內容都是我干的。 我是一名探鬼主播邑时,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奴紧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了晶丘?” 一聲冷哼從身側響起黍氮,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浅浮,沒想到半個月后沫浆,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡滚秩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年专执,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郁油。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡本股,死狀恐怖攀痊,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情拄显,我是刑警寧澤苟径,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站躬审,受9級特大地震影響棘街,放射性物質發(fā)生泄漏。R本人自食惡果不足惜承边,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一遭殉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧博助,春花似錦恩沽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽里伯。三九已至城瞎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疾瓮,已是汗流浹背脖镀。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留狼电,地道東北人蜒灰。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像肩碟,于是被迫代替她去往敵國和親强窖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,647評論 2 354