Android進(jìn)階:Binder機(jī)制胜蛉、AIDL進(jìn)程通信學(xué)習(xí)(模擬支付寶支付案例)

目錄

  • Binder是什么?
  • 從面向?qū)ο蟮乃枷肟?Binder IPC
  • 進(jìn)程空間的劃分
  • Binder機(jī)制是如何跨進(jìn)程通信的
  • Binder 到底是什么
  • 理解Java層的Binder 代碼
  • Android中的AIDL
  • android開發(fā)AIDL使用模擬支付寶支付

Binder是什么颤难?

Binder是Android 系統(tǒng)中最重要的特性之一:稱之為"粘合劑"温自,粘合了兩個不同的進(jìn)程,是系統(tǒng)間各個組件通信的橋梁知给,是一種跨進(jìn)程通信機(jī)制瓤帚。Binder基于Client-Server通信模式。

從面向?qū)ο蟮乃枷肟?Binder IPC炼鞠?

Binder使用Client-Server通信方式:

  • 對Binder而言缘滥,Binder 可以看成 Server 提供的實現(xiàn)某個特定服務(wù)的訪問接入點轰胁, Client 通過這個‘地址’向Server 發(fā)送請求來使用該服務(wù)谒主;對 Client 而言,Binder 可以看成是通向 Server 的管道入口赃阀,要想和某個Server 通信首先必須建立這個管道并獲得管道入口霎肯。

  • Binder 是一個實體位于 Server 中的對象擎颖,該對象提供了一套方法用以實現(xiàn)對服務(wù)的請求,就像類的成員函數(shù)方法观游。遍布于 Client 中的入口 可以看成指向這個 Binder對象的 引用搂捧, 一旦獲得了這個引用就可以調(diào)用該對象的方法訪問 Server 。在 Client 看來通過 Binder引用調(diào)用 Server 提供的方法 和通過引用調(diào)用本地對象的方法并無區(qū)別懂缕,盡管這個Binder 引用的實體位于 Server 端允跑,從通信角度中 Client 中的Binder 引用也可以看作是 Server Binder 的 "代理" ,在本地代表遠(yuǎn)端 Server 為 Client 提供的服務(wù)搪柑。

  • 面向?qū)ο笏枷氲囊雽⑦M(jìn)程間通信轉(zhuǎn)化為通過對某個Binder對象的引用調(diào)用該對象的方法聋丝,而其獨特之處在于Binder對象是一個可以跨進(jìn)程引用的對象,它的實體位于一個進(jìn)程中工碾,而它的引用卻遍布于系統(tǒng)的各個進(jìn)程之中弱睦。最誘人的是,這個引用和java里引用一樣既可以是強(qiáng)類型渊额,也可以是弱類型况木,而且可以從一個進(jìn)程傳給其它進(jìn)程,讓大家都能訪問同一Server旬迹,就象將一個對象或引用賦值給另一個引用一樣火惊。Binder模糊了進(jìn)程邊界,淡化了進(jìn)程間通信過程奔垦,整個系統(tǒng)仿佛運(yùn)行于同一個面向?qū)ο蟮某绦蛑写;巍P涡紊腂inder對象以及星羅棋布的引用仿佛粘接各個應(yīng)用程序的膠水,這也是Binder在英文里的原意宴倍。

摘錄:Android Bander設(shè)計與實現(xiàn) - 設(shè)計篇

進(jìn)程空間的劃分

Android系統(tǒng)基于Linux內(nèi)核 张症,進(jìn)程都有隔離機(jī)制

  • 一個進(jìn)程空間分為 用戶空間 & 內(nèi)核空間(Kernel),即把進(jìn)程內(nèi) 用戶 & 內(nèi)核 隔離開來
  • 二者區(qū)別:
    1. 進(jìn)程間鸵贬,用戶空間的數(shù)據(jù)不可共享俗他,所以用戶空間 = 不可共享空間
    2. 進(jìn)程間,內(nèi)核空間的數(shù)據(jù)可共享阔逼,所以內(nèi)核空間 = 可共享空間

