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