天天看點

餓了麼開源項目Hermes跨程序架構分析1-服務端注冊1 注冊實作

文章目錄

  • 1 注冊實作
    • 1.1 建立aidl檔案
    • 1.2 服務端實作
      • 1.2.1 HermesService
      • 1.2.2 建立Request和Responce
      • 1.2.3 建立單例UserManager.java
      • 1.2.4 建立資料類Friend.java
      • 1.2.5 建立Hermes.java

參考:

https://blog.csdn.net/xiaofei_it/article/details/51464518

https://github.com/Xiaofei-it/Hermes

https://www.jianshu.com/p/29999c1a93cd

連結:

餓了麼開源項目Hermes跨程序架構分析1-服務端注冊

餓了麼開源項目Hermes跨程序架構分析2-用戶端連接配接

先做一個小實驗,SecondActivity另啟一個程序,代碼如下:

public class MyApplication extends Application {
    private static int i= 0;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("hongxue", "onCreate " + (i++));
    }

}
           
<activity android:name=".SecondActivity"
            android:process=":s"/>
           

輸出結果:

hongxue: onCreate 0
hongxue: onCreate 0
           

i的值并沒有增加,說明不同程序間記憶體不會共享。

首先給出hermes跨程序調用的流程圖。

餓了麼開源項目Hermes跨程式架構分析1-服務端注冊1 注冊實作

圖 hermes跨程序調用

  1. 服務端和用戶端都會存在一個單例,根據開始的小實驗我們知道,不同的程序間記憶體不共享,是以用戶端的是一個單例的動态代理。
  2. 在跨程序通信中,如果存在多條資料的發送,就要建立多個aidl檔案。為了防止這種情況,hermes對資料進行了再次封裝,通過json将對象轉化成字元串。

3個步驟:

  1. 服務端注冊
  2. 用戶端連接配接
  3. 通信

1 注冊實作

1.1 建立aidl檔案

先建立aidl檔案用于實作程序間通信:

1 MyEventBusService.aidl

import com.hongx.hermes.Request;
import com.hongx.hermes.Responce;
interface MyEventBusService {
    Responce send(in Request request);
}

           

2 Responce.aidl

package com.hongx.hermes;
parcelable Responce;
           

3 Request.aidl

package com.hongx.hermes;
parcelable Request;
           

1.2 服務端實作

1.2.1 HermesService

