美文网首页Android技术知识Android开发Android开发经验谈
Android源码设计模式(六)— 编程好帮手:代理模式

Android源码设计模式(六)— 编程好帮手:代理模式

作者: 随时学丫 | 来源:发表于2018-07-12 14:27 被阅读150次

    Android源码设计模式(一) -- 面向对象的六大原则
    Android源码设计模式(二)-- 应用最广的模式:单例模式
    Android源码设计模式(三)-- 自由扩展你的项目:Builder 模式
    Android源码设计模式(四)-- 时势造英雄:策略模式
    Android源码设计模式(五)-- 使编程更有灵活性:责任链模式
    Android源码设计模式(六)— 编程好帮手:代理模式
    Android源码设计模式(七)— 解决、解耦的钥匙 — 观察者模式

    简书 MD 语法不识别 [TOC] ,也不会根据标题行(#) 来插入目录,作为每次看资料喜欢先看目录把握总体的我来说,很不习惯,查找跳转也不方便,所以,如果看到文章没有目录预览的,请安装脚本:简书目录脚本地址

    一、代理模式的定义

    对其他对象提供一种代理以控制对这个对象的访问。

    二、代理模式的使用场景

    当无法或不想直接访问某个对象或访问某个对象存在困难时,可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

    三、代理模式的 UML 类图】

    代理模式 UML

    角色介绍

    • AbstractObkect:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
    • RealObject:该类也可以被称为委托类或代理类,定义了代理对象所代表的目标对象,由其执行具体的业务逻辑方法,而客户端类或代理类间接调用 RealObject 中定义的方法。
    • ProxyObject:该类被称为代理类或委托类。该类持有一个 RealObject 的引用,在其所实现的接口方法中调用 RealObject 的方法执行,以此起到代理的作用。

    四、代理模式的简单实现

    借助一个生活中案例来理解代理模式。小民以前在公司上班时,就遇到被老板拖欠工资甚至克扣工资的情况,这种情况下小民还是通过法律途径来解决问题,一旦小民选择了走法律途径,那么不可避免地要请律师作为自己的诉讼代理人,我们将诉讼流程抽象在一个接口类中。

    public interface ILawsuit {
        //提交申请
        void submit();
        //进行举证
        void burden();
        //开始辩护
        void defend();
        //诉讼完成
        void finish();
    }
    

    具体诉讼人:小民

    该类实现 ILawsuit 的 4 个方法做出具体的实现逻辑,虽然逻辑很简单,只是输出了一句话。当然,小民不可以自己去打官司,于是小民请了个律师代理自己进行诉讼。

    public class XiaoMin implements ILawsuit {
        @Override
        public void submit() {
            //老板欠小民工资,小民只好申请仲裁
            System.out.println("老板拖欠工资,特此申请仲裁!");
        }
        @Override
        public void burden() {
            //小民证据充足,不怕告不赢
            System.out.println("这是合同书和过去一年的银行工资流水!");
        }
        @Override
        public void defend() {
            //铁证如山,辩证也没什么好说的
            System.out.println("证据确凿!不需要再说什么了!");
        }
        @Override
        public void finish() {
            //结果也是肯定的,必赢
            System.out.println("诉讼成功,判决老板即日起七天内结算工资!");
        }
    }
    

    诉讼代理人:律师

    在该类中会持有一个呗代理者的引用,律师所执行的方法实质就是简单的调用被代理者中的方法。

    public class Lawyer implements ILawsuit {
        ILawsuit lawsuit;//持有一个具体被代理的引用
        public Lawyer(ILawsuit lawsuit) {
            this.lawsuit = lawsuit;
        }
        @Override
        public void submit() {
            lawsuit.submit();
        }
        @Override
        public void burden() {
            lawsuit.burden();
        }
        @Override
        public void defend() {
            lawsuit.defend();
        }
        @Override
        public void finish() {
            lawsuit.finish();
        }
    }
    

    客户类

    public class Client {
        public static void main(String[] args) {
            ILawsuit xiaoMin = new XiaoMin();
            ILawsuit lawyer = new Lawyer(xiaoMin);
            lawyer.submit();
            lawyer.burden();
            lawyer.burden();
            lawyer.finish();
        }
    }
    //---------------------------------运行结果--------------------------------------------------
    老板拖欠工资,特此申请仲裁!
    这是合同书和过去一年的银行工资流水!
    这是合同书和过去一年的银行工资流水!
    诉讼成功,判决老板即日起七天内结算工资!
    

    代理模式其实就是一种委托模式,真实对象将方法的执行委托给代理对象,其实大家还可以发散思维。

    正如一个代理类可以代理多个被代理类,即:一个律师可以代理很多个人打官司。

    被代理类也可以有多个代理类,即:小民还可以找不同的律师打官司。

    而我们只需要定义不同的代理类和被代理类实现 ILawsuit 接口即可。

    正如文开头所述,代理模式是一个非常重要的模式,所以本文会讲得多一些,希望读者耐心细读。代理模式大致可以分为两部分。

    • 静态代理:如上示例即是一个静态代理,代理者的代码由程序员或通过一些自动化工具生成固定的代码再对其进行编译,也就是说在我们代码运行前代理类 Class 编译文件就存在。
    • 动态代理:与静态代理相反,通过反射机制动态生成代理者对象,也就是说我们在 code 阶段压根不需要知道代理谁,代理谁我们将在执行阶段决定。

    而 Java 给我们提供了一个便捷的动态代理接口 InvocationHandler,实现该接口需要重写其调用方法 invoke。

    package java.lang.reflect;
    public interface InvocationHandler {
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
    }
    

    在这里,我们主要通过 invoke 方法来调用具体的被代理方法,也就是真实的方法。动态代理可以使得我们的代码逻辑更加简洁,不过在此之前我们首先完善动态代理类。

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class DynamicProxy implements InvocationHandler {
        private Object obj;
    
        public DynamicProxy(Object obj) {
            this.obj = obj;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
             //proxy:代表被代理的类
             //method:表示代理类中的方法
            Object result = method.invoke(obj, args);
            return result;
        }
    
    }
    

    如上代码,我们声明一个 Object 的引用,该引用指向被代理者,而我们调用被代理者的具体方法则在 invoke 方法中,也就是说我们原来由代理类做的工作现在都由 谁我们 InvocationHandler 来完成,不需要关心到底代理谁。

    public class Client {
        public static void main(String[] args) {
            //构造小民
            ILawsuit xiaoMin = new XiaoMin();
            //构造动态代理
            DynamicProxy proxy = new DynamicProxy(xiaoMin);
            //获取被代理小民的 ClassLoader
            ClassLoader classLoader = xiaoMin.getClass().getClassLoader();
            //动态构造一个代理律师
            ILawsuit lawyer = (ILawsuit) Proxy.newProxyInstance(classLoader, new Class[] { ILawsuit.class }, proxy);
            //律师诉讼
            lawyer.submit();
            //律师举证
            lawyer.burden();
            //律师辩护
            lawyer.burden();
            //完成诉讼
            lawyer.finish();
        }
    }
    //---------------------------------运行结果--------------------------------------------------
    老板拖欠工资,特此申请仲裁!
    这是合同书和过去一年的银行工资流水!
    这是合同书和过去一年的银行工资流水!
    诉讼成功,判决老板即日起七天内结算工资!
    

    由此可见动态代理通过一个代理类来代理 N 多个被代理类,其实质是对代理者与被代理者进行解耦,使两者没有直接的耦合关系。

    相对于静态代理则只为给定接口下的实现做代理,而接口不同那么久需要重新定义不同的代理类,较为复杂,但是静态代理更符合面向对象原则。在开始时具体使用哪种代理,根据情况。

    静态代理和动态代理是从 code 方面来区分代理模式的两种方式,我们也可以通过试用范围来区分不同的代理实现。

    • 远程代理(Remote Proxy):为某个对象在不同的内存地址空间提供局部代理,使系统可以将 Server 部分实现隐藏,以便 Client 可以不必考虑 Server 的存在。
    • 虚拟代理(Virtual Proxy):使用一个代理对象表示一个十分耗费资源的对象并在真正需要时才创建。
    • 保护代理(Protection Proxy):使用代理控制对原始对象的访问,该类型的代理常被用于原始对象有不同访问权限的情况。
    • 智能引用(Smart Reference):在访问原始对象时执行一些自己的附加操作并对指向原始对象的引用计数。

    五、Android 中代理模式实现

    Android 源码中不少关于代理模式的实现,比如源码中的 ActivityManagerProxy 代理类,其具体代理的是 ActivityManagerNative 的子类 ActivityManagerService,我们主要看看 ActivityManagerProxy 这个类,该类与 ActivityManagerNative 这个类处于同一文件。

    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);
            data.writeStrongBinder(caller != null ? caller.asBinder() : null);
            data.writeString(callingPackage);
            intent.writeToParcel(data, 0);
            data.writeString(resolvedType);
            data.writeStrongBinder(resultTo);
            data.writeString(resultWho);
            data.writeInt(requestCode);
            data.writeInt(startFlags);
            if (profilerInfo != null) {
                data.writeInt(1);
                profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            } else {
                data.writeInt(0);
            }
            if (options != null) {
                data.writeInt(1);
                options.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
            reply.readException();
            int result = reply.readInt();
            reply.recycle();
            data.recycle();
            return result;
        }
        //.............实现了 IActivityManager 中的抽象方法
    }
    

    ActivityManagerProxy 实现了 IActivityManager 中的抽象方法,该接口定义了 Activity 相关的接口方法,其中有些是我们常接触到的。

    public interface IActivityManager extends IInterface {
        public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
                String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flags,
                    ProfilerInfo profilerInfo, Bundle options) throws RemoteException;
        
        public ComponentName startService(IApplicationThread caller, Intent service,
                    String resolvedType, String callingPackage, int userId) throws RemoteException;
        
        public int stopService(IApplicationThread caller, Intent service,
                    String resolvedType, int userId) throws RemoteException;
        
        public int bindService(IApplicationThread caller, IBinder token, Intent service,
                    String resolvedType, IServiceConnection connection, int flags,
                    String callingPackage, int userId) throws RemoteException;
        
        public boolean unbindService(IServiceConnection connection) throws RemoteException;
        
        public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                    IIntentReceiver receiver, IntentFilter filter,
                    String requiredPermission, int userId) throws RemoteException;
        
        public void unregisterReceiver(IIntentReceiver receiver) throws RemoteException;
        
        public int checkPermission(String permission, int pid, int uid)
                    throws RemoteException;
        //........
    }
    

    上面的 IActivityManager 就相当于代理模式中的抽象主题,那么真实的主题是谁呢?就是上面我们提到过的继承于 ActivityManagerNative 的 ActivityManagerService 类。


    AMS.png

    通过 UML 图可以清晰的看到 ActivityManagerProxy 和 ActivityManagerNative 都实现了 IActivityManager,严格来说,ActivityManagerProxy 就是代理部分,而真实部分实质上是 ActivityManagerService,因为 ActivityManagerNative 是个抽象类,大多数逻辑都在 ActivityManagerService 中。

    ActivityManagerService 是系统级的 Service 并运行于自己所处的进程空间中,可以通过 ServiceManager 来获取它。而 ActivityManagerProxy 也运行在自己的进程空间,两者并不相同,显然 ActivityManagerProxy 和 ActivityManagerService 通信是通过跨进程通信,上述类中也可以看到,这里跨进程的实现是基于 Android 中的Binder 机制,而上述的代理类型为 远程代理。

    ActivityManagerProxy 在实际的逻辑处理中并未过多地被外部类使用,因为在 Android 管理与维护 Activity 相关的类是另外一个叫做 ActivityManager 的类,ActivityManager 虽说管理 Activity 信息,但实质上其大多数逻辑都是 ActivityManagerProxy 承担。这里以 ActivityManager 中的 getAppTasks 方法为例。

    public List<ActivityManager.AppTask> getAppTasks() {
        ArrayList<AppTask> tasks = new ArrayList<AppTask>();
        List<IAppTask> appTasks;
        try {
            appTasks = ActivityManagerNative.getDefault().getAppTasks(mContext.getPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        int numAppTasks = appTasks.size();
        for (int i = 0; i < numAppTasks; i++) {
            tasks.add(new AppTask(appTasks.get(i)));
        }
        return tasks;
    }
    

    逻辑就是简单的调用 ActivityManagerNative 的 getDefault() 方法获取 IActivityManager 类型对象,然后通过这个对象调用其 getAppTasks() 方法。

    static public IActivityManager getDefault() {
        return gDefault.get();
    }
    
    private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
            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;
            }
    };
    

    上述代码构造了一个 Singleton<IActivityManager> 的 gDefault 对象,其中通过ServiceManager.getService("activity") 获取了系统级 Service。Service 实质上就是 ActivityManagerService,这里也就创建了一个对 ActivityManagerService 的 Client 代理对象 ActivityManagerProxy 的实例,ActivityManagerProxy 中的 getAppTasks 方法逻辑就很明确,将数据打包跨进程传递给 Server 端的 ActivityManagerService 处理并返回结果。

    public List<IAppTask> getAppTasks(String callingPackage) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken(IActivityManager.descriptor);
            data.writeString(callingPackage);
            mRemote.transact(GET_APP_TASKS_TRANSACTION, data, reply, 0);
            reply.readException();
            ArrayList<IAppTask> list = null;
            int N = reply.readInt();
            if (N >= 0) {
                list = new ArrayList<>();
                while (N > 0) {
                    IAppTask task = IAppTask.Stub.asInterface(reply.readStrongBinder());
                    list.add(task);
                    N--;
                }
            }
            data.recycle();
            reply.recycle();
            return list;
    }
    

    主要是通过 Binder 机制获取信息,通过 mRemote.transact() 方法获取 Client 端的数据,然后 AIDL 获取到 IAppTask 实例,将数据添加到集合中,并通过 reply 发送出去。

    Android 中的 Binder 机制

    想要仔细学习 Binder 机制的,推荐一篇博客,讲述了跨进程的一系列,是迄今为止,我看到解析最详细最易懂的 Binder 解析,讲完了跨进程通信的来龙去脉。

    Android跨进程通信:图文详解 Binder机制 原理

    六、总结

    优点

    • 给对象增加了本地化的扩展性,增加了存取操作控制

    缺点

    • 会产生多余的代理类

    相关文章

      网友评论

      • 囚怀:问一下,问什么要用到代理模式
        随时学丫:@囚怀 Retrofit 就是用 OkHttp3 动态代理的方式去发起网络请求,Retrofit 本身只是封装了繁琐的请求流程,封装解析结果实体,将网络请求简单化,底层还是通过 OkHttp 来发起的网络,而 OkHttp 底层其实是通过 Socket 建立 TCP 连接。
        囚怀:@蒋志碧 就是没遇到过,感觉还是不太理解
        随时学丫:当无法或不想直接访问某个对象或访问某个对象存在困难时,可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现相同的接口。

        你读文章的时候看下上面的使用场景呢:kissing_heart:

      本文标题:Android源码设计模式(六)— 编程好帮手:代理模式

      本文链接:https://www.haomeiwen.com/subject/gwxrpftx.html