目錄
-
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在英文里的原意宴倍。
進(jìn)程空間的劃分
Android系統(tǒng)基于Linux內(nèi)核 张症,進(jìn)程都有隔離機(jī)制
- 一個進(jìn)程空間分為 用戶空間 & 內(nèi)核空間(
Kernel
),即把進(jìn)程內(nèi) 用戶 & 內(nèi)核 隔離開來- 二者區(qū)別:
- 進(jìn)程間鸵贬,用戶空間的數(shù)據(jù)不可共享俗他,所以用戶空間 = 不可共享空間
- 進(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)核空間】。
通信步驟:
- Server 進(jìn)程向 SM 注冊卖陵,并告知自己有什么能力遭顶,我叫zhangsan 有一個 Object 對象,里面有 add() 方法泪蔫。
- Client 向SM 查詢棒旗,我要找 zhangsan 需要使用 Object 對象中的 add() 方法;這時驅(qū)動在數(shù)據(jù)流動的時候鸥滨,它并不會給 Client 進(jìn)程返回一個真正的 Object 對象嗦哆,而是返回一個和Object 一樣的代理對象 objectProxy,這個objectProxy 也有 add()方法婿滓,這個代理對象把參數(shù)包裝后交給驅(qū)動老速。
- 驅(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ù)端的方法了。
AIDL支持的數(shù)據(jù)類型:基本數(shù)據(jù)類型窃这、
String
和CharSequence
瞳别、ArrayList
、HashMap
杭攻、Parcelable
以及AIDL
祟敛;某些類即使和AIDL文件在同一個包中也要顯式import進(jìn)來;
AIDL中除了基本數(shù)據(jù)類兆解,其他類型的參數(shù)都要標(biāo)上方向:
in
馆铁、out
或者inout
;稱之為定向Tag:
InOut 輸入輸出類型
In 類型 輸入類型
Out類型 輸出類型
AIDL接口中支持方法锅睛,不支持聲明靜態(tài)變量埠巨;
為了方便AIDL的開發(fā),建議把所有和AIDL相關(guān)的類和文件全部放入同一個包中现拒,這樣做的好處是乖订,當(dāng)客戶端是另一個應(yīng)用的時候,可以直接把整個包復(fù)制到客戶端工程中具练。
RemoteCallbackList
是系統(tǒng)專門提供的用于刪除跨進(jìn)程Listener的接口乍构。RemoteCallbackList是一個泛型,支持管理任意的AIDL接口,因為所有的AIDL接口都繼承自
IInterface`接口哥遮。
android開發(fā)AIDL使用模擬支付寶支付
模擬實現(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è)計篇