I. Agent mode
Definition of proxy pattern: Provides a proxy for other objects to control access to that object.
Static proxy
The proxy class of a static proxy holds a reference to the proxied, and when a method is called, the proxy object calls the proxied object to perform the real implementation.
2.1 Simple Example
For example, for a logging function, you can use the proxy class to proxy instance logging:
// 1. Define the interface layer first
interface ILogProcessor {
public void printLog(String log);
}
// 2. Define the implementation class
public class MainLogProcessor implements ILogProcessor {
@Override
public void printLog(String log) {
Log.d("LogProcessor"."MainLogProcessor"+ log); }}// 3. Define the proxy class
public class LogProcessorProxy implements ILogProcessor {
// The proxy class holds a reference to the proxy class
private ILogPrinter mainLogProcessor = new MainLogProcessor();
@Override
public void printLog(String log) {
// Some additional processing that the proxy class needs to do
Log.d("LogProcessor"."ProxyLogProcessor"+ log); String finalLog = Thread.currentThread() + log; mainLogPrinter.printLog(finalLog); }}Copy the code
In this way, it can be used in the following ways:
public void main(a) {
String log = "important log";
ILogPrinter proxy = new LogPrinterProxy();
proxy.printLog(log);
}
Copy the code
2.2 AIDL
AIDL also uses proxy mode. Create a new AIDL file in Android Studio, as shown below:
// IMyAidlInterface.aidl
package com.bc.sample;
interface IMyAidlInterface {
void testFun(a);
}
Copy the code
After clicking Make Project, the code corresponding to AIDL interface will be automatically generated, namely the corresponding Stub and Proxy. It can be seen that AIDL Proxy is implemented through static Proxy, and the code automatically generated is as follows:
package com.bc.sample;
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.bc.sample.IMyAidlInterface
{
@Override public void testFun(a) throws android.os.RemoteException
{}@Override
public android.os.IBinder asBinder(a) {
return null; }}/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.bc.sample.IMyAidlInterface
{
private static final java.lang.String DESCRIPTOR = "com.bc.sample.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub(a)
{
this.attachInterface(this, DESCRIPTOR);
}
/** * Cast an IBinder object into an com.bc.sample.IMyAidlInterface interface, * generating a proxy if needed. */
public static com.bc.sample.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if(((iin! =null)&&(iin instanceof com.bc.sample.IMyAidlInterface))) {
return ((com.bc.sample.IMyAidlInterface)iin);
}
return new com.bc.sample.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder(a)
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_testFun:
{
data.enforceInterface(descriptor);
this.testFun();
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags); }}}// Focus on the implementation of proxy mode; You can see that AIDL's Proxy is implemented in static Proxy mode
private static class Proxy implements com.bc.sample.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder(a)
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor(a)
{
return DESCRIPTOR;
}
@Override public void testFun(a) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_testFun, _data, _reply, 0);
if(! _status && getDefaultImpl() ! =null) {
getDefaultImpl().testFun();
return;
}
_reply.readException();
}
finally{ _reply.recycle(); _data.recycle(); }}public static com.bc.sample.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_testFun = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.bc.sample.IMyAidlInterface impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if(Stub.Proxy.sDefaultImpl ! =null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if(impl ! =null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.bc.sample.IMyAidlInterface getDefaultImpl(a) {
returnStub.Proxy.sDefaultImpl; }}public void testFun(a) throws android.os.RemoteException;
}
Copy the code
Dynamic proxy
In the static proxy code of 2.1, if you now want to extend a new interface, you need to change the interface layer, the actual processing class, and the proxy class separately, as follows:
// 1. Define the interface layer first
interface ILogSender {
public void sendLog(String log);
}
// 2. Define the implementation class
public class MainLogSender implements ILogSender {
@Override
public void sendLog(String log) {
Log.d("LogSender"."MainLogSender send"+ log); }}// 3. Define the proxy class
public class LogSenderProxy implements ILogSender {
// The proxy class holds a reference to the proxy class
private ILogSender mainLogSender = new MainLogSender();
@Override
public void sendLog(String log) {
// Some additional processing that the proxy class needs to do
Log.d("LogSender"."ProxyLogSender"+ log); String finalLog = Thread.currentThread() + log; mainLogSender.sendLog(finalLog); }}Copy the code
When you proxy a new interface, the new proxy class code is highly similar to other proxy classes, so you can use dynamic proxies to accomplish similar functions.
3.1 Basic Usage
Unlike static proxies, the classes of dynamic proxy classes are generated dynamically at run time.
3.1.1 Basic Usage
First define the interface layer:
// Define the interface layer
interface ILogProcessor {
public void printLog(String log);
}
Copy the code
Then use proxy.newProxyinstance to complete the dynamic Proxy as follows:
private void main(a) {
ILogProcessor proxy = (ILogProcessor) Proxy.newProxyInstance(this.getClassLoader(), newClass<? >[] {ILogPrinter.class},new InvocationHandler() {
/ * * *@paramProxy Proxy object *@paramMethod Specifies the method to be called@paramArgs The argument list of the called method */
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
String log = (String)args[0];
Log.d("LogProcessor"."ProxyLogProcessor" + log);
String finalLog = Thread.currentThread() + log;
Log.d("LogProcessor"."printLog" + finalLog);
return null; }}); proxy.printLog("");
}
Copy the code
The InvocationHandler is the part that needs to be implemented in the static Proxy is removed, that is, the dynamically generated Proxy represents the InvocationHandler.
3.1.2 Dynamically generated Classes
The invoke method of the dynamically generated proxy class is invoked by the Invoke method of the InvocationHandler. The dynamically generated proxy class pseudocode is as follows:
public final class $Proxy0 extends Proxy implements ILogProcessor {
static {
m3 = Class.forName("com.bc.sample.ILogProcessor").getMethod("printLog");
}
private static Method m3;
public void printLog(String log) {
// The invoke method of InvocationHandler is actually called
super.h.invoke(this, m3, (Object[])log); }}Copy the code
3.1.3 Source code analysis
NewProxyInstance proxy. newProxyInstance
public class Proxy implements java.io.Serializable {
// Dynamically generated class cache; ProxyClassFactory dynamically generates proxy classes
private static finalWeakCache<ClassLoader, Class<? >[], Class<? >> proxyClassCache =new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
// Generate the proxy class and return the corresponding instance
public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h)
{
Objects.requireNonNull(h);
finalClass<? >[] intfs = interfaces.clone();// Find or dynamically generate the class of the proxy classClass<? > cl = getProxyClass0(loader, intfs);// Get the constructor of the dynamic proxy class
finalConstructor<? > cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
if(! Modifier.isPublic(cl.getModifiers())) { cons.setAccessible(true);
}
// Call the constructor by reflection to return an instance of the proxy class; The InvocationHandler object in the argument is passed as a parameter to the class constructor;
return cons.newInstance(new Object[]{h});
}
// Get the dynamic proxy class
private staticClass<? > getProxyClass0(ClassLoader loader, Class<? >... interfaces) {return proxyClassCache.get(loader, interfaces);
}
private static final class ProxyClassFactory
implements BiFunction<ClassLoader.Class<? > [].Class<?
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
Weakcache.get () will call apply, and generateProxy will be called to generate the dynamic proxy class
@Override
publicClass<? > apply(ClassLoader loader, Class<? >[] interfaces) {/ /...
returngenerateProxy(proxyName, interfaces, loader, methodsArray, exceptionsArray); }}// Dynamically generate the proxy class
private static nativeClass<? > generateProxy(String name, Class<? >[] interfaces, ClassLoader loader, Method[] methods, Class<? >[][] exceptions); }Copy the code
3.2 Retrofit dynamic proxy
Retrofit is an open source web library that also uses the dynamic proxy pattern in its source code. A brief introduction is as follows:
3.2.1 Basic Usage
First, create the interface corresponding to the network request:
public interface SampleRequestInterface {
@HTTP(method = "GET", path = "/api", hasBody = true)
Call<BaseResponse> getCall(a);
}
Copy the code
The request is then generated using the Retrofit dynamic proxy:
public class MainActivity {
private void sendRequest(a) {
try {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://graph.baidu.com")
.addConverterFactory(GsonConverterFactory.create())
.build();
// Return the proxy class instance of the request interface. The internal implementation is implemented using dynamic proxies
SampleRequestInterface sampleRequest = retrofit.create(SampleRequestInterface.class);
Call<BaseResponse> call = sampleRequest.getCall();
// Send the request asynchronously
call.enqueue(new Callback<BaseResponse>() {
@Override
public void onResponse(Call call, Response response) {
response.body();
}
@Override
public void onFailure(Call call, Throwable t) {}});// Send requests synchronously
Response<BaseResponse> response = call.execute();
response.body();
} catch(Exception exception) { exception.printStackTrace(); }}}Copy the code
3.2.2 Retrofit dynamic proxy implementation
Let’s look at how the dynamic proxy pattern is used internally in Retrofit:
public final class Retrofit {
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
return (T) Proxy.newProxyInstance(service.getClassLoader(), newClass<? >[] { service },new InvocationHandler() {
private final Platform platform = Platform.get();
@Override public Object invoke(Object proxy, Method method, Object... args)
throws Throwable {
// ...
// The actual code that needs to be executed
ServiceMethod serviceMethod = loadServiceMethod(method);
OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
returnserviceMethod.callAdapter.adapt(okHttpCall); }}); }}Copy the code