aidlクロスプロセス通信パッケージ(偽のエルメス)



Aidl Cross Process Communication Package



Androidが相互に通信できない前のプロセスが異なるため、プロセス間通信の開発プロセスで遭遇した場合、一般的なスキームはAIDL(Androidインターフェイス定義言語)であり、これを介してプロセス間の通信インターフェイスを定義できますが、たとえば、プラグインの開発を体験したい場合や、単一のアプリケーションで複数のプロセスを開く必要がある場合に、多数のアプリケーションがプロセス間通信を行うと、AIDLの学生は深い悲しみを感じるようになります。 。今ここにいるので、空腹のイベントのコース間で配布されたオープンソースライブラリを試すことができます- HermesEventBus
導入前の間に、HermesEventBusはその基礎となる依存ライブラリHermesを簡単に説明します-これはAndroidに飢えたシニアエンジニアのZhao Lifei外科医Androidプロセス間通信IPCフレームワークを巧みに使用した小説でもあり、開発者Hermesの当初の意図はマスタースレーブプロセスを解決することでした通信問題プラグインフレームワークDroidPlugin、効果は最終的にプロセス間通信が便利でシンプルなローカル関数を呼び出すようになることを認識し、プロセス間関数のコールバックとガベージリサイクルをサポートします。
詳細については、神を飛ばすために移動してください エルメス


A、エルメスはコアアイデアを達成します

  1. エイドル
    クロスプロセス通信でのandroid、通常はaidlを使用しますが、aidlを使用すると、aidlが独自のファイルを書き込んでからサービスを作成するたびに、サービスが通信するためのリターンバインダーになるため、親戚はより厄介になります。



  2. 動的プロキシ
    クライアントでインターフェイスを提供し、動的プロキシを作成する場合があります。メソッドが呼び出されるたびに、メソッド名を取得し、サーバー側でクロスプロセスエイドルを介して呼び出し、メソッド名とクラスIDリフレクションを介して対応するメソッドを呼び出します。


第二に、パッケージの使用

サーバーとクライアントのSecondActivityMainActivityを異なるプロセスに設定します



<activity android:name='com.hxl.hermes.MainActivity'> </activity> <activity android:name='com.hxl.hermes.SecondActivity' android:process=':second'/>

1.クロスプロセスシングルトンを作成します

サーバープロセスとクライアントプロセスに対して同一のインターフェイスが必要です。インターフェイスは主に、使用するクライアントプロセスに提供されます。

コメントを追加するための上記のサーバーインターフェイスクラス@ClassId( 'クラスのフルパスを実現する')

@ClassId('com.hxl.hermes.dao.UserManager') public interface IUserManager { Person getPerson() void setPerson(Person person) } There must be a singleton class that implements the server process interfaces, implementation class must be a singleton, and obtain the singleton method name must be:*getInstance()* ``````java public class UserManager implements IUserManager{ private static final UserManager ourInstance = new UserManager() public static UserManager getInstance() { return ourInstance } private UserManager() { } private Person person public Person getPerson() { return person } public void setPerson(Person person) { this.person = person } }

2.登録サーバー

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Hermes.getDefault().register(UserManager.class) Hermes.getDefault().register(FileManager.class) }

配信される単一のセットの値の例



UserManager.getInstance().setPerson(new Person('hxl', '123456'))

3.クライアント接続

Hermes.getDefault().connect(this, HermesService.class)

4.クロスプロセス通信、クライアントはサーバーシングルトンを取得します

IUserManager iUserManager = Hermes.getDefault().getInstance(IUserManager.class) Person person = iUserManager.getPerson() Toast.makeText(this, person.toString(), Toast.LENGTH_SHORT).show()

第三に、コアコード

1.登録サーバー

登録サーバーがキャッシュシングルトンに保存された後

public void register(Class<?> clazz) { typeCenter.register(clazz) } public void register(Class<?> clazz){ registerClass(clazz) registerMethod(clazz) } // class name registration class private final ConcurrentHashMap<String, Class<?>> mAnnotatedClasses // all methods of registration private final ConcurrentHashMap<Class<?>, ConcurrentHashMap<String, Method>> mRawMethods private void registerMethod(Class<?> clazz){ mRawMethods.putIfAbsent(clazz, new ConcurrentHashMap<String, Method>()) ConcurrentHashMap<String, Method> map = mRawMethods.get(clazz) Method[] methods = clazz.getMethods() for (Method method : methods) { String key = TypeUtils.getMethodId(method) map.put(key, method) } } private void registerClass(Class<?> clazz){ String className = clazz.getName() mAnnotatedClasses.putIfAbsent(className, clazz) }

2.クライアント接続

public void connect(Context context, Class<? extends HermesService> service){ connectApp(context, null, service) } private void connectApp(Context context, String packageName, Class<? extends HermesService> service){ serviceConnectionManager.bind(context.getApplicationContext(), packageName, service) }

サーバーHermesServiceをバインドし、バインダーを取得します