需要建立一個 Service 供用戶端遠端綁定,這裡命名為 HermesService。

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class HermesService extends Service {
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

           

并在Manifest檔案中添加:

1.2.2 建立Request和Responce

建立Request.java:

import android.os.Parcel;
import android.os.Parcelable;

public class Request implements Parcelable {
    //請求的對象  RequestBean 對應的json字元串
    private String data;
    //    請求對象的類型
    private int type;
    //    反序列化   A程序
    protected Request(Parcel in) {
        data = in.readString();
        type=in.readInt();
    }
    public String getData() {
        return data;
    }
    public int getType() {
        return type;
    }
    public Request(String data, int type) {
        this.data = data;
        this.type = type;
    }
    public static final Creator<Request> CREATOR = new Creator<Request>() {
        @Override
        public Request createFromParcel(Parcel in) {
            return new Request(in);
        }
        @Override
        public Request[] newArray(int size) {
            return new Request[size];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    //  序列化
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(data);
        parcel.writeInt(type);
    }
}
           

建立Responce.java:

import android.os.Parcel;
import android.os.Parcelable;

public class Responce implements Parcelable {
    //    響應的對象
    private String data;
    public String getData() {
        return data;
    }
    protected Responce(Parcel in) {
        data = in.readString();
    }
    public Responce(String data) {
        this.data = data;
    }
    public static final Creator<Responce> CREATOR = new Creator<Responce>() {
        @Override
        public Responce createFromParcel(Parcel in) {
            return new Responce(in);
        }
        @Override
        public Responce[] newArray(int size) {
            return new Responce[size];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(data);
    }
}
           

注意:要先建立Responce.aidl和Request.aidl ,然後再建立Responce.java 和 Request.java,否則會報錯。

1.2.3 建立單例UserManager.java

建立UserManager并注冊到服務端,用戶端就可以到服務端拿到代理對象。

public class UserManager implements IUserManager {
    Friend friend;
    private static UserManager sInstance = null;
    private UserManager() {}
    public static synchronized UserManager getInstance(){
        if(sInstance == null){
            sInstance = new UserManager();
        }
        return sInstance;
    }
    public static synchronized UserManager getInstance(String s){
        if(sInstance == null){
            sInstance = new UserManager();
        }
        return sInstance;
    }
    public Friend getFriend() {
        return friend;
    }
    public void setFriend(Friend friend) {
        this.friend = friend;
    }
    public static UserManager getsInstance() {
        return sInstance;
    }
    public static void setsInstance(UserManager sInstance) {
        UserManager.sInstance = sInstance;
    }
}
           

1.2.4 建立資料類Friend.java

public class Friend {
    private String name;
    private int age;
    public Friend(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
           
public interface IUserManager {
    public Friend getFriend();
    public void setFriend(Friend friend);
}
           

1.2.5 建立Hermes.java

在MainActivity的onCreate方法中進行初始化和注冊

Hermes.getDefault().init(this);
  Hermes.getDefault().register(UserManager.class);
           

Hermes.java

public class Hermes {
    private Context mContext;
    private TypeCenter typeCenter;
    private ServiceConnectionManager serviceConnectionManager;

    private Gson gson = new Gson();
    //得到對象
    public static final int TYPE_NEW = 0;
    //得到單例
    public static final int TYPE_GET = 1;
    private static final Hermes ourInstance = new Hermes();

    public Hermes() {
        serviceConnectionManager = ServiceConnectionManager.getInstance();
        typeCenter = TypeCenter.getInstance();
    }

    public static Hermes getDefault() {
        return ourInstance;
    }

    public void init(Context context) {
        this.mContext = context.getApplicationContext();
    }

    //----------------------------服務端-------------------------
    public void register(Class<UserManager> clazz) {
        typeCenter.register(clazz);
    }
    //----------------------------用戶端-------------------------
    public void connect(Context context,
                        Class<HermesService> hermesServiceClass) {
        connectApp(context, null, hermesServiceClass);
    }

    private void connectApp(Context context, String packageName, Class<HermesService> hermesServiceClass) {
        init(context);
        serviceConnectionManager.bind(context.getApplicationContext(), packageName, hermesServiceClass);
    }

    public <T> T getInstance(Class<T> tClass, Object... parameters) {
        Responce responce = sendRequest(HermesService.class, tClass, null, parameters);
        return getProxy(HermesService.class, tClass);
    }

    private <T> T getProxy(Class<HermesService> hermesServiceClass, Class<T> tClass) {
        ClassLoader classLoader = hermesServiceClass.getClassLoader();
        T proxy = (T) Proxy.newProxyInstance(classLoader, new Class<?>[]{tClass},
                new HermesInvocationHander(hermesServiceClass, tClass));
        return proxy;
    }

    private <T> Responce sendRequest(Class<HermesService> hermesServiceClass,
                                     Class<T> tClass, Method method, Object[] parameters) {
        RequestBean requestBean = new RequestBean();

        //set全類名
        String className = null;
        if (tClass.getAnnotation(ClassId.class) == null) {
            requestBean.setClassName(tClass.getName());
            requestBean.setResultClassName(tClass.getName());
        } else {
            requestBean.setClassName(tClass.getAnnotation(ClassId.class).value());
            requestBean.setResultClassName(tClass.getAnnotation(ClassId.class).value());
        }


        //set方法
        if (method != null) {
            requestBean.setMethodName(TypeUtils.getMethodId(method));
        }

        //set參數
        RequestParameter[] requestParameters = null;
        if (parameters != null && parameters.length > 0) {
            requestParameters = new RequestParameter[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                Object parameter = parameters[i];
                String parameterClassName = parameter.getClass().getName();
                String parameterValue = gson.toJson(parameter);
                RequestParameter requestParameter = new RequestParameter(parameterClassName, parameterValue);
                requestParameters[i] = requestParameter;
            }
        }
        if (requestParameters != null) {
            requestBean.setRequestParameter(requestParameters);
        }

        Request request = new Request(gson.toJson(requestBean), TYPE_GET);

        return serviceConnectionManager.request(hermesServiceClass, request);
    }

    public <T> Responce sendObjectRequest(Class hermeService, Class<T> aClass,
                                          Method method, Object[] args) {
        RequestBean requestBean = new RequestBean();

        //set全類名
        String className = null;
        if (aClass.getAnnotation(ClassId.class) == null) {
            requestBean.setClassName(aClass.getName());
            requestBean.setResultClassName(aClass.getName());
        } else {
            requestBean.setClassName(aClass.getAnnotation(ClassId.class).value());
            requestBean.setResultClassName(aClass.getAnnotation(ClassId.class).value());
        }

        //set方法
        if (method != null) {
            requestBean.setMethodName(TypeUtils.getMethodId(method));
        }

        //set參數
        RequestParameter[] requestParameters = null;
        if (args != null && args.length > 0) {
            requestParameters = new RequestParameter[args.length];
            for (int i = 0; i < args.length; i++) {
                Object parameter = args[i];
                String parameterClassName = parameter.getClass().getName();
                String parameterValue = gson.toJson(parameter);

                RequestParameter requestParameter = new RequestParameter(parameterClassName, parameterValue);
                requestParameters[i] = requestParameter;
            }
        }

        if (requestParameters != null) {
            requestBean.setRequestParameter(requestParameters);
        }

        Request request = new Request(gson.toJson(requestBean), TYPE_NEW);

        return serviceConnectionManager.request(hermeService, request);
    }
}
           

Hermes中定義了初始化、注冊等一系列方法,現在我們先看init和register方法,調用Hermes的register方法,最終調用的是TypeCenter的register方法,将UserManager對象儲存在一個Map對象中。

TypeCenter.java

public class TypeCenter {

    //為了減少反射,是以儲存起來
    private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Method>> mRawMethods;
    private final ConcurrentHashMap<String, Class<?>> mClazz;

    private static final TypeCenter ourInstance = new TypeCenter();

    public TypeCenter() {
        mRawMethods = new ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Method>>();
        mClazz = new ConcurrentHashMap<>();
    }

    public static TypeCenter getInstance() {
        return ourInstance;
    }

    public void register(Class<HxUserManager> clazz) {
        //注冊--》類, 注冊--》方法
        registerClass(clazz);
        registerMethod(clazz);
    }

    //緩存class
    private void registerClass(Class<HxUserManager> clazz) {
        String name = clazz.getName();
        mClazz.putIfAbsent(name, clazz);
    }
    private void registerMethod(Class<HxUserManager> clazz) {
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            mRawMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>());
            ConcurrentHashMap<String, Method> map = mRawMethods.get(clazz);
            String methodId = TypeUtils.getMethodId(method);
            map.put(methodId, method);
        }
    }

    public Class<?> getClassType(String name) {
        if (TextUtils.isEmpty(name)) {
            return null;
        }
        Class<?> clazz = mClazz.get(name);
        if (clazz == null) {
            try {
                clazz = Class.forName(name);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return clazz;
    }

    public Method getMethod(Class<?> aClass, RequestBean requestBean) {
        String methodName = requestBean.getMethodName();//setFriend()
        if (methodName != null) {
            mRawMethods.putIfAbsent(aClass, new ConcurrentHashMap<String, Method>());
            ConcurrentHashMap<String, Method> methods = mRawMethods.get(aClass);
            Method method = methods.get(methodName);
            if(method != null){
                return method;
            }
            int pos = methodName.indexOf('(');

            Class[] paramters = null;
            RequestParameter[] requestParameters = requestBean.getRequestParameter();
            if (requestParameters != null && requestParameters.length > 0) {
                paramters = new Class[requestParameters.length];
                for (int i=0;i<requestParameters.length;i++) {
                    paramters[i]=getClassType(requestParameters[i].getParameterClassName());
                }
            }
            method = TypeUtils.getMethod(aClass, methodName.substring(0, pos), paramters);
            methods.put(methodName, method);
            return method;
        }
        return null;
    }
}