版權(quán)聲明:本文為博主原創(chuàng)文章西雀,未經(jīng)博主允許不得轉(zhuǎn)載
PS:轉(zhuǎn)載請(qǐng)注明出處
作者: TigerChain
地址: http://www.reibang.com/p/1b3b6b003032
本文出自 TigerChain 簡(jiǎn)書 人人都會(huì)設(shè)計(jì)模式
教程簡(jiǎn)介
- 1蟹但、閱讀對(duì)象
本篇教程適合新手閱讀,老手直接略過 - 2栈戳、教程難度
初級(jí),本人水平有限难裆,文章內(nèi)容難免會(huì)出現(xiàn)問題子檀,如果有問題歡迎指出镊掖,謝謝
正文
一、什么是代理模式
1褂痰、生活中的代理
1亩进、微商代理
代理在生活中就太多了,比如微商缩歪,在朋友圈中很多時(shí)候都可以看到微商說城招全國(guó)代理「不需要貨源归薛,不需要啟動(dòng)資金,只需要一個(gè)電話就能做生意主籍,好吧我口才不好,沒有人家吹的好」骗污,這類代理就是替賣家出售商品
2崇猫、追女孩
遙想當(dāng)年情竇初開「初中的時(shí)候」沈条,喜歡上了一個(gè)女子需忿,可是迫于害羞,就給女孩子寫了幾封情書蜡歹,買了一束花「但是自己沒有那個(gè)賊膽送」屋厘,就讓我們班里一個(gè)和女孩認(rèn)識(shí)的朋友交給她,現(xiàn)在想來原來幫我送情書的女生就是我的代理呀「幫我完成我想要完成的事」~~嘻嘻月而。話說誰還干類似的事汗洒,就在文章末尾點(diǎn)個(gè)贊
3、代銷店等
其實(shí)就是現(xiàn)在的商店父款,以前小的時(shí)候聽家鄉(xiāng)人叫代銷店溢谤,也是一種代理模式。細(xì)細(xì)一想憨攒,跑業(yè)務(wù)的也是代理世杀,律師也是代理,明星的助理就是代理肝集,京東送貨機(jī)器人是代理瞻坝,共享"女友",那個(gè)"女友"也是代理「你懂得」,等等等等杏瞻。不敢再說了所刀,再說萬物都成代理了「不好意思,又忘了吃藥了」
2捞挥、程序中的代理
其實(shí)程序中使用的代理是非常多的浮创,我們?cè)诰帉?MVC 業(yè)務(wù)的時(shí)候就可以使用代理模式「可以讓客戶端使用代理仿問接口」,一般使用最多的是動(dòng)態(tài)代理
代理模式的定義
所謂代理就是代表某個(gè)真實(shí)對(duì)象砌函,也就是代理拿到真實(shí)對(duì)象的引用然后就可以實(shí)現(xiàn)真實(shí)對(duì)象中的功能了
代理模式的結(jié)構(gòu)
角色 | 類別 | 說明 |
---|---|---|
AbstractObject | 接口或抽象類 | 抽象出共同的屬性 |
RealObject | 真實(shí)的類 | 實(shí)現(xiàn)了抽象角色 |
Prxoy | 代理的類 | 實(shí)現(xiàn)了抽象角色斩披,持有真實(shí)類的引用 |
代理模式簡(jiǎn)單的 UML
代理模式的分類
- 遠(yuǎn)程代理:為不同地理的對(duì)象提供局域網(wǎng)代表對(duì)象
- 虛擬代理:根據(jù)需要將資源消耗很大的對(duì)象進(jìn)行延遲,真正需要的時(shí)候再創(chuàng)建
- 安全代理:控制用戶的訪問權(quán)限
- 智能代理:提供對(duì)目標(biāo)對(duì)象額外的服務(wù)「使用最多的」
代理模式的實(shí)現(xiàn)方式「屬于智能代理」
- 靜態(tài)代理方法
- 動(dòng)態(tài)代理方法
二、代理模式舉例
1雏掠、幫忙追 MM
話說在高中期間斩祭,小明看上了我們班一位女同學(xué),可是小明是一個(gè)害羞膽小的人「有賊心沒賊膽」乡话,于是小明跑到我的跟前:Chain 哥摧玫,我看上了咱們班的小倩,你能幫我追一下嗎 .... 绑青。聽小明巴拉巴拉一大堆诬像,本著哥們義氣的我非常爽快的答應(yīng)了,就有了下面的追 MM 手段
簡(jiǎn)單的 UML
根據(jù) UML 擼碼--這里使用靜態(tài)代理方法
- 1闸婴、要追 MM 首先肯定有 MM 坏挠,定義 MM.java
public class MM {
private String name ; // 姓名
private int age ;//年齡
private String address ; // 住址
public MM(String name){
this.name = name ;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
- 2、定義一個(gè)追 MM 方法的接口 ZhuimmWay.java
/**
* Created by TigerChain
* 追 MM 的方法邪乍,是一個(gè)抽象角色
*/
public interface ZhuimmWay {
// 送花
void giveFlowers() ;
// 寫情書
void writeLoveLetters() ;
// 買衣服
void buyClothes() ;
// 干一些其它的事
void doSomthing() ;
}
- 3降狠、主人公小明上場(chǎng) XiaoMing.java
/**
* Created by TigerChain
* 主人公小明,真正的角色
*/
public class XiaoMing implements ZhuimmWay {
// 要追的 MM
private MM mm ;
public void like(MM mm){
this.mm = mm ;
}
@Override
public void giveFlowers() {
System.out.println(mm.getName()+" 送給你一朵花");
}
@Override
public void writeLoveLetters() {
System.out.println(mm.getName()+" 給你八封情書");
}
@Override
public void buyClothes() {
System.out.println(mm.getName()+" 這是給你買的衣服");
}
@Override
public void doSomthing() {
System.out.println("給 "+mm.getName()+"說好聽的話");
System.out.println("給 "+mm.getName()+"洗衣服庇楞,買單等等一系列手段");
}
}
- 4榜配、代理人 TigerChain 上場(chǎng) ProxyTigerChain.java
/**
* Created by TigerChain
* 代理人,我上場(chǎng)了吕晌,感覺像媒婆
*/
public class ProxyTigerChain implements ZhuimmWay {
private XiaoMing xiaoMing ;
public ProxyTigerChain(XiaoMing xiaoMing, MM mm){
this.xiaoMing = xiaoMing ;
xiaoMing.like(mm);
}
@Override
public void giveFlowers() {
xiaoMing.giveFlowers();
}
@Override
public void writeLoveLetters() {
xiaoMing.writeLoveLetters();
}
@Override
public void buyClothes() {
xiaoMing.buyClothes();
}
@Override
public void doSomthing() {
xiaoMing.doSomthing();
}
}
- 5蛋褥、一切準(zhǔn)備就緒,開始追吧睛驳,來個(gè)測(cè)試類 Test.java
public class Test {
public static void main(String args[]){
// 主人公小明
XiaoMing xiaoMing = new XiaoMing();
// 要追的人小倩
MM xiaoqian = new MM("小倩") ;
// 小明委托我去幫他追小倩
ProxyTigerChain proxyChain = new ProxyTigerChain(xiaoMing,xiaoqian) ;
proxyChain.giveFlowers();
proxyChain.writeLoveLetters();
proxyChain.buyClothes();
proxyChain.doSomthing();
}
}
- 6烙心、運(yùn)行查看結(jié)果
上面的代碼完美嗎?完美個(gè)鳥鳥乏沸,試想把 Test 比做一個(gè)場(chǎng)景:比如是在 KTV 淫茵,我靠,小明不是害羞嗎屎蜓?竟然也出現(xiàn)在 KTV 中「如果小明能當(dāng)明看著你幫他追小倩痘昌,早就自己動(dòng)手了」,所以按正常邏輯小明不應(yīng)該出現(xiàn)在 KTV「Test 中」
- 7炬转、修改代碼辆苔,我們添加一個(gè) ZhuimmFactory.java
/**
* Created by TigerChain
* 定義一個(gè)工廠類,這樣就屏蔽了客戶端對(duì)代理的感知
*/
public class ZhuimmFactory {
public static ZhuimmWay getInstance(String name){
return new ProxyTigerChain(new XiaoMing(),new MM(name)) ;
}
}
嘻嘻扼劈,不知不覺又用到以前學(xué)到的簡(jiǎn)單工廠模式了「學(xué)以致用驻啤,不錯(cuò)不錯(cuò)」,我們把代理事情都放在工廠中去做荐吵,這樣客戶端對(duì)代理是無感知的骑冗,這也符合程序開發(fā)的正常邏輯
- 8赊瞬、修改 Test 端調(diào)用代碼
public class Test {
public static void main(String args[]){
// 調(diào)用者不知道調(diào)用的是代理類還是真實(shí)類,這才是正常的邏輯呀
ZhuimmWay zhuimmWay = ZhuimmFactory.getInstance("小倩") ;
zhuimmWay.giveFlowers();
zhuimmWay.writeLoveLetters();
zhuimmWay.buyClothes();
zhuimmWay.doSomthing();
}
}
- 9贼涩、運(yùn)行查看結(jié)果
想知道結(jié)局嗎巧涧?很不幸,小倩也有點(diǎn)"白癡"遥倦,我提醒好多次是小明喜歡她「其實(shí)我最多是代理小明送花等這些事情谤绳,也就是說錢花小明的,美女我來追」袒哥,可是她最終還是看上我了「有點(diǎn)自戀」缩筛,所以以后追 MM 的時(shí)候,千萬千萬不要找代理「以上故事純屬虛構(gòu)堡称,如有雷同瞎抛,那么小明以后就張點(diǎn)心吧」
2、真假美猴王
1却紧、使用靜態(tài)代理完成
六耳獼猴夢(mèng)想簡(jiǎn)單的 UML
根據(jù) UML 擼碼
- 1桐臊、定義抽象接口 IToWest.java
/**
* Created 抽象類,去西天的條件
*/
public interface IToWest {
//保護(hù)唐僧
void baohuTangSeng() ;
//降妖除魔
void xiangYaoChuMo() ;
//上天入地
void shangTianRuDi() ;
}
- 2啄寡、定義孫悟空類 SunWuKong.java
/**
* Created by Tigerchain
* 悟空
*/
public class SunWuKong implements IToWest{
@Override
public void baohuTangSeng() {
System.out.println("我孫悟空能 保護(hù)唐僧");
}
@Override
public void xiangYaoChuMo() {
System.out.println("我孫悟空能 降妖除魔");
}
@Override
public void shangTianRuDi() {
System.out.println("我孫悟空能 能上天入地");
}
}
- 3豪硅、定義六耳獼猴類「代理角色」 LiuErMiHou.java
package prxoy.monkeyking;
/**
* Created by Tigerchain
* 悟空的代理六耳獼猴
*/
public class LiuErMiHou extends SunWuKong implements IToWest {
@Override
public void baohuTangSeng() {
super.baohuTangSeng();
}
@Override
public void xiangYaoChuMo() {
super.xiangYaoChuMo();
}
@Override
public void shangTianRuDi() {
super.shangTianRuDi();
}
}
- 4、測(cè)試 Test.java
/**
* Created by TigerChain
* 測(cè)試類 六耳 代理悟空
*/
public class Test {
public static void main(String args[]){
IToWest liuErMiHou = new LiuErMiHou() ;
liuErMiHou.baohuTangSeng();
liuErMiHou.xiangYaoChuMo();
liuErMiHou.shangTianRuDi();
System.out.println("我孫悟空能去得了西天");
}
}
- 5挺物、運(yùn)行查看結(jié)果
好了,上面我們看到我們使用代理類直接繼承了真實(shí)的類「這也是代理的一個(gè)變種」飘弧,但是根據(jù)多用類組合少用繼承的規(guī)則识藤,我們還是少用這種繼承形式的代理
以上是靜態(tài)代理,靜態(tài)代理有局限性次伶,想如果悟空多了項(xiàng)技能痴昧,六耳獼猴就得學(xué)此項(xiàng)技能「感覺很像我們搞技術(shù)的,技術(shù)日新月異冠王,得不斷的學(xué)習(xí)才能進(jìn)步」
靜態(tài)代理的缺點(diǎn):
- 1赶撰、代理的方法如果很多,那么就要為每個(gè)方法都要代理柱彻,規(guī)模大的程序受不了
- 2豪娜、如果真實(shí)類中新添加一個(gè)方法或功能,那么代理類中就一一對(duì)應(yīng)的寫出來哟楷,這樣不利于擴(kuò)展并且增加代碼維護(hù)成本
- 3瘤载、一個(gè)代理類只能代理一個(gè)真實(shí)的對(duì)象
2、使用動(dòng)態(tài)代理完成
動(dòng)態(tài)代理就是代理類不是在代碼中定義的卖擅,而是根據(jù)我們的指示動(dòng)態(tài)生成的「通過反射機(jī)制動(dòng)態(tài)生成代理者對(duì)象」鸣奔,在編碼階段墨技,你從代碼上根本不知道誰代理誰,具體代理誰挎狸,好吧太繞了扣汪,直接看代碼
1、Proxy 類
說動(dòng)態(tài)代理之前锨匆,我們先來看看 Java 中提供的 Proxy 類
看看這個(gè)類的注釋一部分
/* {@code Proxy} provides static methods for creating dynamic proxy
* classes and instances, and it is also the superclass of all
* dynamic proxy classes created by those methods.
* .....
*
/
public class Proxy implements java.o.Serializable {
.... 省略代碼
}
從注釋可以看出 Proxy 提供一些靜態(tài)方法來創(chuàng)建動(dòng)態(tài)代理類和實(shí)例
Proxy 簡(jiǎn)單的 UML
Proxy 主要方法講解
Proxy 主要方法就是 newProxyInstance 這個(gè)方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
... // 省略若干代碼
// 取得代理類
Class<?> cl = getProxyClass0(loader, intros)
... // 省略若干代碼
// 調(diào)用代理類的構(gòu)造方法
final Constructor<?> cons = cl.getConstructor(constructorParams);
... // 省略若干代碼
final InvocationHandler ih = h;
... // 省略若干代碼
// 通過代理類的構(gòu)造方法生成代理類的實(shí)例
return cons.newInstance(new Object[]{h});
}
其中三個(gè)參數(shù):
- ClassLoader loader:代理類的類加載器
- Class<?>[] interfaces:代理類要實(shí)現(xiàn)的接口列表
- InvocationHandler h:調(diào)用處理程序
從 newProxyInstance 方法中我們知道了代理對(duì)象是如何產(chǎn)生的了「注釋很清楚了」
再看看 InvocationHandler
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
其中三個(gè)參數(shù):
- Object proxy: 被代理的對(duì)象
- Method method:要操作的方法
- Object[] args:方法要傳入的參數(shù)私痹,可以沒有,也可以有多個(gè)或 null
InvocationHandler 接口中的方法就是執(zhí)行被代理對(duì)象中的方法
2统刮、使用動(dòng)態(tài)代理修改真假美猴王代碼
動(dòng)態(tài)代理悟空 簡(jiǎn)單的UML
根據(jù) UML 擼碼
只需要在原有代碼的基礎(chǔ)上添加一個(gè)動(dòng)態(tài)類并且刪掉六耳獼猴類「動(dòng)態(tài)代理來了窗轩,小六你還不快撤」,然后修改 Test 即可
- 1成洗、添加動(dòng)態(tài)代理類 ToWestProxy.java
/**
* 動(dòng)態(tài)代理類
*/
public class ToWestProxy implements InvocationHandler {
// 需要代理的對(duì)象即真實(shí)對(duì)象
private Object delegate ;
public Object getProxy(Object delegate){
this.delegate = delegate ;
// 動(dòng)態(tài)構(gòu)建一個(gè)代理
return Proxy.newProxyInstance(delegate.getClass().getClassLoader(),delegate.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(delegate,args) ; // 通過反射調(diào)用真實(shí)對(duì)象對(duì)應(yīng)的方法
}
}
我們看到上在被代理的對(duì)象是一個(gè) Object 類型印机,所以可以看出這個(gè)代理類就是一個(gè)萬能的代理,不僅僅可以代理悟空鞭衩,牛魔王也能代理「扯遠(yuǎn)了」
- 2学搜、修改 Test.java
/**
* Created by TigerChain
* 測(cè)試類
*/
public class Test {
public static void main(String args[]){
IToWest sunWuKong = new SunWuKong() ;
// 取得動(dòng)態(tài)代理
IToWest proxy = (IToWest) new ToWestProxy().getProxy(sunWuKong);
proxy.baohuTangSeng();
proxy.xiangYaoChuMo();
proxy.shangTianRuDi();
System.out.println("我孫悟空能去得了西天");
}
}
看到了,真實(shí)對(duì)象悟空隨便你改论衍,我再添加接口瑞佩,方法,我動(dòng)態(tài)代理不用動(dòng)「如果是靜態(tài)代理六耳獼猴坯台,那就得隨著悟空的修改必須得修改自己」
而且炬丸,我們還可以得出,這個(gè)動(dòng)態(tài)代理不僅僅可以代理悟空蜒蕾,簡(jiǎn)直可以代理一切對(duì)象「不信你定義一個(gè)牛魔王試試」
- 3稠炬、運(yùn)行查看結(jié)果
簡(jiǎn)直 perfect 有木有
3、自動(dòng)售票機(jī)
隨著科技的發(fā)達(dá)咪啡,我們現(xiàn)在買車票的時(shí)候可以在自動(dòng)售票機(jī)「代理售票人員」上購(gòu)買
自動(dòng)售票機(jī)簡(jiǎn)單的 UML
根據(jù) UML 擼碼--采用動(dòng)態(tài)代理技術(shù)
- 1首启、先來一個(gè)抽象角色 ISellTicket.java
/**
* Created by TigerChain
* 定義一個(gè)抽象接口
*/
public interface ISellTicket {
// 售票
void sellTicket() ;
}
- 2、要出票撤摸,當(dāng)然有買的票的人 User.java
/**
* Created by TigerChain
* 買票的人
*/
public class User {
private String uname ; //姓名
private String address ; // 地址
private String sex ; // 性別
private String idNum ; // 身份證號(hào)
private String pay ; // 掏票錢
public String getUname() {
return name;;
}
public void setUname(String uname) {
this.uname = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getIdNum() {
return idNum;
}
public void setIdNum(String idNum) {
this.idNum = idNum;
}
public String getPay() {
return pay;
}
public void setPay(String pay) {
this.pay = pay;
}
}
- 3毅桃、真實(shí)對(duì)象售票員小張 XiaoZhangSeller.java
/**
* Created 真實(shí)的售票員小張
*/
public class XiaoZhangSeller implements ISellTicket {
private User user ;
public XiaoZhangSeller(User user){
this.user = user ;
}
@Override
public void sellTicket() {
if(null !=user){
System.out.println("買票者的信息===============");
System.out.println("買票者姓名:"+user.getUname());
System.out.println("買票性別:"+user.getSex());
System.out.println("買票者身份證號(hào):"+user.getIdNum());
System.out.println("買票者住址:"+user.getUname());
System.out.println("==============================") ;
System.out.println("正在驗(yàn)證信息...信息無誤,請(qǐng)支付票錢");
System.out.println("買票者支付:"+user.getPay()+" 元");
System.out.println("請(qǐng)稍等正在出票.....");
System.out.println("出票成功:從西安到寶雞大巴進(jìn)站去坐");
}
}
}
- 4、動(dòng)態(tài)代理 DyAutoSellerProxy.java
/**
* Created by TigerChain
* 自動(dòng)出票機(jī),為了演示名字這樣想,其實(shí)這是一個(gè)萬能的動(dòng)態(tài)代理
*/
public class DyAutoSellerProxy implements InvocationHandler {
private Object object ;
public DyAutoSellerProxy(Object object){
this.object = object ;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(object,args) ;
}
}
- 5准夷、測(cè)試一下 Test.java
/**
* Created by TigerChain
* 測(cè)試類
*/
public class Test {
public static void main(String args[]){
// 定義個(gè)買票者
User tigerChain = new User() ;
tigerChain.setUname("TigerChain");
tigerChain.setAddress("中國(guó)陜西");
tigerChain.setSex("男");
tigerChain.setIdNum("610326************");
tigerChain.setPay("45.00");
// 真實(shí)的買票員小張
ISellTicket iSellTicket = new XiaoZhangSeller(tigerChain) ;
// 動(dòng)態(tài)代理
DyAutoSellerProxy dyAutoSellerProxy = new DyAutoSellerProxy(iSellTicket) ;
// 動(dòng)態(tài)創(chuàng)建一個(gè)出票機(jī)钥飞,把出票交給出票機(jī)去處理
ISellTicket iSellTicket1 = (ISellTicket) Proxy.newProxyInstance(iSellTicket.getClass().getClassLoader(),iSellTicket.getClass().getInterfaces(),dyAutoSellerProxy);
iSellTicket1.sellTicket();
}
}
- 6、運(yùn)行查看結(jié)果
自么樣一個(gè)自動(dòng)售票機(jī)就完成了「完全代理了人工去賣票」
PS:這個(gè) Demo 使用動(dòng)態(tài)代理實(shí)現(xiàn)的冕象,請(qǐng)大家自行使用靜態(tài)代理實(shí)現(xiàn)本 Demo 代承,一定要?jiǎng)邮謱?shí)踐哦
4、AIDL 進(jìn)行進(jìn)程間通訊「遠(yuǎn)程代理」
AIDL「Android 接口定義語言渐扮,是一種語言论悴,其實(shí)就是 Android 中的遠(yuǎn)程 Service」掖棉,再說 AIDL 之前就不得不說 Binder「這里簡(jiǎn)潔明了的說一下 Binder 是什么,不展開深入討論膀估,如果深入展開幔亥,三天三夜也說不完」
什么是 Binder
由于兩個(gè)進(jìn)程不能直接進(jìn)行通訊「為了安全系統(tǒng)有進(jìn)程隔離機(jī)制」,所以兩個(gè)進(jìn)程之間是不能直接進(jìn)行通訊的察纯。Binder 可以說是 Android 系統(tǒng)中最重要的架構(gòu)之一帕棉。Binder 是連接 Client「進(jìn)程」 和 Server「進(jìn)程」 的一個(gè)橋梁,Binder 是進(jìn)程間通信的方式之一,在 Android 用的灰潮牵灰常的多
我們先來看看 Android 的架構(gòu)圖像
圖片來自 Android 的源碼官站:https://source.android.com/devices/
從 Android 的框架圖中我們可以看到香伴,應(yīng)用程序框架層和系統(tǒng)服務(wù)層之間就是通過 Binder IPC 進(jìn)行通訊的,說 Binder 機(jī)制前,我們先了解幾個(gè)特點(diǎn)
- 1具则、兩個(gè)進(jìn)程之間不能直接通信
- 2即纲、內(nèi)核可以仿問進(jìn)程中的所有數(shù)據(jù)
- 3、兩個(gè)進(jìn)程之間不能直接進(jìn)行通信博肋,我們可以借助內(nèi)核做中轉(zhuǎn)達(dá)到間接通信的目的「Binder 就是這種機(jī)制」
Binder 下兩個(gè)進(jìn)程通信的簡(jiǎn)易流程
PS: 以上圖是便于理解所以抽象出來一張圖低斋,真實(shí)的 Binder 比這個(gè)過程復(fù)雜的多,這牽扯到 java 層的 Binder 匪凡,native 層的 Binder 等等「這不是我們討論的重點(diǎn)」膊畴,方便我們理解,我們可以認(rèn)為客戶端的進(jìn)程拿到服務(wù)端的引用病游,所以就可以調(diào)用服務(wù)端進(jìn)程的方法了
說了這么多唇跨,這跟代理有個(gè)毛關(guān)系呢,別急我們寫一個(gè) AIDL 的實(shí)例分析一下:
AIDL demo 簡(jiǎn)單的 UML
根據(jù) uml 寫代碼
我們寫一個(gè)簡(jiǎn)單的通過 Client 進(jìn)程調(diào)用 Server 進(jìn)程返回一個(gè)字符串功能礁遵,為了方便起見轻绞,我們直接在一個(gè)項(xiàng)目中創(chuàng)建「Server 開啟在另一個(gè)進(jìn)程中,開兩個(gè) APP 進(jìn)行通信大家可以自行試一下佣耐,道理一模一樣的」
- 1、在項(xiàng)目中新建一個(gè) AIDL 文件「在 AS 中的 APP上直接右鍵 new AIDL 即可」
interface CustomAIDL {
String getStr() ;
}
此時(shí)我們點(diǎn)擊一下
app\build\generated\source\aidl\debug\包名\CustomAIDL.java
文件「把 AS 切換到 project 視圖下很容易找到」兼砖,這是 IDE 幫我們自動(dòng)生成的
- 2、定義一個(gè)遠(yuǎn)程服務(wù) AIDLRemoteService.java
/**
* @Description 創(chuàng)建一個(gè)遠(yuǎn)程服務(wù)
* @Creator TigerChain(創(chuàng)建者)
*/
public class AIDLRemoteService extends Service {
private final CustomAIDL.Stub aidl = new CustomAIDL.Stub() {
@Override
public String getStr() throws RemoteException {
return " 我是遠(yuǎn)程服務(wù)返回的 HELLO ";
}
} ;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return aide;
}
}
- 3既棺、定義 AidlActivity 測(cè)試調(diào)用 「核心代碼給出讽挟,其余代碼看 Demo 即可」
public class AidlActivity extends AppCompatActivity implements View.OnClickListener{
private CustomAIDL customAIDL ;
... 省略若干代碼
// 客戶端連接服務(wù)
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
customAIDL = CustomAIDL.Stub.asInterface(service) ;
Log.e("service:","onServiceConnected") ;
isServerStarted = true ;
}
@Override
public void onServiceDisconnected(ComponentName name) {
customAIDL = null ;
Log.e("service:","onServiceDisconnected") ;
isServerStarted = false ;
}
} ;
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_bind_service:
// 綁定服務(wù)
bindService(new Intent(AidlActivity.this,AIDLRemoteService.class),serviceConnection, Context.BIND_AUTO_CREATE) ;
break ;
case R.id.btn_test_method:
if(!isServerStarted){
Toast.makeText(AidlActivity.this,"請(qǐng)先綁定服務(wù)先",Toast.LENGTH_SHORT).show();
return ;
}
try {
String str = customAIDL.getStr();
Toast.makeText(AidlActivity.this,str,Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
break ;
default:
break ;
}
}
... 省略若干代碼
}
- 4、在 mainifests 中注冊(cè)服務(wù)
<service android:name=".Proxy.AIDL.AIDLRemoteService"
android:process=":reomte"></service>
我這里給服務(wù)定義了一個(gè) process 丸冕,那說明這個(gè)服務(wù)是運(yùn)行在一個(gè)新進(jìn)程中的
- 5耽梅、測(cè)試一下,運(yùn)行查看結(jié)果
我們看一下當(dāng)前項(xiàng)目進(jìn)程情況
的確是兩個(gè)進(jìn)程「AidlActivity 和 AIDLRemoteService 分別在兩個(gè)進(jìn)程中」胖烛,我們定義的 remote 也顯示出來了,看一下結(jié)果
怎么樣眼姐,兩個(gè)進(jìn)程之間完美的進(jìn)行了通信了
通個(gè)毛呢诅迷?這和 proxy 有個(gè)啥關(guān)系呀「巴拉巴拉這么久」,不要急嗎众旗?軟件開發(fā)有一條宗旨:先讓它運(yùn)行起來「我們先把 Demo 運(yùn)行起來再說嗎:咳咳又到了吃藥的時(shí)間了」罢杉,我們來分析一下上面的調(diào)用過程
過程分析
- 1、還記得我們上面說的 AD 幫我們自動(dòng)生成的 CustomAIDL.java 文件嗎贡歧,我們來一窺它的真容「以下代碼是格式化后的」
// 這里的 IInterface 代表遠(yuǎn)程 Server 對(duì)象有什么能力
public interface CustomAIDL extends android.os.Interface {
/**
* Local-side IPC implementation stub class.
*/
// 在 server 端調(diào)用
public static abstract class Stub extends android.os.Binder implements designpattern.jun.com.designpattern.CustomAIDL {
private static final java.lang.String DESCRIPTOR = "designpattern.jun.com.designpattern.CustomAIDL";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an designpattern.jun.com.designpattern.CustomAIDL interface,
* generating a proxy if needed.
* 其中的 android.os.IBinder obj 對(duì)象是驅(qū)動(dòng)給們的滩租,這個(gè)就是我們綁定 service ,在 onServiceConnecttion 回調(diào)里面這個(gè)對(duì)象拿到一個(gè)遠(yuǎn)程的 Service
*/
public static designpattern.jun.com.designpattern.CustomAIDL asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof designpattern.jun.com.designpattern.CustomAIDL))) {
// client 和 Server 在同一個(gè)進(jìn)程調(diào)用 后面 debug 可以驗(yàn)證
return ((designpattern.jun.com.designpattern.CustomAIDL) win);
}
// cliet 和 Server 不在同一個(gè)進(jìn)程調(diào)用代理對(duì)象 后面 debug 可以驗(yàn)證
return new designpattern.jun.com.designpattern.CustomAIDL.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
// 給客戶端寫數(shù)據(jù)
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getStr: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getStr();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 運(yùn)行在客戶端 server 進(jìn)程的遠(yuǎn)程代理,實(shí)現(xiàn)對(duì)遠(yuǎn)程對(duì)象的仿問
private static class Proxy implements designpattern.jun.com.designpattern.CustomAIDL {
private android.os.Binder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.lang.String getStr() throws android.os.RemoteException {
// 讀取服務(wù)端寫過來的數(shù)據(jù)
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getStr, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getStr = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public java.lang.String getStr() throws android.os.RemoteException;
}
這下看到 Proxy 了吧「是不是有點(diǎn)小激動(dòng)呢」利朵,我們來分析一下
上面的圖就是一個(gè)簡(jiǎn)單的 AIDL 的流程圖律想,方便理解認(rèn)為 CustomAIDL.stub 就是遠(yuǎn)程進(jìn)程,它把信息注冊(cè)到 Binder 中绍弟, CustomAIDL.Stub.Proxy 就是一個(gè)代理技即,代理什么呢?代理遠(yuǎn)程的 Binder 晌柬,遠(yuǎn)程 Binder 把方法傳給 Client 就完成了兩個(gè)進(jìn)程間通信「詳細(xì)過程比這個(gè)復(fù)雜」姥份,對(duì)于 Binder 的入門介紹可以參看:Binder 學(xué)習(xí)指南 還是非常不錯(cuò)的,建議看三遍以上
PS:這里再說一點(diǎn)年碘,以上情況是針對(duì) client 和 server 在兩個(gè)進(jìn)程間的通信澈歉,如果 client 和 server 在一個(gè)進(jìn)程中,則 CustomAIDL.Stub.Proxy 就不會(huì)調(diào)用「在同一個(gè)進(jìn)程中屿衅,我自己就能調(diào)自己還代理個(gè)毛呀」埃难,不信?以結(jié)果征服你
client 和 server 同一進(jìn)程和不同進(jìn)程分析
- 1涤久、不同進(jìn)程
<service android:name=".Proxy.AIDL.AIDLRemoteService"
android:process=":reomte"></service>
通過以上配置涡尘,我們可以看到 AIDLRemoteService 是運(yùn)行在單獨(dú)進(jìn)程中的,我們?cè)?CustomAIDL.java 中的 asInterface 方法中 debug 跟一下看看結(jié)果
通過圖我們可以看出响迂,如果 client 和 server 不在同一個(gè)進(jìn)程中考抄,那么代碼就會(huì)走到
調(diào)用代理的地方---CustomAIDL.Stub.Proxy,并傳遞遠(yuǎn)程代理的對(duì)象
- 2、在同一進(jìn)程
去掉 service 中的 android:process=":reomte" 則 client 和 server 就在同一進(jìn)程了
<service android:name=".Proxy.AIDL.AIDLRemoteService"/>
同理 debug 看結(jié)果
對(duì)比上面的圖我們就知道了蔗彤,這里的 iin 不為空川梅,進(jìn)入了 if 的方法體「沒有調(diào)用代理」,至此上面的結(jié)果驗(yàn)證完畢
關(guān)于 AIDL 遠(yuǎn)程代理就說到這里了然遏,如果對(duì) Binder 想要深入了解贫途,可以自行回去研究「這不在本節(jié)的范圍內(nèi)」
WTF 一個(gè) AIDL 說了這么大半天,希望大家不要暈「我都有點(diǎn)暈了」
源碼地址: https://github.com/tigerchain/DesignPattern 看 proxy/aidl 這部分
三待侵、Android 源碼中的代理模式
其實(shí)通過上面的 AIDL 實(shí)驗(yàn)丢早,我們就可以知道 Binder 使用的就是遠(yuǎn)程代理模式,Android 中的源碼使用非常多秧倾,我就不一一分析了「說的太多人會(huì)受不鳥」怨酝,感興趣的朋友可以自行分析傀缩,我這里貼出一張圖,大家可以看
我們看看應(yīng)用程序框架層的 XXXManager 對(duì)應(yīng)田系統(tǒng)層的 XXXService 它們之間通過使用 AIDL 來進(jìn)行跨進(jìn)程通信凫碌,有興趣可以扒扒這部分的源碼看一下
四扑毡、代理模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 1、代理模式拿到的真實(shí)對(duì)象的引用盛险,把真實(shí)對(duì)象很好的保護(hù)起來安全性高
- 2瞄摊、擴(kuò)展性好
缺點(diǎn)
- 增加了系統(tǒng)的復(fù)雜度,增加了額外好多的代碼「設(shè)計(jì)模式好像都是這樣」
到此為止苦掘,我們把代理模式就說完了换帜,由于這篇篇幅比較大,Android 源碼也沒有給大家分析「希望大家自行去看看鹤啡,希望你有一種哦~原來是這樣的趕腳」惯驼,其它的虛擬代理,緩存代理大家有興趣也可以試試
參考資料
- 小米開放平臺(tái):徹底理解ANDROID BINDER通信架構(gòu)(上)
- Binder學(xué)習(xí)指南 建議看三遍以上递瑰,非乘钌基礎(chǔ)的一步步介紹 Binder