摘要:話說(shuō)誰(shuí)還干類(lèi)似的事,就在文章末尾點(diǎn)個(gè)贊代銷(xiāo)店等其實(shí)就是現(xiàn)在的商店,以前小的時(shí)候聽(tīng)家鄉(xiāng)人叫代銷(xiāo)店,也是一種代理模式??梢哉f(shuō)是系統(tǒng)中最重要的架構(gòu)之一。
PS:轉(zhuǎn)載請(qǐng)注明出處
作者: TigerChain
地址: http://www.jianshu.com/p/1b3b6b003032
本文出自 TigerChain 簡(jiǎn)書(shū) 人人都會(huì)設(shè)計(jì)模式
教程簡(jiǎn)介
1、閱讀對(duì)象
本篇教程適合新手閱讀,老手直接略過(guò)
2、教程難度
初級(jí),本人水平有限,文章內(nèi)容難免會(huì)出現(xiàn)問(wèn)題,如果有問(wèn)題歡迎指出,謝謝
一、什么是代理模式 1、生活中的代理正文
1、微商代理
代理在生活中就太多了,比如微商,在朋友圈中很多時(shí)候都可以看到微商說(shuō)城招全國(guó)代理「不需要貨源,不需要啟動(dòng)資金,只需要一個(gè)電話就能做生意,好吧我口才不好,沒(méi)有人家吹的好」,這類(lèi)代理就是替賣(mài)家出售商品
2、追女孩
遙想當(dāng)年情竇初開(kāi)「初中的時(shí)候」,喜歡上了一個(gè)女子,可是迫于害羞,就給女孩子寫(xiě)了幾封情書(shū),買(mǎi)了一束花「但是自己沒(méi)有那個(gè)賊膽送」,就讓我們班里一個(gè)和女孩認(rèn)識(shí)的朋友交給她,現(xiàn)在想來(lái)原來(lái)幫我送情書(shū)的女生就是我的代理呀「幫我完成我想要完成的事」~~嘻嘻。話說(shuō)誰(shuí)還干類(lèi)似的事,就在文章末尾點(diǎn)個(gè)贊
3、代銷(xiāo)店等
其實(shí)就是現(xiàn)在的商店,以前小的時(shí)候聽(tīng)家鄉(xiāng)人叫代銷(xiāo)店,也是一種代理模式。細(xì)細(xì)一想,跑業(yè)務(wù)的也是代理,律師也是代理,明星的助理就是代理,京東送貨機(jī)器人是代理,共享"女友",那個(gè)"女友"也是代理「你懂得」,等等等等。不敢再說(shuō)了,再說(shuō)萬(wàn)物都成代理了「不好意思,又忘了吃藥了」
2、程序中的代理其實(shí)程序中使用的代理是非常多的,我們?cè)诰帉?xiě) MVC 業(yè)務(wù)的時(shí)候就可以使用代理模式「可以讓客戶端使用代理仿問(wèn)接口」,一般使用最多的是動(dòng)態(tài)代理
代理模式的定義
所謂代理就是代表某個(gè)真實(shí)對(duì)象,也就是代理拿到真實(shí)對(duì)象的引用然后就可以實(shí)現(xiàn)真實(shí)對(duì)象中的功能了
代理模式的結(jié)構(gòu)
角色 | 類(lèi)別 | 說(shuō)明 |
---|---|---|
AbstractObject | 接口或抽象類(lèi) | 抽象出共同的屬性 |
RealObject | 真實(shí)的類(lèi) | 實(shí)現(xiàn)了抽象角色 |
Prxoy | 代理的類(lèi) | 實(shí)現(xiàn)了抽象角色,持有真實(shí)類(lèi)的引用 |
代理模式簡(jiǎn)單的 UML
代理模式的分類(lèi)
遠(yuǎn)程代理:為不同地理的對(duì)象提供局域網(wǎng)代表對(duì)象
虛擬代理:根據(jù)需要將資源消耗很大的對(duì)象進(jìn)行延遲,真正需要的時(shí)候再創(chuàng)建
安全代理:控制用戶的訪問(wèn)權(quán)限
智能代理:提供對(duì)目標(biāo)對(duì)象額外的服務(wù)「使用最多的」
代理模式的實(shí)現(xiàn)方式「屬于智能代理」
靜態(tài)代理方法
動(dòng)態(tài)代理方法
二、代理模式舉例1、幫忙追 MM
話說(shuō)在高中期間,小明看上了我們班一位女同學(xué),可是小明是一個(gè)害羞膽小的人「有賊心沒(méi)賊膽」,于是小明跑到我的跟前:Chain 哥,我看上了咱們班的小倩,你能幫我追一下嗎 .... 。聽(tīng)小明巴拉巴拉一大堆,本著哥們義氣的我非常爽快的答應(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() ; // 寫(xiě)情書(shū) void writeLoveLetters() ; // 買(mǎi)衣服 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()+" 給你八封情書(shū)"); } @Override public void buyClothes() { System.out.println(mm.getName()+" 這是給你買(mǎi)的衣服"); } @Override public void doSomthing() { System.out.println("給 "+mm.getName()+"說(shuō)好聽(tīng)的話"); System.out.println("給 "+mm.getName()+"洗衣服,買(mǎi)單等等一系列手段"); } }
4、代理人 TigerChain 上場(chǎng) ProxyTigerChain.java
/** * Created by TigerChain * 代理人,我上場(chǎng)了,感覺(jué)像媒婆 */ 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)備就緒,開(kāi)始追吧,來(lái)個(gè)測(cè)試類(lèi) 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è)鳥(niǎo)鳥(niǎo),試想把 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è)工廠類(lèi),這樣就屏蔽了客戶端對(duì)代理的感知 */ public class ZhuimmFactory { public static ZhuimmWay getInstance(String name){ return new ProxyTigerChain(new XiaoMing(),new MM(name)) ; } }
嘻嘻,不知不覺(jué)又用到以前學(xué)到的簡(jiǎn)單工廠模式了「學(xué)以致用,不錯(cuò)不錯(cuò)」,我們把代理事情都放在工廠中去做,這樣客戶端對(duì)代理是無(wú)感知的,這也符合程序開(kāi)發(fā)的正常邏輯
8、修改 Test 端調(diào)用代碼
public class Test { public static void main(String args[]){ // 調(diào)用者不知道調(diào)用的是代理類(lèi)還是真實(shí)類(lèi),這才是正常的邏輯呀 ZhuimmWay zhuimmWay = ZhuimmFactory.getInstance("小倩") ; zhuimmWay.giveFlowers(); zhuimmWay.writeLoveLetters(); zhuimmWay.buyClothes(); zhuimmWay.doSomthing(); } }
9、運(yùn)行查看結(jié)果
想知道結(jié)局嗎?很不幸,小倩也有點(diǎn)"白癡",我提醒好多次是小明喜歡她「其實(shí)我最多是代理小明送花等這些事情,也就是說(shuō)錢(qián)花小明的,美女我來(lái)追」,可是她最終還是看上我了「有點(diǎn)自戀」,所以以后追 MM 的時(shí)候,千萬(wàn)千萬(wàn)不要找代理「以上故事純屬虛構(gòu),如有雷同,那么小明以后就張點(diǎn)心吧」
2、真假美猴王
六耳獼猴夢(mèng)想簡(jiǎn)單的 UML
根據(jù) UML 擼碼
1、定義抽象接口 IToWest.java
/** * Created 抽象類(lèi),去西天的條件 */ public interface IToWest { //保護(hù)唐僧 void baohuTangSeng() ; //降妖除魔 void xiangYaoChuMo() ; //上天入地 void shangTianRuDi() ; }
2、定義孫悟空類(lèi) 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、定義六耳獼猴類(lèi)「代理角色」 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è)試類(lèi) 六耳 代理悟空 */ 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é)果
好了,上面我們看到我們使用代理類(lèi)直接繼承了真實(shí)的類(lèi)「這也是代理的一個(gè)變種」,但是根據(jù)多用類(lèi)組合少用繼承的規(guī)則,我們還是少用這種繼承形式的代理
以上是靜態(tài)代理,靜態(tài)代理有局限性,想如果悟空多了項(xiàng)技能,六耳獼猴就得學(xué)此項(xiàng)技能「感覺(jué)很像我們搞技術(shù)的,技術(shù)日新月異,得不斷的學(xué)習(xí)才能進(jìn)步」
靜態(tài)代理的缺點(diǎn):
1、代理的方法如果很多,那么就要為每個(gè)方法都要代理,規(guī)模大的程序受不了
2、如果真實(shí)類(lèi)中新添加一個(gè)方法或功能,那么代理類(lèi)中就一一對(duì)應(yīng)的寫(xiě)出來(lái),這樣不利于擴(kuò)展并且增加代碼維護(hù)成本
3、一個(gè)代理類(lèi)只能代理一個(gè)真實(shí)的對(duì)象
動(dòng)態(tài)代理就是代理類(lèi)不是在代碼中定義的,而是根據(jù)我們的指示動(dòng)態(tài)生成的「通過(guò)反射機(jī)制動(dòng)態(tài)生成代理者對(duì)象」,在編碼階段,你從代碼上根本不知道誰(shuí)代理誰(shuí),具體代理誰(shuí),好吧太繞了,直接看代碼
1、Proxy 類(lèi)
說(shuō)動(dòng)態(tài)代理之前,我們先來(lái)看看 Java 中提供的 Proxy 類(lèi)
看看這個(gè)類(lèi)的注釋一部分
/* {@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)方法來(lái)創(chuàng)建動(dòng)態(tài)代理類(lèi)和實(shí)例
Proxy 簡(jiǎn)單的 UML
Proxy 主要方法講解
Proxy 主要方法就是 newProxyInstance 這個(gè)方法
public static Object newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h){ ... // 省略若干代碼 // 取得代理類(lèi) Class> cl = getProxyClass0(loader, intros) ... // 省略若干代碼 // 調(diào)用代理類(lèi)的構(gòu)造方法 final Constructor> cons = cl.getConstructor(constructorParams); ... // 省略若干代碼 final InvocationHandler ih = h; ... // 省略若干代碼 // 通過(guò)代理類(lèi)的構(gòu)造方法生成代理類(lèi)的實(shí)例 return cons.newInstance(new Object[]{h}); }
其中三個(gè)參數(shù):
ClassLoader loader:代理類(lèi)的類(lèi)加載器
Class>[] interfaces:代理類(lèi)要實(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ù),可以沒(méi)有,也可以有多個(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)類(lèi)并且刪掉六耳獼猴類(lèi)「動(dòng)態(tài)代理來(lái)了,小六你還不快撤」,然后修改 Test 即可
1、添加動(dòng)態(tài)代理類(lèi) ToWestProxy.java
/** * 動(dòng)態(tài)代理類(lè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) ; // 通過(guò)反射調(diào)用真實(shí)對(duì)象對(duì)應(yīng)的方法 } }
我們看到上在被代理的對(duì)象是一個(gè) Object 類(lèi)型,所以可以看出這個(gè)代理類(lèi)就是一個(gè)萬(wàn)能的代理,不僅僅可以代理悟空,牛魔王也能代理「扯遠(yuǎn)了」
2、修改 Test.java
/** * Created by TigerChain * 測(cè)試類(lèi) */ 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)在買(mǎi)車(chē)票的時(shí)候可以在自動(dòng)售票機(jī)「代理售票人員」上購(gòu)買(mǎi)
自動(dòng)售票機(jī)簡(jiǎn)單的 UML
根據(jù) UML 擼碼--采用動(dòng)態(tài)代理技術(shù)
1、先來(lái)一個(gè)抽象角色 ISellTicket.java
/** * Created by TigerChain * 定義一個(gè)抽象接口 */ public interface ISellTicket { // 售票 void sellTicket() ; }
2、要出票,當(dāng)然有買(mǎi)的票的人 User.java
/** * Created by TigerChain * 買(mǎi)票的人 */ public class User { private String uname ; //姓名 private String address ; // 地址 private String sex ; // 性別 private String idNum ; // 身份證號(hào) private String pay ; // 掏票錢(qián) 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("買(mǎi)票者的信息==============="); System.out.println("買(mǎi)票者姓名:"+user.getUname()); System.out.println("買(mǎi)票性別:"+user.getSex()); System.out.println("買(mǎi)票者身份證號(hào):"+user.getIdNum()); System.out.println("買(mǎi)票者住址:"+user.getUname()); System.out.println("==============================") ; System.out.println("正在驗(yàn)證信息...信息無(wú)誤,請(qǐng)支付票錢(qián)"); System.out.println("買(mǎi)票者支付:"+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è)萬(wàn)能的動(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è)試類(lèi) */ public class Test { public static void main(String args[]){ // 定義個(gè)買(mǎi)票者 User tigerChain = new User() ; tigerChain.setUname("TigerChain"); tigerChain.setAddress("中國(guó)陜西"); tigerChain.setSex("男"); tigerChain.setIdNum("610326************"); tigerChain.setPay("45.00"); // 真實(shí)的買(mǎi)票員小張 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ī)就完成了「完全代理了人工去賣(mài)票」
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 接口定義語(yǔ)言,是一種語(yǔ)言,其實(shí)就是 Android 中的遠(yuǎn)程 Service」,再說(shuō) AIDL 之前就不得不說(shuō) Binder「這里簡(jiǎn)潔明了的說(shuō)一下 Binder 是什么,不展開(kāi)深入討論,如果深入展開(kāi),三天三夜也說(shuō)不完」
什么是 Binder
由于兩個(gè)進(jìn)程不能直接進(jìn)行通訊「為了安全系統(tǒng)有進(jìn)程隔離機(jī)制」,所以兩個(gè)進(jìn)程之間是不能直接進(jìn)行通訊的。Binder 可以說(shuō)是 Android 系統(tǒng)中最重要的架構(gòu)之一。Binder 是連接 Client「進(jìn)程」 和 Server「進(jìn)程」 的一個(gè)橋梁,Binder 是進(jìn)程間通信的方式之一,在 Android 用的灰?;页5亩?/p>
我們先來(lái)看看 Android 的架構(gòu)圖像
圖片來(lái)自 Android 的源碼官站:https://source.android.com/devices/
從 Android 的框架圖中我們可以看到,應(yīng)用程序框架層和系統(tǒng)服務(wù)層之間就是通過(guò) Binder IPC 進(jìn)行通訊的,說(shuō) Binder 機(jī)制前,我們先了解幾個(gè)特點(diǎn)
1、兩個(gè)進(jìn)程之間不能直接通信
2、內(nèi)核可以仿問(wèn)進(jìn)程中的所有數(shù)據(jù)
3、兩個(gè)進(jìn)程之間不能直接進(jìn)行通信,我們可以借助內(nèi)核做中轉(zhuǎn)達(dá)到間接通信的目的「Binder 就是這種機(jī)制」
Binder 下兩個(gè)進(jìn)程通信的簡(jiǎn)易流程
PS: 以上圖是便于理解所以抽象出來(lái)一張圖,真實(shí)的 Binder 比這個(gè)過(guò)程復(fù)雜的多,這牽扯到 java 層的 Binder ,native 層的 Binder 等等「這不是我們討論的重點(diǎn)」,方便我們理解,我們可以認(rèn)為客戶端的進(jìn)程拿到服務(wù)端的引用,所以就可以調(diào)用服務(wù)端進(jìn)程的方法了
說(shuō)了這么多,這跟代理有個(gè)毛關(guān)系呢,別急我們寫(xiě)一個(gè) AIDL 的實(shí)例分析一下:
AIDL demo 簡(jiǎn)單的 UML
根據(jù) uml 寫(xiě)代碼
我們寫(xiě)一個(gè)簡(jiǎn)單的通過(guò) Client 進(jìn)程調(diào)用 Server 進(jìn)程返回一個(gè)字符串功能,為了方便起見(jiàn),我們直接在一個(gè)項(xiàng)目中創(chuàng)建「Server 開(kāi)啟在另一個(gè)進(jìn)程中,開(kāi)兩個(gè) APP 進(jìn)行通信大家可以自行試一下,道理一模一樣的」
1、在項(xiàng)目中新建一個(gè) AIDL 文件「在 AS 中的 APP上直接右鍵 new AIDL 即可」
interface CustomAIDL { String getStr() ; }
此時(shí)我們點(diǎn)擊一下圖標(biāo)構(gòu)造一下項(xiàng)目,此時(shí)會(huì)在 appuildgeneratedsourceaidldebug包名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ù)
我這里給服務(wù)定義了一個(gè) process ,那說(shuō)明這個(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 也顯示出來(lái)了,看一下結(jié)果
怎么樣,兩個(gè)進(jìn)程之間完美的進(jìn)行了通信了
通個(gè)毛呢?這和 proxy 有個(gè)啥關(guān)系呀「巴拉巴拉這么久」,不要急嗎?軟件開(kāi)發(fā)有一條宗旨:先讓它運(yùn)行起來(lái)「我們先把 Demo 運(yùn)行起來(lái)再說(shuō)嗎:咳咳又到了吃藥的時(shí)間了」,我們來(lái)分析一下上面的調(diào)用過(guò)程
過(guò)程分析
1、還記得我們上面說(shuō)的 AD 幫我們自動(dòng)生成的 CustomAIDL.java 文件嗎,我們來(lái)一窺它的真容「以下代碼是格式化后的」
// 這里的 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 { // 給客戶端寫(xiě)數(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ì)象的仿問(wèn) 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ù)端寫(xiě)過(guò)來(lái)的數(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)呢」,我們來(lái)分析一下
上面的圖就是一個(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ì)過(guò)程比這個(gè)復(fù)雜」,對(duì)于 Binder 的入門(mén)介紹可以參看:Binder 學(xué)習(xí)指南 還是非常不錯(cuò)的,建議看三遍以上
PS:這里再說(shuō)一點(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)程
通過(guò)以上配置,我們可以看到 AIDLRemoteService 是運(yùn)行在多帶帶進(jìn)程中的,我們?cè)?CustomAIDL.java 中的 asInterface 方法中 debug 跟一下看看結(jié)果
通過(guò)圖我們可以看出,如果 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)程了
同理 debug 看結(jié)果
對(duì)比上面的圖我們就知道了,這里的 iin 不為空,進(jìn)入了 if 的方法體「沒(méi)有調(diào)用代理」,至此上面的結(jié)果驗(yàn)證完畢
關(guān)于 AIDL 遠(yuǎn)程代理就說(shuō)到這里了,如果對(duì) Binder 想要深入了解,可以自行回去研究「這不在本節(jié)的范圍內(nèi)」
WTF 一個(gè) AIDL 說(shuō)了這么大半天,希望大家不要暈「我都有點(diǎn)暈了」
源碼地址: https://github.com/githubchen001/DesignPattern 看 proxy/aidl 這部分
三、Android 源碼中的代理模式其實(shí)通過(guò)上面的 AIDL 實(shí)驗(yàn),我們就可以知道 Binder 使用的就是遠(yuǎn)程代理模式,Android 中的源碼使用非常多,我就不一一分析了「說(shuō)的太多人會(huì)受不鳥(niǎo)」,感興趣的朋友可以自行分析,我這里貼出一張圖,大家可以看
我們看看應(yīng)用程序框架層的 XXXManager 對(duì)應(yīng)田系統(tǒng)層的 XXXService 它們之間通過(guò)使用 AIDL 來(lái)進(jìn)行跨進(jìn)程通信,有興趣可以扒扒這部分的源碼看一下
四、代理模式的優(yōu)缺點(diǎn)優(yōu)點(diǎn)
1、代理模式拿到的真實(shí)對(duì)象的引用,把真實(shí)對(duì)象很好的保護(hù)起來(lái)安全性高
2、擴(kuò)展性好
缺點(diǎn)
增加了系統(tǒng)的復(fù)雜度,增加了額外好多的代碼「設(shè)計(jì)模式好像都是這樣」
到此為止,我們把代理模式就說(shuō)完了,由于這篇篇幅比較大,Android 源碼也沒(méi)有給大家分析「希望大家自行去看看,希望你有一種哦~原來(lái)是這樣的趕腳」,其它的虛擬代理,緩存代理大家有興趣也可以試試
參考資料小米開(kāi)放平臺(tái):徹底理解ANDROID BINDER通信架構(gòu)(上)
Binder學(xué)習(xí)指南 建議看三遍以上,非?;A(chǔ)的一步步介紹 Binder
以后文章會(huì)第一時(shí)間發(fā)在公號(hào),請(qǐng)大家添加博文的公號(hào),掃描添加即可關(guān)注
公眾號(hào):TigerChain
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/70717.html
摘要:不同的代碼運(yùn)行環(huán)境賦值的結(jié)果不同。當(dāng)訪問(wèn)的屬性不是類(lèi)型或者屬性值在被代理的對(duì)象上不存在,則拋出錯(cuò)誤提示,否則就返回該屬性值。該方法可以在開(kāi)發(fā)者錯(cuò)誤的調(diào)用屬性時(shí),提供提示作用。只不過(guò)目前規(guī)范還沒(méi)有很完善,使用的時(shí)候要稍加注意。 前幾篇文章中,我們主要講了merge options的一些操作。今天我們回到init方法往下講。 if (process.env.NODE_ENV !== pro...
摘要:介于目前項(xiàng)目的前端開(kāi)發(fā)基于人人企業(yè)版有了快狗團(tuán)隊(duì)的手摸手,很快就能用部署這樣一個(gè)后臺(tái)管理平臺(tái)。構(gòu)建鏡像,部署靜態(tài)資源這里借助獲取鏡像,通鏡像作為基礎(chǔ)來(lái)構(gòu)建人人企業(yè)版鏡像。本許可協(xié)議授權(quán)之外的使用權(quán)限可以從處獲得。 Created by huqi at 2019-5-24 21:01:30 Updated by huqi at 2019-5-26 00:00:42 前言 最近后端的小...
摘要:介于目前項(xiàng)目的前端開(kāi)發(fā)基于人人企業(yè)版有了快狗團(tuán)隊(duì)的手摸手,很快就能用部署這樣一個(gè)后臺(tái)管理平臺(tái)。構(gòu)建鏡像,部署靜態(tài)資源這里借助獲取鏡像,通鏡像作為基礎(chǔ)來(lái)構(gòu)建人人企業(yè)版鏡像。本許可協(xié)議授權(quán)之外的使用權(quán)限可以從處獲得。 Created by huqi at 2019-5-24 21:01:30 Updated by huqi at 2019-5-26 00:00:42 前言 最近后端的小...
摘要:道阻且長(zhǎng)啊前端面試總結(jié)前端面試筆試面試騰訊一面瀏覽器工作原理瀏覽器的主要組件包括用戶界面包括地址欄后退前進(jìn)按鈕書(shū)簽?zāi)夸洖g覽器引擎用來(lái)查詢及操作渲染引擎的接口渲染引擎渲染界面和是基于兩種渲染引擎構(gòu)建的,使用自主研發(fā)的渲染引擎,和都使用網(wǎng)絡(luò)用來(lái) 道阻且長(zhǎng)啊TAT(前端面試總結(jié)) 前端 面試 筆試 面試 騰訊一面 1.瀏覽器工作原理 瀏覽器的主要組件包括: 用戶界面- 包括地址欄、后退/前...
閱讀 2909·2021-11-25 09:43
閱讀 1105·2021-10-11 10:57
閱讀 2558·2020-12-03 17:20
閱讀 3816·2019-08-30 14:05
閱讀 2508·2019-08-29 14:00
閱讀 2045·2019-08-29 12:37
閱讀 1740·2019-08-26 11:34
閱讀 3282·2019-08-26 10:27