Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> java/android 設計模式學習筆記(9)---代理模式

java/android 設計模式學習筆記(9)---代理模式

編輯:關於Android編程

  這篇博客我們來介紹一下代理模式(Proxy Pattern),代理模式也成為委托模式,是一個非常重要的設計模式,不少設計模式也都會有代理模式的影子。代理在我們日常生活中也很常見,比如上網時連接的代理服務器地址,更比如我們平時租房子,將找房子的過程代理給中介等等,都是代理模式在日常生活中的使用例子。
  代理模式中的代理對象能夠連接任何事物:一個網絡連接,一個占用很多內存的大對象,一個文件,或者是一些復制起來代價很高甚至根本不可能復制的一些資源。總之,代理是一個由客戶端調用去訪問幕後真正服務的包裝對象,使用代理可以很容易地轉發到真正的對象上,或者在此基礎上去提供額外的邏輯。代理模式中也可以提供額外的功能,比如在資源集中訪問操作時提供緩存服務,或者在操作真正執行到對象之前進行前提條件的檢查。對於客戶端來說,使用代理對象,和使用真正的對象其實是類似的,因為他們都實現了同樣的接口。
 

特點

  代理模式為另一個對象提供一個代理以控制對這個對象的訪問。使用代理模式創建代理,讓代理對象控制對某個對象的訪問,被代理的對象可以是遠程的對象,創建開銷很大的對象,或者是需要安全控制的對象,所以代理模式的使用場景為:當無法或者不想直接訪問某個對象或訪問某個對象存在困難時可以通過一個代理對象來間接訪問,為了保證客戶端的透明性,委托對象與代理對象需要實現同樣的接口。
  代理模式可以大致分為靜態代理和動態代理。靜態代理模式的代碼由程序員自己或通過一些自動化工具生成固定的代碼再對其進行編譯,也就是說我們的代碼在運行前代理類的 class 編譯文件就已經存在;而動態代理則與靜態代理相反,在 Java 或者 Android 中通過反射機制動態地生成代理者的對象,也就是說我們在 code 階段完全不需要知道代理誰,代理誰我們將會在執行階段決定,在 Java 中,也提供了相關的動態代理接口 InvocationHandler 類,這個我們在後面源碼的時候會用到。
  代理模式根據實際使用的場景也可以分為以下幾種:

遠程代理(Remote Proxy):為某個在不同的內存地址空間的對象提供局部代理,使系統可以將 Server 部分的實現隱藏,以便 Client 可以不必考慮 Server 的存在,類似於 C/S 模式(主要攔截並控制遠程方法的調用,做代理防火牆之類的);虛擬代理(Virtual Proxy):使用一個代理對象標識一個十分耗資源的對象,並在真正需要時才創建,實現一個延遲加載的機制;保護代理(Protection Proxy):是用代理控制對原始對象的訪問,該類型的代理通常被用於原始對象有不同訪問權限的情況;智能引用(Smart Proxy):在訪問原始對象時執行一些自己的附加操作並對指向原始對象的引用計數;寫時拷貝(克隆)代理(Copy-on-write Proxy):其實是虛擬代理的一個分支,提供了拷貝大對象的時候只有在對象真正變化後才會進行拷貝(克隆)的操作,即延遲拷貝。需要注意的一點是,靜態和動態代理都可以應用於上述的幾種情形,兩者是各自獨立變化的。

 

UML類圖

  我們來看看代理模式的 uml 類圖:
  這裡寫圖片描述
據此我們可以寫出代理模式的通用代碼:
Subject.class

public abstract class Subject {
    public abstract void operation();
}

RealSubject.class

public class RealSubject extends Subject{
    @Override
    public void operation() {
        //the real operation
    }
}

ProxySubject.class

public class ProxySubject extends Subject{

    private Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void operation() {
        if (realSubject != null) {
            realSubject.operation();
        } else {
            //do something else
        }
    }
}