進(jìn)程隔離是為保護(hù)操作系統(tǒng)中進(jìn)程互不干擾而設(shè)計的一組不同硬件和軟件的技術(shù)兆衅。這個技術(shù)是為了避免進(jìn)程A寫入進(jìn)程B的情況發(fā)生。 進(jìn)程的隔離實現(xiàn)嗜浮,使用了虛擬地址空間羡亩。進(jìn)程A的虛擬地址和進(jìn)程B的虛擬地址不同,這樣就防止進(jìn)程A將數(shù)據(jù)信息寫入進(jìn)程B危融。

不同進(jìn)程之間畏铆,數(shù)據(jù)不共享;而需要進(jìn)程間通信吉殃,則需要某種機(jī)制才能完成辞居。Linux內(nèi)核擁有著非常多的跨進(jìn)程通信機(jī)制楷怒,比如管道,System V瓦灶,Socket等鸠删;

而Android 為什么要用 Binder ?主要從性能和安全上

性能:

  • 在移動設(shè)備上贼陶,廣泛地使用跨進(jìn)程通信肯定對通信機(jī)制本身提出了嚴(yán)格的要求刃泡;Binder相對出傳統(tǒng)的Socket方式,更加高效碉怔;

安全:

  • 另外陡蝇,傳統(tǒng)的進(jìn)程通信方式對于通信雙方的身份并沒有做出嚴(yán)格的驗證渡嚣,只有在上層協(xié)議上進(jìn)行架設(shè);比如Socket通信ip地址是客戶端手動填入的,都可以進(jìn)行偽造灯蝴;而Binder機(jī)制從協(xié)議本身就支持對通信雙方做身份校檢虑啤,傳輸過程只需一次拷貝咏瑟,為發(fā)送發(fā)添加UID/PID身份尚卫,既支持實名Binder也支持匿名Binder,因而大大提升了安全性叁征。這個也是Android權(quán)限模型的基礎(chǔ)

Binder機(jī)制是如何跨進(jìn)程通信的纳账?

通信過程的四個角色模型:

a. Client(客戶端), 使用服務(wù)的進(jìn)程

b. Server(服務(wù)端),提供服務(wù)的進(jìn)程

c. ServiceManager(SM 類似于通信錄或路由器)捺疼,管理 Service注冊與查詢(將字符形式的Binder 名字轉(zhuǎn)化成 Client中對該 Binder的引用 )

d. driver(binder驅(qū)動)疏虫,連接 Server 進(jìn)程、Client進(jìn)程 和 ServiceManage的橋梁啤呼;作用為:1傳遞進(jìn)程間數(shù)據(jù)卧秘,通過內(nèi)存映射;2 實現(xiàn)線程控制官扣,采用BInder 的線程池翅敌,由Binder驅(qū)動自身進(jìn)行管理

Server,Client惕蹄,ServiceManager運(yùn)行于【用戶空間】蚯涮;驅(qū)動運(yùn)行于【內(nèi)核空間】。

通信步驟:

  1. Server 進(jìn)程向 SM 注冊卖陵,并告知自己有什么能力遭顶,我叫zhangsan 有一個 Object 對象,里面有 add() 方法泪蔫。
  2. Client 向SM 查詢棒旗,我要找 zhangsan 需要使用 Object 對象中的 add() 方法;這時驅(qū)動在數(shù)據(jù)流動的時候鸥滨,它并不會給 Client 進(jìn)程返回一個真正的 Object 對象嗦哆,而是返回一個和Object 一樣的代理對象 objectProxy,這個objectProxy 也有 add()方法婿滓,這個代理對象把參數(shù)包裝后交給驅(qū)動老速。
  3. 驅(qū)動收到這個消息,發(fā)現(xiàn)是objectProxy 凸主; 驅(qū)動通知 Server 調(diào)用真正的 Object 的add() 方法橘券,然后把結(jié)果發(fā)給驅(qū)動,驅(qū)動再把結(jié)果返回給 Client 卿吐,于是整個過程就完成了旁舰。

實際上,由于SM與Server通常不在一個進(jìn)程嗡官,Server進(jìn)程向SM注冊的過程也是跨進(jìn)程通信箭窜,驅(qū)動也會對這個過程進(jìn)行暗箱操作:SM中存在的Server端的對象實際上也是代理對象,后面Client向SM查詢的時候衍腥,驅(qū)動會給Client返回另外一個代理對象磺樱。Sever進(jìn)程的本地對象僅有一個,其他進(jìn)程所擁有的全部都是它的代理婆咸。