public void bind(Context context, String packageName, Class<? extends HermesService> service){ HermesServiceConnection connection = new HermesServiceConnection(service) mHermesServiceConnections.put(service, connection) Intent intent if (TextUtils.isEmpty(packageName)){ intent = new Intent(context, service) }else{ intent = new Intent() intent.setClassName(packageName,service.getName()) } context.bindService(intent, connection, Context.BIND_AUTO_CREATE) } private class HermesServiceConnection implements ServiceConnection{ private Class<? extends HermesService> mClass public HermesServiceConnection(Class<? extends HermesService> mClass) { this.mClass = mClass } @Override public void onServiceConnected(ComponentName name, IBinder service) { SunService sunService = SunService.Stub.asInterface(service)// Get binder mHermesServices.put(mClass, sunService) } @Override public void onServiceDisconnected(ComponentName name) { mHermesServices.remove(mClass) } }

3.クロスプロセスコミュニケーション

クライアントが単一のケースを取得した場合の動的プロキシサーバーによる

IUserManager iUserManager = Hermes.getDefault().getInstance(IUserManager.class) public <T> T getInstance(Class<T> clazz){ return getProxy(HermesService.class, clazz) } private <T> T getProxy(Class<? extends HermesService> service, Class clazz){ ClassLoader classLoader = service.getClassLoader() return (T) Proxy.newProxyInstance(classLoader, new Class<?>[]{clazz}, new HermesInvocationHandler(service, clazz)) }

動的プロキシクライアントメソッドが単一の実施形態によって呼び出されると、次のように、invokeメソッドに応答します。

public class HermesInvocationHandler implements InvocationHandler { private Class clazz private static Gson gson = new Gson() private Class hermesService public HermesInvocationHandler(Class<? extends HermesService> service, Class clazz){ this.clazz = clazz this.hermesService = service } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Response responce= Hermes.getDefault().sendObjectRequest(hermesService, clazz, method, args) //1 if (!TextUtils.isEmpty(responce.getData())){ ResponseBean responceBean = gson.fromJson(responce.getData(), ResponseBean.class) if (responceBean.getData() != null){ String data = gson.toJson(responceBean.getData()) Class<?> returnType = method.getReturnType() return gson.fromJson(data, returnType) } } return null } }

次のように、単一のクラス名、呼び出し要求オブジェクトとしてパッケージ化されたメソッド名に基づくコード例1:

public <T> Response sendObjectRequest(Class<? extends HermesService> hermesServiceClass, Class<T> clazz, Method method, Object[] parameters) { RequestBean requestBean = new RequestBean() // Get the class name ClassId classId = clazz.getAnnotation(ClassId.class) // whether annotated if (classId == null){ requestBean.setClassName(clazz.getName()) requestBean.setResultClassName(clazz.getName()) }else{ requestBean.setClassName(classId.value()) requestBean.setResultClassName(classId.value()) } // Set the method name if (method != null){ requestBean.setMethodName(TypeUtils.getMethodId(method)) } // set the parameter information, the parameters of json 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.setRequestParameters(requestParameters) } // Request object encapsulated as Request request = new Request(gson.toJson(requestBean), TYPE_GET) return serviceConnectionManager.request(hermesServiceClass, request)//1 } ... //serviceConnectionManager.request public Response request(Class<? extends HermesService> sunHermesServiceClass, Request request){ SunService sunService = mHermesServices.get(sunHermesServiceClass)// Get the cache binder if (sunService != null){ try { return sunService.send(request)// send calls through the service side of the binder } catch (RemoteException e) { e.printStackTrace() } } return null }

サービス終了情報を受信したら、応答オブジェクトを生成します

public class HermesService extends Service{ @Nullable @Override public IBinder onBind(Intent intent) { return mBinder } private SunService.Stub mBinder = new SunService.Stub() { @Override public Response send(Request request) { ResponseMake responseMake = null switch (request.getType()){ case Hermes.TYPE_GET: // get a singleton responseMake = new InstanceResponseMake() break } if (responseMake != null){ return responseMake.makeResponse(request)// generate a response based on request } return null } } }

次のように応答オブジェクトを生成します。

public Response makeResponse(Request request){ // 1. Remove the requestBean request message and converts requestBean. RequestBean requestBean = gson.fromJson(request.getData(), RequestBean.class) // 2. Singleton class name of the target set by requestBean to load the class. resultClass = typeCenter.getClassType(requestBean.getResultClassName()) // 3. Get to the specific method to be executed by the method name set in requestBean. setMethod(requestBean)//1 // 4. Assembled parameter, the parameter reduction assembly. RequestParameter[] requestParameters = requestBean.getRequestParameters() if (requestParameters != null && requestParameters.length > 0){ mParameters = new Object[requestParameters.length] for (int i = 0 i < requestParameters.length i++) { RequestParameter requestParameter = requestParameters[i] Class<?> clazz = typeCenter.getClassType(requestParameter.getParameterClassName()) mParameters[i] = gson.fromJson(requestParameter.getParameterValue(), clazz) } }else{ mParameters = new Object[0] } // 5. The execution method, and get the results Object resultObj = invokeMethod()//2 // 6. The results of the implementation package is returned to the client process Response ResponseBean responseBean = new ResponseBean(resultObj) return new Response(gson.toJson(responseBean)) }

1,2 InstanceResponseMakeがコードに実装され、

@Override protected void setMethod(RequestBean requestBean) { try { instance = objectCenter.get(requestBean.getClassName()) if (instance == null){// for access to the singleton object, we need to get // server method name singleton must be getInstance Method getInstanceMethod = resultClass.getMethod('getInstance', new Class[]{}) if (getInstanceMethod != null){ instance = getInstanceMethod.invoke(null)// singleton object Get objectCenter.put(instance)// cached } } // Get the request method name in mMethod = typeCenter.getMethod(resultClass, requestBean) } catch (Exception e) { e.printStackTrace() } } @Override protected Object invokeMethod() { Object object = null try { // final through reflection, calls the server method object = mMethod.invoke(instance, mParameters) } catch (Exception e) { e.printStackTrace() } return object }

プロジェクトアドレスhttps://github.com/hxljy/HermesTest