preface

In the last article [Android is the most simple] AIDL advanced (two-way communication), has introduced to you the advanced use of AIDL, used for service and client two-way call, basically can meet your daily development needs, And the establishment of a cross-process invocation framework based on AIDL implementation for a new requirement or a new project, it is not difficult to handle the keyboard code in the end. However, if a project that has been maintained for N hands suddenly needs to implement N multiple requirements into a cross-process implementation, then it will be very difficult to realize. N multiple methods need to implement AIDL, N multiple transfer bean classes need to implement Parceable serialization, three N series.

As long as the mind does not slide, the way is more than difficult!

Their thinking

-1.0

To achieve a universal interface, all data transfer to JSON string, after receiving data in the unboxed restore bean class, good, perfect solution, Xi Big pu run! This over!

Such processing, although not not good, but so simple, operation is so complex, but also this article why!

-2.0

Getting down to business, the easiest way to think of is a pure AIDL implementation, interface file one-to-one correspondence, all methods write AIDL interface file implementation! This can be easily implemented using the AIDL advanced (two-way communication) introduction in the series “Android Is the Simplest”.

  • Advantages: Simple and easy to understand.
  • Disadvantages:
    • Renovation cost: high cost, more suitable for new projects, not suitable for old projects;
    • Follow-up maintenance: There are many AIDL interface files and AIDL serialized object files, which is not conducive to maintenance.

-3.0

Since AIDL can transmit data, can it transmit the name of the interface class to be called, the name of the method, and the parameters, find the corresponding implementation class based on the data received, execute the method, and return the result? In this way, there is no perception for the caller and the lowest cost for the provider. We can simply divergent our thinking. Imagine initiating a network request, we only need to know the corresponding URL and the corresponding parameters.

The answer, of course, is yes, otherwise why so much nonsense. It’s time for today’s big kill – Dynamic Proxy & Reflection!

Butcher knife. – Practical operation

1. Create bidirectional communication

Create a bidirectional communication AIDL interface file based on the advanced method introduced in the previous article.

interface IService {
    IPCResponse sendRequest(in IPCRequest request);

    void attach(in IClientBridge iClientBridge);
}
Copy the code
interface IClientBridge {
	IPCResponse sendRequest(in IPCRequest request);
}
Copy the code

Create an interface class (IPCRequest) that stores the parameters of the request and an interface class (IPCResponse) that carries the results of the return, just by specifying the implementation of Parcelable, which will be implemented later.

parcelable IPCRequest;
Copy the code
parcelable IPCResponse;
Copy the code

2. Cache implementation objects

We will implement object to cache the corresponding class by the implementation interface name, cache all methods by class, and cache object itself.

public class IPCCache {

    /** * Holds the Class mapping and internal methods */ for the interfaces that the server processes the client request
    private finalMap<String, Class<? >> mClazzs =new HashMap<>();
    private finalMap<Class<? >, HashMap<String, Method>> mMethods =new HashMap<>();
    /** * saves the instance of the server processing the client request */
    private final Map<String, WeakReference<Object>> mInstance = new HashMap<>();

    /** * Cache objects and their methods **@param object
     */
    public void register(Object object) { Class<? > clazz = object.getClass(); Class<? >[] interfaces = clazz.getInterfaces();for(Class<? > cls : interfaces) { mClazzs.put(cls.getName(), clazz); }/ / the cache Method
        HashMap<String, Method> method = new HashMap<>();
        Method[] methods = clazz.getMethods();
        for (Method m : methods) {
            method.put(m.getName(), m);
        }
        mMethods.put(clazz, method);
        addObject(clazz.getName(), object);
    }

    public void unRegister(Object object) { Class<? > clazz = object.getClass(); Class<? >[] interfaces = clazz.getInterfaces();for(Class<? > cls : interfaces) { mClazzs.remove(cls.getName()); } mMethods.remove(clazz); removeObject(clazz.getName()); }publicClass<? > getClass(String interfacesName) {if (TextUtils.isEmpty(interfacesName)) {
            return null;
        }
        return mClazzs.get(interfacesName);
    }

    public Method getMethod(Class
        clazz, String methodName) {
        HashMap<String, Method> methods = mMethods.get(clazz);
        return methods == null ? null : methods.get(methodName);
    }

    private void addObject(String className, Object object) {
        mInstance.put(className, new WeakReference<>(object));
    }

    private void removeObject(String className) {
        mInstance.remove(className);
    }

    public Object getObject(String className) {
        return mInstance.containsKey(className) ? mInstance.get(className).get() : null; }}Copy the code

3. Dynamic proxy [Using client as an example]

The dynamic proxy encapsulates method names, parameters, and so on into IPCRequest and sends it to the service.

    public <T> T get(Class<T> inter) {
        if (null == mIpcService) {
            return null;
        }
        // Get the Key of the object processing the client request to find the corresponding handler on the server side
        return (T) Proxy.newProxyInstance(getClass().getClassLoader(),
                new Class[]{inter},
                new ClientInvocationHandler(inter.getName()));
    }
Copy the code
public class ClientInvocationHandler implements InvocationHandler {

    private Gson mGson;
    private String interfacesName;

    public ClientInvocationHandler(String interfacesName) {
        this.interfacesName = interfacesName;
        mGson = new Gson();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /* When a proxy object executes a method, it goes here and constructs a request to forward to the server */
        IPCRequest ipcRequest = new IPCRequest();
        ipcRequest.setInterfacesName(interfacesName);
        ipcRequest.setMethodName(method.getName());
        ipcRequest.setParameters(ParamsConvert.serializationParams(args));
        IPCResponse ipcResponse = ClientManager.getInstance().sendRequest(ipcRequest);
        if(ipcResponse ! =null&& ipcResponse.isSuccess()) { Class<? > returnType = method.getReturnType();if(returnType ! =void.class && returnType ! = Void.class) {returnmGson.fromJson(ipcResponse.getResult(), returnType); }}return null; }}Copy the code

4. Implement bidirectional communication [Take service as an example]

Provide according to the interface name, Method name and parameter carried in the request IPCRequest, find the corresponding implementation class object and Method, call the execution Method through reflection, and return the result.

        @Override
        public IPCResponse sendRequest(IPCRequest request) throws RemoteException {
            try{ Class<? > aClass = ServiceManager.getInstance().getClass(request.getInterfacesName()); Object object = ServiceManager.getInstance().getObject(aClass.getName()); Method me = ServiceManager.getInstance().getMethod(aClass, request.getMethodName()); Object[] params = ParamsConvert.unSerializationParams(request.getParameters()); Object result = me.invoke(object, params); String r = ParamsConvert.mGson.toJson(result);return new IPCResponse(r, "Method executed successfully".true);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
Copy the code

The Demo link

Github.com/LongAgoLong…