Binder 跨進(jìn)程并不是真把一個對象傳遞到另一個進(jìn)程竹捉,是通過代理對象經(jīng)過 Binder 驅(qū)動來查找并調(diào)用真正對象的方法和屬性。

事實上 Client 進(jìn)程只不過持有了 Server 端的代理尚骄,代理對象協(xié)助驅(qū)動完成了跨進(jìn)程的通信块差。

而相同進(jìn)程下直接通過驅(qū)動返回真實的對象。

在這里插入圖片描述

Binder 到底是什么倔丈?

  • 通常意義下憨闰,Binder指的是一種通信機(jī)制;我們說AIDL使用Binder進(jìn)行通信需五,指的就是Binder這種IPC機(jī)制起趾。在Android 中實現(xiàn)跨進(jìn)程通信
  • Binder是一種虛擬的物理設(shè)備驅(qū)動,即 Binder 驅(qū)動警儒;作用于連接 Server 進(jìn)程训裆、Client進(jìn)程 和 ServiceManage進(jìn)程
  • 從Framework層角度看,Binder是ServiceManager連接各種Manager和相應(yīng)的ManagerService的橋梁
  • 從Android應(yīng)用層來說蜀铲,Binder是客戶端和服務(wù)端進(jìn)行通信的媒介边琉,當(dāng)bindService的時候,服務(wù)端會返回一個包含了服務(wù)端業(yè)務(wù)調(diào)用的Binder對象记劝,通過這個Binder對象变姨,客戶端就可以獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù),這里的服務(wù)包括普通服務(wù)和基于AIDL的服務(wù)厌丑。
  • 在Android開發(fā)中定欧,Binder主要用在Service中渔呵,包括AIDL和Messenger,其中普通Service中的Binder不涉及進(jìn)程間通信砍鸠,較為簡單扩氢;而Messenger的底層其實是AIDL,正是Binder的核心工作機(jī)制爷辱。

理解Java層的Binder 代碼

IBinder/IInterface/Binder/BinderProxy/Stub

我們使用AIDL接口的時候录豺,經(jīng)常會接觸到這些類,那么這每個類代表的是什么呢饭弓?

  • IBinder是一個接口双饥,它代表了一種跨進(jìn)程傳輸?shù)哪芰?/strong>;只要實現(xiàn)了這個接口弟断,就能將這個對象進(jìn)行跨進(jìn)程傳遞咏花;這是驅(qū)動底層支持的;在跨進(jìn)程數(shù)據(jù)流經(jīng)驅(qū)動的時候阀趴,驅(qū)動會識別IBinder類型的數(shù)據(jù)迟螺,從而自動完成不同進(jìn)程Binder本地對象以及Binder代理對象的轉(zhuǎn)換。
  • IBinder負(fù)責(zé)數(shù)據(jù)傳輸舍咖,那么client與server端的調(diào)用契約(這里不用接口避免混淆)呢矩父?這里的IInterface代表的就是遠(yuǎn)程server對象具有什么能力。具體來說排霉,就是aidl里面的接口窍株。
  • Java層的Binder類,代表的其實就是Binder本地對象攻柠。BinderProxy類是Binder類的一個內(nèi)部類球订,它代表遠(yuǎn)程進(jìn)程的Binder對象的本地代理;這兩個類都繼承自IBinder, 因而都具有跨進(jìn)程傳輸?shù)哪芰迮ィ粚嶋H上冒滩,在跨越進(jìn)程的時候,Binder驅(qū)動會自動完成這兩個對象的轉(zhuǎn)換浪谴。
  • 在使用AIDL的時候开睡,編譯工具會給我們生成一個Stub的靜態(tài)內(nèi)部類;這個類繼承了Binder, 說明它是一個Binder本地對象苟耻,它實現(xiàn)了IInterface接口篇恒,表明它具有遠(yuǎn)程Server承諾給Client的能力;Stub是一個抽象類凶杖,具體的IInterface的相關(guān)實現(xiàn)需要我們手動完成胁艰,這里使用了策略模式。

Android中的AIDL

1. AIDL的簡介

AIDL (Android Interface Definition Language) 是一種接口定義語言,用于生成可以在Android設(shè)備上兩個進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼腾么。如果在一個進(jìn)程中(例如Activity)要調(diào)用另一個進(jìn)程中(例如Service)對象的操作奈梳,就可以使用AIDL生成可序列化的參數(shù),來完成進(jìn)程間通信解虱。

