使用AIDL实现多进程Service的流程(使用XMPP Service为例)

所有涉及到包名的地方 全部应该要 一致(包括aidl文件和java文件)!!这个很重要!!!

所有涉及到包名的地方 全部应该要 一致(包括aidl文件和java文件),最终实现 remote XMPPService

包名假定为: xyz.jimbray.xmpp

  • 编写 AIDL 相关文件(包括自定义类型数据java)

    1. 新建 IXMPPServiceInterface.aidl
     package xyz.jimbray.xmpp;
         
     import xyz.jimbray.xmpp.IXMPPReceiveListenerCallback;
         
     interface IXMPPServiceInterface {
        void registerXMPPListenerCallback(IXMPPReceiveListenerCallback listener);
        void unregisterXMPPListenerCallback(IXMPPReceiveListenerCallback listener);
     }
    

    可以看到上面包含了 一个自定义类型,看样子应该是个接口,不是普通的数据类,但是Java普通的interface类在AIDL是不能用的,所以还是要新建一个专门的接口AIDL类

    1. 新建 IXMPPReceiveListenerCallback.aidl
     package xyz.jimbray.xmpp
         
     import xyz.jimbray.xmpp.XMPPMessageBase;
         
     interface IXMPPReceiveListenerCallback {
         void OnXMPPMessageReceived(intout XMPPMessageBase messageBase);
     }
    

    这里有几个知识点

    • 自定义数据类型的使用

    XMPPMessageBase 是一个数据类,必须要实现序列化Parcelable接口

    XMPPMessageBase.java 文件 必须在同一个包名同一个包名同一个包名地址下生成,除了使用AS自动生成的Parcelable代码外,还需要另外定义一个 函数(在AIDL过程中会调用):

       public void readFromParcel(Parcel in) {
               string = in.readString();
       }
    

    同时还要新建一个 XMPPMessageBase.aidl 文件,与java文件同一个包名,文件名也是相同的

       package xyz.jimbray.xmpp;
           
       parcelable XMPPMessageBase;
    

    并且在使用 这个自定义类型数据的时候,aidl文件必须要**手动、显式地进行导入操作 **import

    看 IXMPPReceiveListenerCallback.aidl 文件的 第3行!

    • 数据的 流向设置

    在 AIDL 文件中,所有非Java基本类型的参数必须要加上 in、out、inout标记以表明参数流向

    • in : 表示参数数据只能从客户端传递到服务端,基本类型默认只支持in 流向
    • out: 表示参数数据只能从服务端传递到客户端。即:如果服务端修改了参数对象的值,那么客户端的值也会变化,但是服务端无法读取到客户端的值。
    • inout: 表示参数可以双向传递。

    到目前为止,我们写了 一共4个文件

    1. IXMPPServiceInterface.aidl
    2. IXMPPReceiveListenerCallback.aidl
    3. XMPPMessageBase.aidl
    4. XMPPMessageBase.java
  • 编写 服务端 java 代码

    1. 新建 XMPPService.java (只写关键代码)
     class XMPPService extends Service {
             
         // RemoteCallback
         final RemoteCallbackList<IXMPPReceiveListenerCallback> mXMPPReceiveListenerCallbacks = new RemoteCallbackList<>();
             
         // Binder 
         private final IXMPPServiceInterface.Stub mXMPPBinder = new IXMPPServiceInterface.Stub() {
             @Override
             public void registerXMPPListenerCallback(IXMPPReceiveListenerCallback listener) {
                 // 客户端注册 callback
                 if(listener != null) {
                     mXMPPReceiveListenerCallbacks.register(listener);
                 }
             }
                 
             public void unregisterXMPPListenerCallback(IXMPPReceiveListenerCallback listener) {
                 // 客户端取消注册
                 if(listener != null) {
                     mXMPPReceiveListenerCallbacks.unregister(listener);
                 }
             }
         };
             
         @Override
         public IBinder onBind(Intent intent) {
             return mXMPPBinder;
         }
             
         /**
         * XMPP 的接收消息函数
         * 收到消息之后要callback给客户端
         **/
         @Override
         public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
             // 通过 Message 的消息,构造一个自定义的 XMPPMessageBase 数据类
             // 然后逐个通知 各客户端
             final int clientsSize = mXMPPReceiveListenerCallbacks.beginBroadcast();
             for (int i = 0; i < clientsSize; i++) {
                 try {
                     mXMPPReceiveListenerCallbacks.getBroadcastItem(i).onXMPPMessageReceived(xmppMessageBase);
                 } catch (RemoteException e) {
                         
                 }
             }
         }
             
     }
    

    到这里,服务端就完成了。

    1. 编写客户端 java 代码

    如果是不同的app,需要将AIDL相关文件原封不动地复制到新的App对应的目录中,如果是同一个App的话就不需要了。

    MainActivity.java (只写关键代码)

     private IXMPPServiceInterface mBinder;
         
         
     // 绑定服务
     bindService(Intent(this, XMPPService.class), new ServiceConnection() {
                 @Override
                 public void onServiceConnected(ComponentName name, IBinder service) {
                     mBinder = IXMPPServiceInterface.Stub.asInterface(service);
                    // 写在你应该 注册监听器的地方 mBinder.registerXMPPListenerCallback(localXMPPReceiveListenerCallback)
                 }
         
                 @Override
                 public void onServiceDisconnected(ComponentName name) {
                  // 卸载unBind Service之前即可   mBinder.unregisterXMPPListenerCallback(localXMPPReceiveListenerCallback)
                    mBinder = null;
                 }
             }, BIND_AUTO_CREATE);
         
         
     // 定义 本地的 callback 实例
     private IXMPPReceiveListenerCallback localXMPPReceiveListenerCallback = new IXMPPReceiveListenerCallback.Stub() {
             @Override
             public void onXMPPMessageReceived(XMPPMessageBase messageBase) throws RemoteException {
                 // XMPP 消息接收(从remote XMPPService 远道而来)
                 if (messageBase != null) {
                     // 消息处理
                 }
         
             }
         };
    

    到这里,一个ADIL流程就走完了。

而且还顺便做了一个 Remote Service 的实现。

当然了,要作为一个remote Service ,还需要进行 AndroidMenifest.xml 的配置

  <service android:name=".xmpp.XMPPService"
              android:enabled="true"
              android:exported="true"
              android:process=":xmpp"/>
 
comments powered by Disqus