代理模式

431 阅读7分钟

小伙伴们,代理模式在Android中应用非常的广泛,它的作用非常的重要。 

同时要想成为高级或者资深工程师,这个技能也是必须要掌握的。 下面让我们一起来学习他吧。 

首先我们看一下代理的分类。

代理的分类

静态代理。 动态代理。

 那么静态代理和动态代理之间有什么区别呢?

静态代理VS动态代理

下面将详细的讲解一下静态代理和动态代理。

静态代理

静态代理的UML类图(Z行结构)

通过UML类图,我们可以很清晰,很简洁的知道静态代码模板的结构。

静态代码的模板代码

第一步、定义被代理接口。

内部是一个简单的方法。

package com.example.createproject;

/**
 * 被代理接口
 */
public interface Subject {
    void visit();
}

第二步、定义具体被代理类 。

实现接口,实现了具体的方法。 

package com.example.createproject;

import android.util.Log;

import com.example.createproject.constant.TAGConstant;

/**
 * 具体被代理类
 */
public class RealSubject implements Subject{
    private static final String TAG = RealSubject.class.getSimpleName();

    @Override
    void visit() {
        Log.e(TAGConstant.proxy,"实际被代理类执行");
    }
}

第三步、代理类

代理类内部封装了关于被代理对象的引用,然后调用方法的时候,回去调用具体的被代理对象的方法。

package com.example.createproject;

import android.util.Log;

import com.example.createproject.constant.TAGConstant;

/**
 * 代理类
 */
public class ProxySubject implemens Subject {

    Subject mSubject;

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

    public void visit() {
        Log.e(TAGConstant.proxy, "代理模式执行start");
        if (mSubject != null) {
            mSubject.visit();
        }
        Log.e(TAGConstant.proxy, "代理模式执行end");
    }
}

第四步、静态代理访问实战

package com.example.createproject

import android.os.Bundle
import android.support.v7.app.AppCompatActivity

class Test6Activity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test6)

        val subject = RealSubject()
        val proxySubject = ProxySubject(subject)
        proxySubject.visit()
    }
}

思考:通过上面的静态代理模板,静态代理模式有什么作用? 

通过上面的例子,我们可以看到,代理类可以对被代理类进行加工处理,比如在代理类中我们在被代理对象执行方法的前后都增加了日志处理。 实际的过程中,我们可能在前后增加逻辑的处理。 

思考:代理模式和装饰模式相似吗?

代理模式和装饰模式都是比较相似的,都是对原有的功能进行增强。 只不过代理模式要求代理类需要实现接口,而装饰模式确没有这个限制。

下面我们学习一下动态代理。

动态代理

动态代理的UML类图

动态代理模板

第一步:定义被代理接口

package com.example.createproject;

/**
 * 抽象被代理接口
 */
public interface Subject {
    void visit();
}

第二步:接口实现类

package com.example.createproject;

import android.support.annotation.NonNull;
import android.util.Log;

import com.example.createproject.constant.TAGConstant;

/**
 * 具体被代理类
 */
public class RealSubject implements Subject{

    @Override
    public void visit() {
        Log.e(TAGConstant.proxy,"实际被代理类执行");
    }

    @NonNull
    @Override
    public String toString() {
        return "1111";
    }
}

 

第三步:动态代理帮助类

package com.example.createproject;

import android.util.Log;

import com.example.createproject.constant.TAGConstant;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理帮助类
 */
public class DynamicProxy implements InvocationHandler {

    /**
     * 被代理对象
     */
    Object mTarget;

    public DynamicProxy(Object mObject) {
        this.mTarget = mObject;
    }

    /**
     * 获得动态代理对象
     * @return
     */
    public Object getProxy() {
        // 通过字节码
        return Proxy.newProxyInstance(mTarget.getClass().getClassLoader(), mTarget.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.e(TAGConstant.proxy, "调用方法 method.getName() 前处理");
        method.invoke(mTarget, args);
        Log.e(TAGConstant.proxy, "调用方法 method.getName() 后处理");
        return null;
    }
}

第四步:动态代理执行实战

package com.example.createproject;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

import java.lang.reflect.Proxy;

public class Test7Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test7);

        // 优势:我们可以看到,通过代理对象,我们只是包装了一下(DynamicProxy handler = new DynamicProxy(realSubject);),和 我们构造出一个动态代理对象,
        // 然后我们就可以按照被代理对象调用方法的方式调用,但是内部我们做了对于被代理方法的包装和升级。

        // 被代理对象
        Subject realSubject = new RealSubject();
        // 动态代理对象帮助类
        DynamicProxy handler = new DynamicProxy(realSubject);
        // 构造动态代理对象
        Subject proxy = (Subject) handler.getProxy();
        // 动态代理执行方法:对原有的方法可以进行加工升级
        proxy.visit();
    }
}

代理在Android中的应用场景

静态代理在Android中的应用场景

我们知道Android中的通知在各个版本均有不同的API,那么我们该如何利用静态代理去封装通知的逻辑呢?

第一步:定义通知抽象类

package com.example.createproject;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;

/**
 * 通知抽象类
 */
public abstract class Notify {

    protected Context context;
    protected NotificationManager notificationManager;
    protected NotificationCompat.Builder builder;

    public Notify(Context context) {
        this.context = context;

        notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        builder = new NotificationCompat.Builder(context);
        builder.setSmallIcon(R.drawable.ic_launcher_background)
                .setContentIntent(PendingIntent.getActivity(context,0,
                        new Intent(context,Test1Activity.class),PendingIntent.FLAG_CANCEL_CURRENT));
    }

    /**
     * 发送一条通知
     */
    public abstract void send();

    /**
     * 取消一条通知
     */
    public abstract void cancel();
}

第二步:定义在不同API下,通知的实现类。

package com.example.createproject;

import android.content.Context;
import android.util.Log;

public class NotificationBig extends Notify{
    private static final String TAG = "NotificationBig";

    public NotificationBig(Context context) {
        super(context);
    }

    @Override
    public void send() {
        Log.e(TAG,"大通知send");
    }

    @Override
    public void cancel() {
        Log.e(TAG,"大通知cancel");
    }
}

package com.example.createproject;

import android.content.Context;
import android.util.Log;

public class NotificationNormal extends Notify{
    private static final String TAG = "NotificationNormal";

    public NotificationNormal(Context context) {
        super(context);
    }

    @Override
    public void send() {
        Log.e(TAG,"正常通知send");
    }

    @Override
    public void cancel() {
        Log.e(TAG,"正常通知cancel");
    }
}

第三步:通知的代理类。

package com.example.createproject;

import android.content.Context;
import android.os.Build;
import android.util.Log;

public class NotificationProxy extends Notify{

    Notify notify = null;

    public NotificationProxy(Context context) {
        super(context);
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP){
            notify = new NotificationBig(context);
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.CUPCAKE){
            notify = new NotificationNormal(context);
        } else {
            // 省略逻辑
        }
    }

    @Override
    public void send() {
        notify.send();
    }

    @Override
    public void cancel() {
        notify.cancel();
    }
}

思考:

 1、上面关于静态代理模式的使用,也是面向对象中封装的一个使用,将多个通知的显示逻辑封装到了NotificationProxy类中,也是一种多态的体现,父类的引用指向子类的对象。 也是一种继承的体现。 

2、上面关于静态代理模式的使用,我们也可以看成是外观设计模式的体现。 外观设计模式:对于一个复杂的系统,它下面有很多个子系统,但是对于用户来说,只有暴漏出来一个接口可以供用户使用。这个例子种,NotificationProxy相当于复杂系统的统一入口。 

3、上面代理模式的使用,我们也可以看出程序设计里面非常重要的一个思想:那么就是面向接口编程。 

4、代理模式的使用,和策略模式也是有一些相似的,首先都是定义抽象父类,然后有具体的子类来实现,但是策略模式是动态的注入策略,而代理模式是内部封装了策略的切换。 

动态代理在Android中的应用场景

首先我们看下,在项目中如果要使用OKHttp去进行网络请求,代码如下:

public class ApiStore {
	OkHttpClient client = new OkHttpClient();
//员工登录接口 
String login(String json) {
	RequestBody body = RequestBody.create(JSON,json);
	Request reques = new Request.Build()
	.url(“/resource/d/member/login”)
	.post(body)
	.build()
	
	return response.body().string()
}

// 退出登录
String logout(String json) {
RequestBody body = RequestBody.create(JSON,json);
	Request reques = new Request.Build()
	.url(“/resource/d/member/signOut”)
	.post(body)
	.build()
	
	return response.body().string()

}
}

思考:代码中有很多重复性的代码,比如构造Request,以及发送请求,返回结果的操作。作为一个网络库来说,如何让调用者更方便的去使用它,如何使用更少的代码,而实现相同的功能?

 Retrofi解决了这个问题,它内部采用了动态代理技术,让用户更加方便的请求网络,而无需关系太多网络框架的API,下面让我们来看看Retrofit的巧妙之处。 

比如我们定义了网络接口如下

public interface ApiStore {

    // 员工登录
    @POST("/resource/d/member/login")
    Observable<BaseResponse<LoginResult>> login(@FieldMap Map<String, String> params);

    // 退出登录
    @POST("/resource/d/member/signOut")
    Observable<BaseResponse<LogOutResult>> logout(@FieldMap Map<String, String> params);
    
}

创建一个Retrofit

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://ww.xxx.com/")
.build();
ApiStore service = retrofit.create(ApiStore.class);

调用者每次调用的时候,就可以采用service.login()方法进行网络请求了,非常的方便。

而 retrofit.create(ApiStore.class);这句代码其实就是创建一个代理类ProxyX,这个生成的代理类的类声明是这样的:

public $Proxy3 extends Proxy implements ApiStore
可以看出create内部具体的实现方式:
public <T> T create(final Class<T> service) {
    //...
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

Retrofit就是通过Proxy.newProxyInstance的方式创建出来一个代理类,invoke中的代码就是当网络接口被调用的时候需要做的处理。在这个方法里面,首先根据接口的定义,生成一个ServiceMethod对象,在ServiceMethod对象中回反射接口中定义的注解,解析出具体的网络请求方式,然后拿到封装号的ServiceMethod对象后,然后构造出一个OkHttpCall对象,然后正真的进行网络OkHttp的请求。 动态生成代码。 

上面代码的优点:非常的简洁,将一些包装ServiceMethod和OkHttpCall的逻辑封装了起来。