簡言之攘须,AIDL能夠?qū)崿F(xiàn)進(jìn)程間通信,其內(nèi)部是通過Binder機(jī)制來實現(xiàn)的

2. AIDL的具體使用

AIDL的實現(xiàn)一共分為三部分饭寺,一部分是客戶端阻课,調(diào)用遠(yuǎn)程服務(wù)叫挟。一部分是服務(wù)端艰匙,提供服務(wù)。最后一部分抹恳,也是最關(guān)鍵的是AIDL接口员凝,用來傳遞的參數(shù),提供進(jìn)程間通信奋献。

大致流程:首先建一個Service和一個AIDL接口健霹,接著創(chuàng)建一個類繼承自AIDL接口中的Stub類并實現(xiàn)Stub類中的抽象方法,在Service的onBind方法中返回這個類的對象瓶蚂,然后客戶端就可以綁定服務(wù)端Service糖埋,建立連接后就可以訪問遠(yuǎn)程服務(wù)端的方法了。

  1. AIDL支持的數(shù)據(jù)類型:基本數(shù)據(jù)類型窃这、StringCharSequence瞳别、ArrayListHashMap杭攻、Parcelable以及AIDL祟敛;

  2. 某些類即使和AIDL文件在同一個包中也要顯式import進(jìn)來;

  3. AIDL中除了基本數(shù)據(jù)類兆解,其他類型的參數(shù)都要標(biāo)上方向:in馆铁、out或者inout

    稱之為定向Tag:

    InOut 輸入輸出類型

    In 類型 輸入類型

    Out類型 輸出類型

  4. AIDL接口中支持方法锅睛,不支持聲明靜態(tài)變量埠巨;

  5. 為了方便AIDL的開發(fā),建議把所有和AIDL相關(guān)的類和文件全部放入同一個包中现拒,這樣做的好處是乖订,當(dāng)客戶端是另一個應(yīng)用的時候,可以直接把整個包復(fù)制到客戶端工程中具练。

  6. RemoteCallbackList是系統(tǒng)專門提供的用于刪除跨進(jìn)程Listener的接口乍构。RemoteCallbackList是一個泛型,支持管理任意的AIDL接口,因為所有的AIDL接口都繼承自IInterface`接口哥遮。


android開發(fā)AIDL使用模擬支付寶支付

alipayMp4 (3).gif
模擬實現(xiàn)功能

模擬支付寶支付岂丘,客戶端進(jìn)行充值調(diào)用 支付寶的服務(wù),支付寶處理充值請求眠饮,返回充值成功或失敗的結(jié)果給客戶端

創(chuàng)建兩個應(yīng)用項目奥帘,AlipayServiceDemo 和 AlipayClientDemo 端

Service流程:

1、定義一個 Service 服務(wù) 仪召,并在 manifest中注冊服務(wù)寨蹋,定義意圖 并 設(shè)置 android:exported="true" 可被外部應(yīng)用使用該服務(wù);

2扔茅、 服務(wù)端 編寫 AIDL 文件 已旧,定義發(fā)起支付接口,以及回調(diào)召娜,ReBuild Project 生成對應(yīng)的 .java 文件运褪;

3、發(fā)起支付請求玖瘸,通過在Service 的onBind 中根據(jù) action 的不同秸讹, 返回一個指定了支付寶 action 的 IBinder 對象 ,繼承自 AIDL 構(gòu)建的 xxx.Stub 對象 雅倒,(是根據(jù)AIDL 自動生成的.java文件 )璃诀。繼承Stub 并實現(xiàn)接口定義的方法;

requestPay(); //實現(xiàn)蔑匣,打開一個支付界面

paySuccess()劣欢;//回調(diào)告訴第三方應(yīng)用支付成功

payFailed();//回調(diào)第三方應(yīng)用支付失敗

4殖演、 在打開的頁面回綁該服務(wù) 并在服務(wù)中實現(xiàn)支付寶的支付服務(wù)邏輯動作

Client流程:

1氧秘、創(chuàng)建 AIDL文件夾 ,將 Service 端的 AIDL 文件包 復(fù)制過來 Client 中進(jìn)行替換 (需要一模一樣)趴久,進(jìn)行 Rebuild Project 生成.java 文件

2丸相、綁定支付寶的服務(wù),之后在onServiceConnected() 連接中 并使用 :.Stub.asInterface(service); 轉(zhuǎn)換成接口的實現(xiàn)

3彼棍、使用該接口方法灭忠,判空并 catch 異常


AlipayServiceDemo 服務(wù)端實現(xiàn)

AndroidMenifest.xml
  <application
      ....>
        <service
            android:name=".PayService"
            android:exported="true">
                <!--定義可被其它應(yīng)用使用該服務(wù),定義意圖-->
            <intent-filter>
                <action android:name="com.alibaba.alipay.THIRD_PART_PAY" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>

        </service>
    </application>
2個AIDL文件的創(chuàng)建:
package com.alibaba.alipay;
//使用要導(dǎo)入完整包名W丁弛作!
import com.alibaba.alipay.ThirdPartPayResult;

interface ThirdPartPayAction {
    /*
    發(fā)起支付請求 接口
    */
    void requestPay( String orderInfo, float payMoney,ThirdPartPayResult callBack);
}
----------------------------------------------------------------------------------------
package com.alibaba.alipay;
interface ThirdPartPayResult {
    /*
    支付成功的回調(diào)
    */
    void onPaySuccess();
         /*
    支付失敗的回調(diào)
    */
    void onPayFaild(in int errorCode , in String msg);

}


PayService.java
package com.alibaba.alipay;

/**
 * @author:thisfeng
 * @time 2019/4/18 22:41
 * 支付服務(wù)流程:
 * 1、首先接收到第三方應(yīng)用的綁定华匾,請求支付映琳。
 * <p>
 * 2、然后拉起我們的支付頁面Activity ,拉起之后回綁服務(wù)萨西,服務(wù)內(nèi)定義并返回支付的動作類 給 PayActivity有鹿,
 * <p>
 * 3、通過用戶輸入的密碼判斷是否正確谎脯, 從服務(wù)連接中拿到 IBinder 也是就是這個支付的動作類葱跋, 進(jìn)行服務(wù)的動作回調(diào)
 */
public class PayService extends Service {


    private static final String TAG = "PayService";

    private ThirdPartPayImpl thirdPartPay;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        String action = intent.getAction();
        Log.d(TAG, "action --->" + action);

        if (!TextUtils.isEmpty(action)) {
            if ("com.alibaba.alipay.THIRD_PART_PAY".equals(action)) {
                //通過 action 說明這是第三方要求我們支付寶進(jìn)行支付
                thirdPartPay = new ThirdPartPayImpl();
                //提取指全局供外部交互時調(diào)用
                return thirdPartPay;
            }
        }

        //PayActivity 回綁服務(wù) 返回的對象 進(jìn)行交互,無指定action 默認(rèn)返回此對象
        return new PayAction();
    }

    /**
     * 定義支付寶的支付 服務(wù)邏輯動作
     */
    public class PayAction extends Binder {

        /**
         * 實際的支付是比較復(fù)雜的源梭,比如說加密娱俺,向服務(wù)器發(fā)起請求,等待服務(wù)器的結(jié)果废麻,多次握手等
         * <p>
         * 支付方法
         */
        public void pay(float payMoney) {
            Log.d(TAG, "pay money is --->" + payMoney);

            if (thirdPartPay != null) {
                //回調(diào)告訴遠(yuǎn)程第三方 支付成功
                thirdPartPay.onPaySuccess();
            }
        }

        /**
         * 用戶點擊界面上的取消/退出
         */
        public void onUserCancel() {
            if (thirdPartPay != null) {
                //回調(diào)告訴遠(yuǎn)程 支付失敗
                thirdPartPay.onPayFaild(-1, "user cancel pay...");
            }
        }
    }

    /**
     * 第三方調(diào)用起 跨進(jìn)程 進(jìn)行支付
     */
    private class ThirdPartPayImpl extends ThirdPartPayAction.Stub {


        private ThirdPartPayResult callBack;

        @Override
        public void requestPay(String orderInfo, float payMoney, ThirdPartPayResult callBack) throws RemoteException {
            this.callBack = callBack;

            Log.d(TAG, "requestPay --->orderInfo:" + orderInfo + " payMoney:" + payMoney);
          
            //第三方應(yīng)用發(fā)起請求荠卷,拉起 打開一個支付頁面
            Intent intent = new Intent();
            intent.setClass(PayService.this, PayActivity.class);
            intent.putExtra(Const.KEY_BILL_INFO, orderInfo);
            intent.putExtra(Const.KEY_PAY_MONEY, payMoney);
            //新的 task 中打開
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(intent);
        }

        /**
         * 定義相同的方法,進(jìn)行回調(diào) 脑溢,給外部調(diào)用
         */
        public void onPaySuccess() {
            try {
                callBack.onPaySuccess();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onPayFaild(int errorCode, String errorMsg) {
            try {
                callBack.onPayFaild(errorCode, errorMsg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}
PayActivity.java
package com.alibaba.alipay;

/**
 * @author:thisfeng
 * @time 2019/4/18 22:52
 * 支付頁面
 */

public class PayActivity extends AppCompatActivity {

    private final String TAG = "PayActivity";

    private boolean isBind;

    private PayService.PayAction payAction;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pay);
      
        doBindService();
        initView();
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
        if (payAction != null) {
            payAction.onUserCancel();
        }
    }

    private void initView() {
        Intent intent = getIntent();
        String billInfo = intent.getStringExtra(Const.KEY_BILL_INFO);
        final float payMoney = intent.getFloatExtra(Const.KEY_PAY_MONEY, 0);

        TextView tvPayInfo = findViewById(R.id.tvPayInfo);
        TextView tvPayMoney = findViewById(R.id.tvPayMoney);
        final EditText edtPayPwd = findViewById(R.id.edtPayPwd);

        tvPayInfo.setText("支付賬單:" + billInfo);
        tvPayMoney.setText("支付金額:¥" + payMoney);

        findViewById(R.id.btnCommit).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                String password = edtPayPwd.getText().toString().trim();

                if ("123456".equals(password) && payAction != null) {
                    //模擬如果密碼輸入成功就去調(diào)用支付僵朗,實際上應(yīng)該請求后端進(jìn)行加密驗證
                    payAction.pay(payMoney);
                    Toast.makeText(PayActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
                    finish();
                } else {
                    Toast.makeText(PayActivity.this, "支付密碼錯誤赖欣!", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

    /**
     * 支付頁面回綁服務(wù)屑彻,等待獲取服務(wù)中的支付結(jié)果
     * 因為我們的Activity 也要跟 服務(wù) 進(jìn)行通訊,告訴服務(wù)通訊結(jié)果顶吮,所以也要綁定服務(wù)
     * <p>
     * 綁定服務(wù)
     */
    private void doBindService() {

        Intent intent = new Intent(this, PayService.class);
//        intent.setAction("com.alibaba.alipay.THIRD_PART_PAY");//回綁頁面不需要指定這個了社牲,這里不指定設(shè)置的話就會默認(rèn)返回 了 PayAction 這個對象
//        intent.addCategory(Intent.CATEGORY_DEFAULT);
//        intent.setPackage(this.getPackageName());
        isBind = bindService(intent, connection, BIND_AUTO_CREATE);

        Log.d(TAG, "Bind Pay Service..");
    }

    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            //從服務(wù)連接中拿到 支付動作
            payAction = (PayService.PayAction) service;
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };


    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (isBind && connection != null) {
            unbindService(connection);
            Log.d(TAG, "unBind Pay Service..");
            connection = null;
            isBind = false;
        }

    }
}


AlipayClientDemo 客戶端實現(xiàn)

MainActivity.java

拷貝服務(wù)端的 AIDL 整個文件到本地后定義一個充值頁面。

package com.thisfeng.alipayclientdemo;


public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Client MainActivity";
  
    private AlipayConnection alipayConnection;
    private boolean isBind;
    private ThirdPartPayAction thirdPartPayAction;
    private TextView tvTitle;
    private TextView tvMoney;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
      
        bindAlipayService();
        initView();
    }

    /**
     * 綁定支付寶的服務(wù)悴了,在現(xiàn)在開發(fā)中搏恤,其實這部分動作是由支付寶的 SDK 完成
     */
    private void bindAlipayService() {

        Intent intent = new Intent();
        intent.setAction("com.alibaba.alipay.THIRD_PART_PAY");//復(fù)制服務(wù)端的action 和包名,建議提取至全局
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setPackage("com.alibaba.alipay");

        alipayConnection = new AlipayConnection();

        isBind = bindService(intent, alipayConnection, BIND_AUTO_CREATE);

        Log.d(TAG, "Client bind service ....");
    }

    private void initView() {

        tvTitle = findViewById(R.id.tvTitle);
        tvMoney = findViewById(R.id.tvMoney);

        findViewById(R.id.btnRecharge).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (thirdPartPayAction != null) {
                    //進(jìn)行充值
                    try {
                        thirdPartPayAction.requestPay("充值100QB", 100, new PayCallBack());
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

                }

            }
        });

    }

    private class PayCallBack extends ThirdPartPayResult.Stub {

        @Override
        public void onPaySuccess() throws RemoteException {
            //支付成功,修改 UI內(nèi)容
            //實際上是取修改數(shù)據(jù)庫湃交,其實支付寶是通過回調(diào)URL地址熟空,直接通知我們的后臺服務(wù)器,后臺返回我們客戶端進(jìn)行通知
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    tvMoney.setText("100");

                    Log.d(TAG, "Client onPaySuccess() -----充值成功 !");

                    Toast.makeText(MainActivity.this, "充值成功搞莺!", Toast.LENGTH_SHORT).show();
                }
            });
        }

        @Override
        public void onPayFaild(int errorCode, String msg) throws RemoteException {

            Log.d(TAG, "Client onPayFaild() ----- errorCode:" + errorCode + "---msg:" + msg);

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, "充值失斚⒙蕖!", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

    private class AlipayConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            //獲取支付寶的服務(wù)
            thirdPartPayAction = ThirdPartPayAction.Stub.asInterface(service);
            Log.d(TAG, "Client onServiceConnected----->" + name);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "Client onServiceDisconnected----->" + name);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBind && alipayConnection != null) {
            unbindService(alipayConnection);
            Log.d(TAG, "Client unbind service ....");
            alipayConnection = null;
            isBind = false;
        }
    }
}

以上為Service 和 Client 的 全部代碼才沧。便于理解請務(wù)必手動敲一遍實例加深印象迈喉。

完整案例代碼

學(xué)習(xí)參考:

Android Bander設(shè)計與實現(xiàn) - 設(shè)計篇

Binder學(xué)習(xí)指南

Android跨進(jìn)程通信:圖文詳解 Binder機(jī)制 原理

Android藝術(shù)探索

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市温圆,隨后出現(xiàn)的幾起案子挨摸,更是在濱河造成了極大的恐慌,老刑警劉巖岁歉,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件得运,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)熔掺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門彬檀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瞬女,你說我怎么就攤上這事窍帝。” “怎么了诽偷?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵坤学,是天一觀的道長。 經(jīng)常有香客問我报慕,道長深浮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任眠冈,我火速辦了婚禮飞苇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蜗顽。我一直安慰自己布卡,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布雇盖。 她就那樣靜靜地躺著忿等,像睡著了一般。 火紅的嫁衣襯著肌膚如雪崔挖。 梳的紋絲不亂的頭發(fā)上贸街,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天,我揣著相機(jī)與錄音狸相,去河邊找鬼薛匪。 笑死,一個胖子當(dāng)著我的面吹牛脓鹃,可吹牛的內(nèi)容都是我干的逸尖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼将谊,長吁一口氣:“原來是場噩夢啊……” “哼冷溶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起尊浓,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤逞频,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后栋齿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體苗胀,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡襟诸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了基协。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歌亲。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖澜驮,靈堂內(nèi)的尸體忽然破棺而出陷揪,到底是詐尸還是另有隱情,我是刑警寧澤杂穷,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布悍缠,位于F島的核電站,受9級特大地震影響耐量,放射性物質(zhì)發(fā)生泄漏飞蚓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一廊蜒、第九天 我趴在偏房一處隱蔽的房頂上張望趴拧。 院中可真熱鬧,春花似錦山叮、人聲如沸著榴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽兄渺。三九已至缝龄,卻和暖如春汰现,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背叔壤。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工瞎饲, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人炼绘。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓嗅战,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俺亮。 傳聞我的和親對象是個殘疾皇子驮捍,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容