introduce

The proxy pattern, also known as the delegate pattern, is a structural design pattern.

When it comes to agents, most people may have a strange and familiar feeling, it seems to be able to meet in daily life, such as online agents, investment agents, business agents and so on; But not one, two, or three; If we change our perspective and start from the role of the client, we find an agent to access the Internet, because we have difficulties in accessing some websites, we need a role to help us indirectly to achieve this function; We may use a business agent because we are not good at many things or for other reasons, we need to find a professional intermediary to do things for us. Therefore, in our daily life, we more play the role of the principal, the agent to a middleman role, to help us is to deal with the things we can’t help.

From the point of view of writing code, when we encounter the following scenario:

  • An object cannot be accessed directly
  • You don’t want to access an object directly
  • Difficulty accessing an object

We can access the real object indirectly through a proxy.

Define and UML diagrams

Definition:

Provide a proxy for the target object, through which the client can access the target object.

UML diagrams

From the UML class diagram of the proxy pattern, we can draw the following conclusions:

  • Proxy objects and delegate objects need to implement the same interface (abstract class)
  • The proxy object holds a reference to the delegate object

As you can see, the proxy pattern is very concise. There are only three roles, including abstract topic, principal, and agent. Here is a simple code implementation of the basic proxy pattern.

public interface Subject {
    void doSomething(a);
}

public class RealSubject implements Subject {
    @Override
    public void doSomething(a) {
        System.out.println("This is real doSomeThing"); }}public class ProxySubject implements Subject {

    private Subject mSubject;
    // The proxy class holds a reference to the delegate class
    public ProxySubject(Subject realSubject) {
        mSubject = realSubject;
    }

    @Override
    public void doSomething(a) { mSubject.doSomething(); }}public class Client {
    public static void main(String[] args) {
        // Create the delegate class
        Subject mRealSubject=new RealSubject();
        // Create a proxy class
        ProxySubject mProxy = new ProxySubject(mRealSubject);
        // Let the proxy class do the actionmProxy.doSomething(); }}Copy the code

You can see that both the RealSubject and ProxySubject implement the interface Subject. The doSomething method is invoked on the client using an instance of ProxySubject instead of an instance of the RealSubject.

What’s the point of all this, you might wonder? Wouldn’t it be possible to call the doSomething method directly from an instance of the RealSubject? Why bother. Imagine if there were multiple delegate classes, each with a different implementation, and the client only cared about the call to doSomething, not the implementation, then the proxy class could internally mask the difference between delegate classes, which is something the client doesn’t want to care about. It may be a bit dizzy to say so, the following is through the Android source code implementation to feel.

Proxy mode in Android

When writing code, you may feel that the proxy pattern has not been encountered. In fact, it is not. It can even be said that the agent pattern is one of the most commonly used design patterns. Here’s a look at AppCompatActivity, which is used almost every day.

In the early days, we created our own activities by directly inheriting android.app.activity. Later, as Android versions upgrade, we create an Activity that inherits AppCompatActivity. Compat is short for Compatible, so how does it work?

onCreate

The onCreate() method is the beginning of the entire Activity lifecycle. How does AppCompatActivity do that?

AppCompatActivity-onCreate()

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        finalAppCompatDelegate delegate = getDelegate(); delegate.installViewFactory(); delegate.onCreate(savedInstanceState); ... }Copy the code

As you can see, he doesn’t actually implement onCreate here, but uses an AppCompatDelegate instance’s onCreate() method to do so. Let’s move on to the implementation of getDelegate.

AppCompatActivity-getDelegate()

    @NonNull
    public AppCompatDelegate getDelegate(a) {
        if (mDelegate == null) {
            mDelegate = AppCompatDelegate.create(this.this);
        }
        return mDelegate;
    }
Copy the code

You can see that this instance creation is in the AppCompatDelegate class. Let’s look at the implementation of create

AppCompatDelegate-create()

    public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
        return create(activity, activity.getWindow(), callback);
    }

    private static AppCompatDelegate create(Context context, Window window, AppCompatCallback callback) {
        if (Build.VERSION.SDK_INT >= 24) {
            return new AppCompatDelegateImplN(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 23) {
            return new AppCompatDelegateImplV23(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 14) {
            return new AppCompatDelegateImplV14(context, window, callback);
        } else if (Build.VERSION.SDK_INT >= 11) {
            return new AppCompatDelegateImplV11(context, window, callback);
        } else {
            return newAppCompatDelegateImplV9(context, window, callback); }}Copy the code

As you can see, different Appcompatdelegates are returned for different Android versions. If we look at the source code, we can see that from AppCompatDelegateImplN to AppCompatDelegateImplV9, the subclass is inherited from the superclass. And AppCompatDelegateImplV9 inherited from AppCompatDelegateImplBase (abstract class), and the AppCompatDelegateImplBase is inherited from AppCompatDelegate.

Here, combined with the content of the agency model we said at the beginning, we can easily conclude the following conclusions:

  • AppCompatDelegate has the role of both abstract topic and proxy class
  • AppCompatDelegateImplN, AppCompatDelegateImplV23 and so on are all delegate classes and they all inherit from the AppcompatDelegdelegate class.

As we can see from the source code appcompatdelegate.java, this abstract class defines a range of abstractions related to the Activity, including the Activity lifecycle function, the setContentView, and the compatdelegate.java module. SetSupportActionBar, etc. As we know, a child class can extend (spuer) or directly override the method implementation of the parent class by inheriting from the parent class. AppCompatDelegateImplV9 This class is a specific implementation of AppcompatDelegate. later versions can extend or modify some method implementation by inherits AppCompatDelegateImplV9. Using AppCompatDelegate to create different delegate classes in the create method to do this, we can see that the Android source code is pretty clever about Activity compatibility. AppCompatDelegate is mainly for the compatibility of ActionBar and night mode processing to do some convenient developer implementation; I won’t go into the details here.

Of course, the proxy mode, which can hardly find shortcomings, is widely used in Android source code. Basically, the implementation of compatibility will use the above ideas, for example, NotificationCompatImpl almost uses the same idea as AppCompatDelegate. Realized in the mobile phone notification bar to achieve different notification styles. In addition to compatibility, another classic implementation is Binder. As the core concept of cross-process communication, Binder uses the proxy mode to deal with the problem that we cannot share and pass data between different applications. As for Binder’s analysis, there are so many on the Internet that I will not repeat it here. Interested students can read this article about the use of the proxy model in Binder.

A dynamic proxy

In the above analysis, the proxy class was created directly by us; In a real world scenario, the proxy class is not created when the program is compiled, but is created dynamically during the run through the Java reflection mechanism. In this case, the proxy mode becomes a dynamic proxy, which is the corresponding to the static proxy.

In fact, the implementation of dynamic proxy is nothing to say, it is all template code, Java for developers to provide the InvocationHandler, implement the interface override its Invoke method.

Again, take the example of Subject

public interface Subject {
    void doSomething(a);
}

public class RealSubject implements Subject {
    @Override
    public void doSomething(a) {
        System.out.println("This is real doSomeThing"); }}public class DynamicProxyHandler implements InvocationHandler {
    private Object mObject;


    public DynamicProxyHandler(Object object) {
        mObject = object;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        returnmethod.invoke(mObject, objects); }}public class MainClass {
    public static void main(String[] args) {
        / / the delegate class
        Subject mRealSubject = new RealSubject();
        // Delegate the classLoader
        ClassLoader mClassLoader = mRealSubject.getClass().getClassLoader();
        // Delegate class corresponding ProxyHandler
        DynamicProxyHandler mProxyHandler = new DynamicProxyHandler(mRealSubject);
        Class[] mClasses = new Class[]{Subject.class};
        / / the proxy class
        Subject proxySubject = (Subject) Proxy.newProxyInstance(mClassLoader, mClasses, mProxyHandler);
        // The proxy class calls the methodproxySubject.doSomething(); }}Copy the code

We can see that DynamicProxyHandler does not hold a specific Object internally, but an Object class. In its invoke method, it calls the corresponding method based on the specific Object and its parameters. So when we call the Proxy class on the client side, we dynamically create the Proxy class based solely on the delegate class using the proxy.newProxyInstance method. In the above code, we are dynamically creating a proxy class with the delegate class RealSubject. By calling the methods defined in the abstract topic from the proxy class, we are actually calling the concrete implementation in the delegate class. In Java, we can dynamically create classes and their instances through the reflection mechanism, so we have the flexibility to create proxy classes at run time with different delegate classes to achieve different functions.

This 10-minute article on dynamic proxy analysis in Java is very good. If you are interested in dynamic proxy analysis, you can read it again.

In Android, the most classic use of dynamic proxies is Retrofit, the most popular one in recent years. You can take a quick look at it here.


public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

GitHubService service = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = service.listRepos("octocat");
Copy the code

The above implementation should be familiar by now, but what happens when we call the create method of a Retrofit instance?

  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, @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);
            returnserviceMethod.callAdapter.adapt(okHttpCall); }}); }Copy the code

