所有涉及到包名的地方 全部应该要 一致(包括aidl文件和java文件),最终实现 remote XMPPService
包名假定为: xyz.jimbray.xmpp
编写 AIDL 相关文件(包括自定义类型数据java)
- 新建 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类
- 新建 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个文件
- IXMPPServiceInterface.aidl
- IXMPPReceiveListenerCallback.aidl
- XMPPMessageBase.aidl
- XMPPMessageBase.java
编写 服务端 java 代码
- 新建 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) { } } } }
到这里,服务端就完成了。
- 编写客户端 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