Android中的静态代理和动态代理

456 阅读4分钟

前言

前面几篇我们讲了aidl和binder的基本使用方法,aidl在帮我们创建类的过程中,使用了静态代理模式,
本篇我们就来具体说说Java中最基本的代理模式:静态代理和动态代理

代理模式

  • 代理模式也叫委托模式,是结构型设计模式中的一种。
    在现实生活中我们用到类似代理模式的场景有很多,比如代购、代理上网、打官司等。
  • 定义
    为其他对象提供一种代理以控制对这个对象的访问,我们称之为代理模式
  • 在代理模式中有如下角色。
    Subject:抽象主题类,声明真实主题与代理的共同接口方法。
    RealSubject:真实主题类,定义了代理所表示的集体对象,客户端通过代理类间接调用真实主题类的方法 Proxy:代理类,持有对真实主题类的引用,在其所实现的接口方法中调用真实主题类中相应的接口方法执行。
    Client:客户端类。

代理模式

准备

我们先创建一个抽象主题类IBuyFood和真实主题类XiaoMing

public interface IBuyFood {

    public void buyBanana();

}
public class XiaoMing implements IBuyFood {
    public static String TAG = "XiaoMing=======";

    @Override
    public void buyBanana() {
        Log.d(TAG, "买香蕉");
    }
}

静态代理

我们创建一个实现了IBuyFood接口的代理类,其内部持有IBuyFood的真实实例

public class BuyFoodProxy implements IBuyFood {
    public static String TAG = "Proxy=======";
    IBuyFood iBuyFood;

    public BuyFoodProxy(IBuyFood iBuyFood) {
        this.iBuyFood = iBuyFood;
    }

    @Override
    public void buyBanana() {
        Log.d(TAG, "准备买香蕉....");
        iBuyFood.buyBanana();
        Log.d(TAG, "香蕉买完了....");
    }
}

接下来具体看下我们的代码是如何调用的:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_proxy_static).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                IBuyFood iBuyFood = new XiaoMing();
                BuyFoodProxy buyFoodProxy = new BuyFoodProxy(iBuyFood);
                buyFoodProxy.buyBanana();
            }
        });
    }
}

日志信息:

2022-04-07 16:25:14.960 23163-23163/com.example.a05proxymodel D/Proxy=======: 准备买香蕉....
2022-04-07 16:25:14.960 23163-23163/com.example.a05proxymodel D/XiaoMing=======: 买香蕉
2022-04-07 16:25:14.960 23163-23163/com.example.a05proxymodel D/Proxy=======: 香蕉买完了....

通过日志信息我们可以发现,我们使用面向切面的思想,在不影响原来代码的基础上,分别在前面和后面加上了额外的操作,这其实也是代理模式的一种具体用途。

动态代理

上面我们所讲的静态代理,在代码运行前就已经存在了代理类的class文件;而动态代理则是在代码运行时通过发射来动态的生成代理类的对象,并确定到底来代理谁。
也就是说我们在编码阶段并不知道要代理谁,到底代理谁我们将在代码运行时决定。
Java提供了动态的代理接口InvocationHandler,我们需要实现该接口并重写invoke方法。
我们创建一个动态代理类DynamicBuy:

public class DynamicBuy implements InvocationHandler {
    public static String TAG = "DynamicBuy=======";
    Object object;

    public DynamicBuy(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("buyBanana")) {
            Log.d(TAG, "准备买香蕉....");
        }
        Object result = method.invoke(object, args);
        if (method.getName().equals("buyBanana")) {
            Log.d(TAG, "香蕉买完了....");
        }
        return result;
    }
}

在DynamicBuy中我们声明了一个Object的引用,该引用会指向被代理类,然后我们再间接调用被代理类的具体方法。
下面是具体调用实现:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_proxy_dynamic).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                IBuyFood iBuyFood = new XiaoMing();//其实我们并不需要知道XiaoMing这个实例类,他也可以是其他实现了IBuyFood接口的实例。
                DynamicBuy dynamicBuy = new DynamicBuy(iBuyFood);//创建动态代理
                ClassLoader classLoader = getClassLoader();//获取类加载器
                IBuyFood proxy = (IBuyFood) Proxy.newProxyInstance(classLoader, new Class[]{IBuyFood.class}, dynamicBuy);//创建动态代理类
                proxy.buyBanana();
            }
        });
    }
}

调用 Proxy.newProxyInstance来生成动态代理类,当我们调用proxy的buyBanana方法会调用dynamicBuy的invoke方法,然后我们再在里面调用真正的实现类。

关于静态代理,aidl已经帮我们演示过了他的具体用途;而关于动态代理模式,其作用也是非常大的,特别是当我们想要在app运行阶段Hook某些系统类以便进行消息拦截修改的时候, 动态代理会是一种很不错的方式。