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