AIDL 消息通信
16lz
2021-01-26
AIDL 消息通信
- 操作步骤
- 创建aidl文件
- 创建服务
- 请求服务
AIDL(Android Interface Definition Language) 即Android 接口定义语言,是用来实现不同进程间通信的。AIDL同时也是另外两种进程通信方式Messager和ContentProvider的底层实现方法,所以了解aidl的使用显得尤为重要。
本案例可在 Github 获取到Demo源码。
操作步骤
AIDL通信的实现是需要客户端和服务端的配合,具体的实现过程如下:
创建aidl文件
aidl文件是客户端请求服务端操作获取交互信息的基础,只需要在Android Studio的src/main目录下创建一个aidl目录(或者也可以在gradle中指定aidl位置),然后在该目录下创建一个aidl文件,android studio会自动生成一个包名并将aidl放在该包下。
// ITestInterface.aidlpackage com.kubo.aidlproject;import com.kubo.aidlproject.TestData;// Declare any non-default types here with import statements//设置客户端调用的接口interface ITestInterface { /** * Demonstrates some basic types that you can use as parameters * and return values in AIDL. */ void sendTest(in TestData testData); void sendTest2(out TestData testData,int a); void sendTest3(inout TestData testData,String a);}
如上所示,aidl支持如下如下数据传递:
- Java所有的基本数据类型(int、long、short等)
- String和CharSequence
- List 子项中也必须是aidl支持的类型
- Map 子项中也必须是aidl支持的类型
- AIDL aidl本省也可作为传递的类型
- Parcelable 所有实现Parcelable接口的对象
AIDL中除了基本类型外其他类型需要设置数据流的方向(in、out,inout):
- in
in 代表为输入流方向,即client可修改,service端修改无效 - out
out 代表输出流方向,即service端可修改,client修改无效 - inout
inout代表双向流,即service和client端均可对其进行修改
如上,创建的aidl需要传递的参数TestData:
package com.kubo.aidlproject;import android.os.Parcel;import android.os.Parcelable;/** * * 测试数据序列化 * @author hfcai */public class TestData implements Parcelable { private int id; private String name; @Override public String toString() { return "TestData{" + "id=" + id + ", name='" + name + '\'' + ", tips='" + tips + '\'' + ", can=" + can + '}'; } private String tips; private boolean can; public TestData(){} public TestData(int id, String name, String tips, boolean can) { this.id = id; this.name = name; this.tips = tips; this.can = can; } protected TestData(Parcel in) { id = in.readInt(); name = in.readString(); tips = in.readString(); can = in.readByte()!=0; } public void readFromParcel(Parcel in) { id = in.readInt(); name = in.readString(); tips = in.readString(); can = in.readByte()!=0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); dest.writeString(name); dest.writeString(tips); dest.writeByte((byte) (can?1:0)); } @Override public int describeContents() { return 0; } public static final Creator CREATOR = new Creator() { @Override public TestData createFromParcel(Parcel in) { return new TestData(in); } @Override public TestData[] newArray(int size) { return new TestData[size]; } }; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isCan() { return can; } public void setCan(boolean can) { this.can = can; } public String getTips() { return tips; } public void setTips(String tips) { this.tips = tips; }}
同时还需要在aidl中声明一个parcelable对象:
//TestData .aidlpackage com.kubo.aidlproject;// Declare any non-default types here with import statementsparcelable TestData;
创建完aidl后,android sdk工具可将其自动转化为对应的继承IInterface的接口,其实也就是生成一个binder的过程,如上aidl对应的接口如下:
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.kubo.aidlproject;import android.os.Binder;import android.os.IBinder;import android.os.IInterface;import android.os.Parcel;import android.os.RemoteException;public interface ITestInterface extends IInterface { void sendTest(TestData var1) throws RemoteException; void sendTest2(TestData var1, int var2) throws RemoteException; void sendTest3(TestData var1, String var2) throws RemoteException; public abstract static class Stub extends Binder implements ITestInterface { private static final String DESCRIPTOR = "com.kubo.aidlproject.ITestInterface"; static final int TRANSACTION_sendTest = 1; static final int TRANSACTION_sendTest2 = 2; static final int TRANSACTION_sendTest3 = 3; public Stub() { this.attachInterface(this, "com.kubo.aidlproject.ITestInterface"); } public static ITestInterface asInterface(IBinder obj) { if (obj == null) { return null; } else { IInterface iin = obj.queryLocalInterface("com.kubo.aidlproject.ITestInterface"); return (ITestInterface)(iin != null && iin instanceof ITestInterface ? (ITestInterface)iin : new ITestInterface.Stub.Proxy(obj)); } } public IBinder asBinder() { return this; } public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { String descriptor = "com.kubo.aidlproject.ITestInterface"; TestData _arg0; switch(code) { case 1: data.enforceInterface(descriptor); if (0 != data.readInt()) { _arg0 = (TestData)TestData.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.sendTest(_arg0); reply.writeNoException(); return true; case 2: data.enforceInterface(descriptor); _arg0 = new TestData(); int _arg1 = data.readInt(); this.sendTest2(_arg0, _arg1); reply.writeNoException(); if (_arg0 != null) { reply.writeInt(1); _arg0.writeToParcel(reply, 1); } else { reply.writeInt(0); } return true; case 3: data.enforceInterface(descriptor); if (0 != data.readInt()) { _arg0 = (TestData)TestData.CREATOR.createFromParcel(data); } else { _arg0 = null; } String _arg1 = data.readString(); this.sendTest3(_arg0, _arg1); reply.writeNoException(); if (_arg0 != null) { reply.writeInt(1); _arg0.writeToParcel(reply, 1); } else { reply.writeInt(0); } return true; case 1598968902: reply.writeString(descriptor); return true; default: return super.onTransact(code, data, reply, flags); } } private static class Proxy implements ITestInterface { private IBinder mRemote; Proxy(IBinder remote) { this.mRemote = remote; } public IBinder asBinder() { return this.mRemote; } public String getInterfaceDescriptor() { return "com.kubo.aidlproject.ITestInterface"; } public void sendTest(TestData testData) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken("com.kubo.aidlproject.ITestInterface"); if (testData != null) { _data.writeInt(1); testData.writeToParcel(_data, 0); } else { _data.writeInt(0); } this.mRemote.transact(1, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } public void sendTest2(TestData testData, int a) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken("com.kubo.aidlproject.ITestInterface"); _data.writeInt(a); this.mRemote.transact(2, _data, _reply, 0); _reply.readException(); if (0 != _reply.readInt()) { testData.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } public void sendTest3(TestData testData, String a) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { _data.writeInterfaceToken("com.kubo.aidlproject.ITestInterface"); if (testData != null) { _data.writeInt(1); testData.writeToParcel(_data, 0); } else { _data.writeInt(0); } _data.writeString(a); this.mRemote.transact(3, _data, _reply, 0); _reply.readException(); if (0 != _reply.readInt()) { testData.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); } } } }}
创建服务
如上完成了aidl的创建后还需要创建提供服务的服务类,从而实现提供服务的目的,如下:
public class AIDLService extends Service { private static final String TAG = "AIDLService"; ITestAidlStub stub = new ITestAidlStub(); @Override public void onCreate() { super.onCreate(); Log.e(TAG,"-----------创建AIDL服务-------------"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG,"----------------onStartCommand-----------------"); return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { Log.e(TAG,"----------------------onDestroy---------------"); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { Log.e(TAG,"----------------------onBind---------------"); return stub; }}public class ITestAidlStub extends ITestInterface.Stub { private static final String TAG = "ITestAidlStub"; @Override public void sendTest(TestData testData) throws RemoteException { Log.e(TAG,"sendTest,testData is in:"+testData.toString()); testData.setName("test_service"); } @Override public void sendTest2(TestData testData, int a) throws RemoteException { Log.e(TAG,"sendTest,testData is out:"+testData.toString()); testData.setName("test_service"); } @Override public void sendTest3(TestData testData, String a) throws RemoteException { Log.e(TAG,"sendTest,testData is inout:"+testData.toString()); testData.setName("test_service"); }}
完成服务的编写还得需要在manifest中注册该服务:
由于本次是为了测试多进程通信,且是在同一个app中,所以就需要我们为这个服务单独开一个进程
请求服务
服务创建好后,我们就可以在我们app的主进程中创建一个服务连接并通过bindService绑定服务了。
public class MainActivity extends AppCompatActivity { ITestInterface iTestInterface; /** * 连接服务 */ private ServiceConnection serviceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { iTestInterface = ITestAidlStub.asInterface(service); Log.e("MainActivity","onServiceConnected"); } /** * service异常终止 * @param name */ @Override public void onServiceDisconnected(ComponentName name) { iTestInterface = null; Log.e("MainActivity","onServiceDisconnected"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onClick(View view){ TestData testData = new TestData(0,"hfcai","注意提示!",true); switch (view.getId()){ case R.id.bind: //绑定服务 Intent intent = new Intent(this,AIDLService.class); bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE); break; case R.id.send_in: //发送测试数据 if (iTestInterface!=null){ try { iTestInterface.sendTest(testData); Log.e("in","testData:"+testData.toString()); } catch (RemoteException e) { e.printStackTrace(); Log.e("in error",e.getMessage()); } }else { Toast.makeText(this,"连接服务失败!",Toast.LENGTH_SHORT).show(); } break; case R.id.send_out: //发送测试数据 if (iTestInterface!=null){ try { iTestInterface.sendTest2(testData,0); Log.e("out","testData:"+testData.toString()); } catch (RemoteException e) { e.printStackTrace(); Log.e("out error",e.getMessage()); } }else { Toast.makeText(this,"连接服务失败!",Toast.LENGTH_SHORT).show(); } break; case R.id.send_inout: //发送测试数据 if (iTestInterface!=null){ try { iTestInterface.sendTest3(testData,"inout"); Log.e("inout","testData:"+testData.toString()); } catch (RemoteException e) { e.printStackTrace(); Log.e("inout error",e.getMessage()); } }else { Toast.makeText(this,"连接服务失败!",Toast.LENGTH_SHORT).show(); } break; default: break; } }}
这样就完成了一个简单的Demo实现。
更多相关文章
- tcping测试服务器TCP端口
- Android(安卓)进程间通讯之通过Intent+bundle实现跨进程通讯
- 详解Android提交数据到服务器的两种方式四种方法
- 模拟器中加载和使用SDCard卡
- Android(安卓)Service 的一些笔记
- Android客户端和服务器端数据交互的第二种方法
- 最全面的Android(安卓)Studio使用教程
- 使用TestProject Python SDK创建移动Appium测试
- Android(安卓)弹出式布局之Dialog源码分析