客戶端 Client 代碼

ProxySubject subject = new ProxySubject(new RealSubject());
subject.operation();

代理模式的角色:

Subject:抽象主題類該類的主要職責是聲明真實主題與代理的共同接口方法,該類既可以是一個抽象類,也可以是一個接口;RealSubjct:真實主題類該類也稱為被委托類或被代理類,該類定義了代理所表示的真實對象,由其執行具體的業務邏輯方法,而客戶端則通過代理類間接地調用真實主題類中定義的方法;ProxySubject:代理類該類也稱為委托類或代理類,該類持有一個對真實主題類的引用,在其所實現的接口方法中調用真實主題類中對應的接口方法,以此起到代理的作用;Client:客戶類,即使用代理類的部分。

 

Android 源碼代理模式分析

  Android 源碼裡有不少關於代理模式的實現,最典型的比如源碼中的 ActivityManagerProxy 代理類,其具體代理的是 ActivityManagerNative 的子類 ActivityManagerService 類,對 AMS 感興趣的可以去網上查閱一下相關資料,這裡就不詳細介紹了。我們老規矩,來看看 uml 圖:
  這裡寫圖片描述
我在之前的一篇博客:android 不能在子線程中更新ui的討論和分析 中分析了 Activity 的創建過程,感興趣的可以去看看,在博客中分析到 startActivity 方法調用到了 Instrumentation 的 execStartActivity 方法, execStartActivity 方法又會調用 ActivityManagerNative.getDefault().startActivity 方法,最後調用到了 ActivityManagerService 中,但是當時並沒有很明白為什麼會調用到 AMS 中,所以這裡根據上面的 uml 圖補充說明一下。其實不光是 Instrumentation 類會調用到 ActivityManagerNative.getDefault() 方法,ActivityManager 中的很多方法都調用到了 ActivityManagerNative.getDefault(),比如 moveTaskToFront 方法:

public void moveTaskToFront(int taskId, int flags, Bundle options) {
    try {
        ActivityManagerNative.getDefault().moveTaskToFront(taskId, flags, options);
    } catch (RemoteException e) {
        // System dead, we will be dead too soon!
    }
}

和 getRunningServices 方法等等等:

public List getRunningServices(int maxNum)
        throws SecurityException {
    try {
        return ActivityManagerNative.getDefault()
                .getServices(maxNum, 0);
    } catch (RemoteException e) {
        // System dead, we will be dead too soon!
        return null;
    }
}

直接將函數調用轉給了 ActivityManagerNative.getDefault() 方法,看看 getDefault() 函數:

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
    /**
     * Cast a Binder object into an activity manager interface, generating
     * a proxy if needed.
     */
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }
...
    static public IActivityManager getDefault() {
        return gDefault.get();
    }
...
    private static final Singleton gDefault = new Singleton() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };
...
}

getDefault 函數通過一個 Singleton 對象對外提供, ServiceManager.getService(“activity”) 返回的是 ActivityManagerService 的 Binder 對象,我們在看看 asInterface 函數,如果 queryLocalInterface 方法返回的是 null ,那麼就會去創建一個 ActivityManagerProxy 對象,並且將 AMS 這個 Binder 對象作為參數傳遞給 ActivityManagerProxy 對象,來簡單看看 ActivityManagerProxy 的代碼:

class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }

    public IBinder asBinder()
    {
        return mRemote;
    }

    public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        ...
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        ...
    }
...
}

看了這裡的代碼之後就一目了然了,代理對象的函數比如 startActivity 方法最終調用到的還是 AMS 中,ActivityManagerProxy 的作用只是作為代理而已。調用到 AMS 的後續步驟可以回到博客: android 不能在子線程中更新ui的討論和分析 去看看,這裡就重復了。
  還有幾點需要在這裡特殊說明一下:

第一點:為什麼 IActivityManager 需要實現 IInterface 接口,或者為什麼 ActivityManagerProxy 需要繼承 IInterface 接口的行為我們看看 IInterface 接口的定義:

 

/**
 * Base class for Binder interfaces.  When defining a new interface,
 * you must derive it from IInterface.
 */
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}

有一個 asBinder 的方法,而 ActivityManagerProxy 的 asBinder 方法實現在上面已經貼出代碼了,所以說 ActivityManagerProxy 中的 asBinder 函數是用來返回 AMS 這個 Binder 對象的;

第二點,繼承 IBinder 和 Binder 的作用我們知道 AMS 是由 SystemServer 來啟動的,它是運行在另一個獨立進程中的(這點和 WindowManagerService 類似,對 WMS 感興趣的可以看看我的上篇博客: java/android 設計模式學習筆記(8)—橋接模式),而 ActivityManagerProxy 是運行於當前進程當中的,此時必須要用到跨進程的通信,繼承自 IInterface ,IBinder 接口和 Binder 類也是必然的(關於 IPC 通信,感興趣的可以去看看我的博客:android IPC通信(下)-AIDL)。到這裡,如果對 AIDL 熟悉的童鞋,就應該發現了其實這個 ActivityManagerProxy 和 ActivityManagerService 類就相當於我們寫了一個 aidl 文件之後,系統為我們自動生成文件中的 Proxy 類和 Stub 類,所以說 aidl 就是一個使用遠程代理模式非常實際的例子。

 

示例與源碼

  看了上面的通用代碼和 Android 源碼的 AMS 機制之後,代理模式應該已經很清楚了,但是上面用到的都是靜態代理,都是在編譯階段生成的 class 文件,所以這裡以一個動態+保護代理模式的 demo 為例:

動態+保護代理

  先看看它的 uml 類圖:
  這裡寫圖片描述vcHLIDxhIGhyZWY9"https://developer.android.com/reference/java/lang/reflect/InvocationHandler.html">InvocationHandler 這個類,這是 Java 為我們提供的一個便捷動態代理接口,作用就是用來在運行時動態生成代理對象,在這裡就不詳細介紹了,感興趣的可以去查閱相關資料。其次是保護代理,這裡就以 ProxyA 和 ProxyB 兩個代理類為例,一個只能調用 operationA 方法,另一個只能調用 operationB 方法。首先來看看 Subject 和 RealSubject 類:
Subject.class

public interface Subject {

    String operationA();

    String operationB();
}

RealSubjct.class

public class RealSubject implements Subject{
    @Override
    public String operationA() {
        return "this is operationA";
    }

    @Override
    public String operationB() {
        return "this is operationB";
    }
}

定義完了抽象主題類和真實主題類之後,開始構造代理類,首先是代理類的虛基類:
ISubjectProxy.class

public abstract class ISubjectProxy implements InvocationHandler {
    protected Subject subject;
    public ISubjectProxy(Subject subject) {
        this.subject = subject;
    }
}

然後是 ProxyA 和 ProxyB 這兩個代理子類:
ProxyA.class

public class ProxyA extends ISubjectProxy{
    public ProxyA(Subject subject) {
        super(subject);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("operationB")){
            throw new UnsupportedOperationException("ProxyA can't invoke operationB");
        }else if (method.getName().equals("operationA")) {
            return method.invoke(subject, args);
        }
        return null;
    }
}

ProxyB.class

public class ProxyB extends ISubjectProxy{
    public ProxyB(Subject subject) {
        super(subject);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("operationA")){
            throw new UnsupportedOperationException("ProxyB can't invoke operationA");
        } else if (method.getName().equals("operationB")) {
            return method.invoke(subject, args);
        }
        return null;
    }
}

最後是 Client 的代碼:

Subject subject = new RealSubject();
ISubjectProxy proxy = null;
switch (v.getId()) {
    case R.id.proxy_a:
        proxy = new ProxyA(subject);
        break;
    case R.id.proxy_b:
        proxy = new ProxyB(subject);
        break;
}
Subject sub = (Subject) Proxy.newProxyInstance(subject.getClass().getClassLoader(),
        subject.getClass().getInterfaces(), proxy);
try {
    Log.e("Shawn", sub.operationA());
}catch (UnsupportedOperationException e){
    Log.e("Shawn", e.getMessage());
}
try {
    Log.e("Shawn", sub.operationB());
}catch (UnsupportedOperationException e){
    Log.e("Shawn", e.getMessage());
}

結果:

com.android.proxypattern E/Shawn: this is operationA
com.android.proxypattern E/Shawn: ProxyA can't invoke operationB
com.android.proxypattern E/Shawn: ProxyB can't invoke operationA
com.android.proxypattern E/Shawn: this is operationB

代碼很簡單,第一點是使用 InvocationHandler 實現了動態代理,第二點是根據 method 的名字實現了保護代理。
  這裡只總結了保護代理,虛擬代理這裡簡單舉個例子: moduleA 為 modleB 的 lib,所以如果在 moduleA 中需要用到 moduleB 實現的對象,就可以使用代理模式,具體步驟是在 moduleA 中定義接口,moduleB 去實現該接口,moduleA 中定義一個代理對象並提供一些默認操作,當 moduleB 的相關模塊初始化之後,將該對象設置到 moduleA 的代理對象中以替代原來的默認實現,之後代理對象就能成功調用 moduleB 中定義的行為了,而且也實現了延遲加載。遠程代理和其他的代理模式大家去網上查閱一下相關資料,萬變不離其宗,道理是一樣的。

總結

  代理模式應用廣泛,超級廣泛,很多設計模式都會有代理模式的影子,有些模式單獨作為一種設計模式,倒不如說是對代理模式的一種針對性優化,而且代理模式的缺點也很少,總結一下代理模式的優缺點:

優點:代理作為調用著和真實對象的中間層,降低了模塊間和系統的耦合性;可以以一個小對象代理一個大對象,達到優化系統提高運行速度的目的;提供 RealSubject 的權限管理;容易擴展,RealSubject 和 ProxySubject 都接口化了,RealSubject更改業務後只要接口不變,ProxySubject 可以不做任何修改。缺點:調用者和真實對象多了一個中間層,對象的創建,函數調用使用的反射等都增加調用響應的時間;設計模式的通病:類的增加,不過這點也不嚴重。

 

適配器 VS 裝飾者 VS 橋接 VS 代理

  這四個都是結構型設計模式,他們有些類似,在實際使用過程中也容易搞混,我們在這就給他們做一個對比:

適配器模式

  適配器模式和其他三個設計模式一般不容易搞混,它的作用是將原來不兼容的兩個類融合在一起,uml 圖也和其他的差別很大。
  uml 類圖:
  這裡寫圖片描述

裝飾者模式

  裝飾者模式結構上類似於代理模式,但是和代理模式的目的是不一樣的,裝飾者是用來動態地給一個對象添加一些額外的職責,裝飾者模式為對象加上行為,而代理則是控制訪問。
  uml 類圖:
  這裡寫圖片描述

橋接模式

  橋接模式的目的是為了將抽象部分與實現部分分離,使他們都可以獨立地進行變化,所以說他們兩個部分是獨立的,沒有實現自同一個接口,這是橋接模式與代理模式,裝飾者模式的區別。
  uml 類圖:
  這裡寫圖片描述

代理模式

  代理模式為另一個對象提供代表,以便控制客戶對對象的訪問,管理的方式有很多種,比如遠程代理和虛擬代理等,這個在上面有,這裡就不說了,而裝飾者模式則是為了擴展對象。
  uml 類圖:
  這裡寫圖片描述

源碼下載

  https://github.com/zhaozepeng/Design-Patterns/tree/master/ProxyPattern

 

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved