代理模式
所谓代理模式,就是指「代理对象」(或者我们形象地称呼为中介),通过实现接口类具有了接口的能力,并通过和「实际的生产者」产生联系,将「实际生产者」的能力通过自己中转给「客户(调用者)」
客户不直接通过「实际生产者」获取信息,而是跟「代理对象(中介)」打交道获取信息
我们通过房屋租赁作为例子
静态代理
举个例子,我们有个 IPriceService.java
的接口,用于获取房屋的报价
1 2 3 4
| interface IPriceService{ int getPrice(); }
|
一般在代码里我们还会有一个类去实现这个 IPriceService.java
接口,例如下面的 APriceService.java
代表A房东的报价为 100元
1 2 3 4 5 6
| class APriceService implements IPriceService{ override int getPrice(){ return 100; } }
|
我们可能还有一个代理类,PriceServiceProxy,对 APriceService 进行代理,相当于一个「中介」,不让「租客」和「房东」接触,「中介」可以对数据(即价格)进行加工处理后再提供给「租客」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PriceServiceProxy implements IPriceService{
private IPriceService realPriceService; public PriceServiceProxy(IPriceService service){ realPriceService = service; } override int getPrice(){ int aPrice = realPriceService.getPrice(); return aPrice + 100; } }
|
这就是静态代理,从工程上来说,静态代理是添加一个「委托类」在不需要修改真正的实现类的基础上,做到对需求变动进行灵活修改的目的。
例如有一天需求变为「提供给租客的价格是房东的价格的 9折」,如果没有代理类,而是「租客」和「房东」直接打交道,那么我们需要修改所有实现了 IPriceService 接口的类中的 getPrice()
方法,有了代理类,我们只需要修改代理类中的 getPrice()
方法,在房东的报价的基础上 * 0.9 即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class PriceServiceProxy implements IPriceService{
private IPriceService realPriceService; public PriceServiceProxy(IPriceService service){ realPriceService = service; } override int getPrice(){ int aPrice = realPriceService.getPrice(); return (int)(aPrice * 0.9f); } }
|
测试方法:
1 2 3 4 5
| public static void main(String[] args) { IPriceService priceService = new PriceServiceProxy(new APriceService()); System.out.print("租客获取到的房子报价为"+priceService.getPrice()); }
|
上述的「静态代理」有如下几个特点:
- 「租客」只关心报价,而不关心是谁提供了报价;
即只关心 IPriceService 接口的返回,而不关心实现 IPriceService 接口的实例是谁
- 将「租客」和「房东」分离,降低了一定的耦合性
- 但也需要创建很多的代理类,使得代码量增加,一旦接口需要变动,代理类和实际目标类都需要进行修改
动态代理
动态代理就是将上述我们的静态代理中的 PriceServiceProxy 类通过代码动态生成,并通过该动态代理对目标对象(房东)进行进一步操作,达到代理的目的
我们可以使用 Java 中的 java.lang.reflect.Proxy#newProxyInstance
生成动态代理对象
其方法签名如下:
1 2 3
| public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
|
第一个参数传入需要代理的接口的 ClassLoader
第二个参数传入需要代理的接口类
第三个参数传入 InvocationHandler 对象,对需要代理的目标对象进行处理
eg.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| private val aPriceService by lazy { APriceService() }
val priceServiceProxy = Proxy.newProxyInstance( IPriceService::class.java.classLoader, arrayOf(IPriceService::class.java), object : InvocationHandler {
override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? { Log.d("ProxyDemoActivity","method is ${method?.name}") if (method?.name?:"" == "getPrice"){ return (method?.invoke(aPriceService,*(args?: arrayOfNulls(0))) as Int) + 100 } return method?.invoke(aPriceService,*(args?: arrayOfNulls(0))) }
} ) as IPriceService
findViewById<Button>(R.id.btnProxyDemo).setOnClickListener { Log.d("ProxyDemoActivity","price is "+priceServiceProxy.getPrice()) }
|
可见,我们通过代码 Proxy.newProxyInstance 生成了一个动态代理对象,对原本的 aPriceService 对象进行代理,从中做一些「偷梁换柱」的方法
那这到底有什么用呢,静态代理不也能做到吗,何必多此一举呢。
下面我们看个例子,假设我们有个需求,需要对 Android Framework 中的 startActivity() 方法做一些拦截处理
然而这个方法的代码是我们无法编辑的,也无法通过静态代理的方式生成一个 ActivityManagerProxy 对象进行代理,这时我们就可以考虑使用动态代理的方式,生成一个动态代理对象,对 activityManager 对象进行代理,在 startActivity
方法前后进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| try { Class<?> activityManagerNativeClass = null; Field defaultFiled = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { activityManagerNativeClass = Class.forName("android.app.ActivityManager"); defaultFiled = activityManagerNativeClass.getDeclaredField("IActivityManagerSingleton"); } else { activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative"); defaultFiled = activityManagerNativeClass.getDeclaredField("gDefault"); }
defaultFiled.setAccessible(true); Object defaultValue = defaultFiled.get(null); Class<?> SingletonClass = Class.forName("android.util.Singleton"); Field mInstance = SingletonClass.getDeclaredField("mInstance"); mInstance.setAccessible(true); Object iActivityManagerObject = mInstance.get(defaultValue);
Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
AmsInvocationHandler handler = new AmsInvocationHandler(iActivityManagerObject);
Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[]{IActivityManagerIntercept}, handler);
mInstance.set(defaultValue, proxy);
} catch (Exception e) { Log.e("vivid","hook 异常"); e.printStackTrace(); }
class AmsInvocationHandler implements InvocationHandler {
private Object iActivityManagerObject;
private AmsInvocationHandler(Object iActivityManagerObject) { this.iActivityManagerObject = iActivityManagerObject; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if ("startActivity".contains(method.getName())) { } return method.invoke(iActivityManagerObject, args); } }
|
上述代码则是通过反射获取到 ActivityManager 对象,然后通过动态代理构建其一个代理类,并在 invoke
方法中对 startActivity
方法进行判断处理,达到我们的目的
Retrofit
中的动态代理
下文基于 "com.squareup.retrofit2:retrofit:2.7.1"
分析
我们知道在 Retrofit
中,api 方法的定义是定义在一个 interface 中的,例如
1 2 3 4
| interface IPodCastBusiness { @GET fun getRss(@Url url: String): Flowable<RssResponse> }
|
而在使用该方法进行网络请求时,是使用以下的方法进行请求的
1 2 3 4 5 6 7 8
| Retrofit.Builder() .baseUrl("xxxx") .client(OkHttpClient()) .addConverterFactory(SimpleXmlConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(IPodCastBusiness::class.java) .getRss(rssLink)
|
我们重点看一下这个 create
方法,看 Retrofit 是如何将一个 IPodCastBusiness
的接口转化为一个实例的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| public <T> T create(final Class<T> service) { validateServiceInterface(service); return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service }, new InvocationHandler() { private final Platform platform = Platform.get(); private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable { if (method.getDeclaringClass() == Object.class) { return method.invoke(this, args); } if (platform.isDefaultMethod(method)) { return platform.invokeDefaultMethod(method, service, proxy, args); } return loadServiceMethod(method).invoke(args != null ? args : emptyArgs); } }); }
|
到这里,我们就知道了在 Retrofit 中是如何将一个 Interface 转化成一个实例对象了的
其远离就是通过 Proxy.newProxyInstance() 方法生成其代理对象返回