As you can see, here is a typical dynamic proxy, through serviceMethod. CallAdapter. Adapt to return a proxy object of service objects, in the example above, is the return of a GitHubService proxy objects, In this way, we can call the various network requests defined in GitHubService from such an object, without having to decide whether to put the parameters in the Body or params at the time of use, because Retrofit is a smart combination of reflective annotations and dynamic proxies. All parameters that need to be passed to OKHttp are passed dynamically. Once the interface is defined, it is very convenient to get the most important Call in OKHttp. With a Call we can initiate network requests using execute or enqueue.

conclusion

Above is the analysis of the proxy pattern, generally speaking, the structure of the proxy pattern is very simple; Including abstract topic, delegate class, agent class three core roles, from the general direction can be divided into static proxy and dynamic proxy two categories; Through the way of static proxy, in the process of development iteration, provides a very friendly implementation idea for the implementation of compatibility; In everyday development, if there is a strong coupling between the objects we use, consider whether we can decouple them through the proxy pattern. At the same time, when we need to expand the partial function of a class, but don’t want to go to destruction of the original functions or is can’t modify, we can consider the proxy pattern, but also should understand, through the proxy pattern, we can do is function expansion to update the delegate class has been achieved in the content of he is powerless.

Dynamic proxy, can be dynamically generated according to the runtime delegate class, this reduces the burden of the proxy class, to avoid the coding phase of the specific delegate class to make a variety of judgments.

The proxy pattern is simple and useful, but don’t forget that proxy and delegate classes need interfaces or abstract classes to implement functionality, and don’t overlook this.

Ok, so much for the analysis of the proxy pattern