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