Android之AIDL知识总结
1.AIDL介绍
AIDL是一个缩写,全称是Android Interface Definition Language,翻译为Android接口定义语言。主要用于线程之间的通信,本文主要以不同应用之间使用AIDL通信为例介绍AIDL。
2.AIDL的使用
AIDL的使用按照AIDL文件类型分类,一种是序列化数据类,需要实现Parcelable,另一种是定义方法接口,以供系统使用来完成跨进程通信的。
AIDL默认支持JAVA的八种基本数据类型、String、CharSequence、List类型、Map类型。
2.1. 数据类实现Parcelable
Message.aidl文件
// Message.aidl package com.zhangmiao.aidlservice; // Declare any non-default types here with import statements parcelable Message;
Message.java文件
package com.zhangmiao.aidlservice; import android.os.Parcel; import android.os.Parcelable; /** * Created by zhangmiao on 2017/4/19. */ public class Message implements Parcelable { protected int status; protected String content; public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Message(int status, String content){ this.status = status; this.content = content; } public Message(Parcel parcel){ status = parcel.readInt(); content = parcel.readString(); } public final static Creator<Message> CREATOR = new Creator<Message>() { @Override public Message createFromParcel(Parcel source) { return new Message(source); } @Override public Message[] newArray(int size) { return new Message[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(status); dest.writeString(content); } @Override public String toString() { return "status : "+status + ",content : "+content; } }
2.2.定义方法接口
MessageManager.aidl文件
// MessageManager.aidl package com.zhangmiao.aidlservice; // Declare any non-default types here with import statements import com.zhangmiao.aidlservice.Message; interface MessageManager { List<Message> getMessages(); void addMessage(in Message message); }
2.3.移植aidl与java文件
将服务端的Message.aidl、MessageManager.aidl与Message.java文件移植到客户端项目中,注意文件的包名要保持一致,不可不同。下图是项目结构图。
2.4.编写服务器代码
package com.zhangmiao.aidlservice; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.support.annotation.Nullable; import java.util.ArrayList; import java.util.List; /** * Created by zhangmiao on 2017/4/19. */ public class AIDLService extends Service { private final static String TAG = AIDLService.class.getSimpleName(); private List<Message> mMessageList = new ArrayList<>(); private final MessageManager.Stub messageService = new MessageManager.Stub() { @Override public List<Message> getMessages() throws RemoteException { if(mMessageList != null) { return mMessageList; } return new ArrayList<>(); } @Override public void addMessage(Message message) throws RemoteException { if(mMessageList == null){ mMessageList = new ArrayList<>(); } mMessageList.add(message); } }; @Override public void onCreate() { super.onCreate(); Message message1 = new Message(1,"open"); mMessageList.add(message1); Message message2 = new Message(2,"get"); mMessageList.add(message2); } @Nullable @Override public IBinder onBind(Intent intent) { return messageService; } }
修改服务器的AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.zhangmiao.aidlservice"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".AIDLService" android:exported="true"> <intent-filter> <action android:name="com.zhangmiao.service.aidlservice" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application> </manifest>
2.5.编写客户端代码
package com.zhangmiao.aidldemo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.util.Log; import com.zhangmiao.aidlservice.Message; import com.zhangmiao.aidlservice.MessageManager; import java.util.List; public class MainActivity extends AppCompatActivity { private final static String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(); intent.setPackage("com.zhangmiao.aidlservice"); intent.setAction("com.zhangmiao.service.aidlservice"); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG,"ServiceConnection onServiceConnected"); MessageManager messageManager = MessageManager.Stub.asInterface(service); try { List<Message> messages = messageManager.getMessages(); for(int i = 0;i<messages.size();i++){ Log.d(TAG,"get Message for MessageManager:"+messages.get(i).toString()); } }catch (RemoteException e){ e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG,"ServiceConnection onServiceDisconnected"); } }; }
运行结果如图:
3.AIDL继承
在AIDL的序列化数据类的时候,有时候数据类有很多,并且是有继承关系的,这个时候直接使用是有问题的,需要处理一下。
3.1.错误做法
3.1.1.添加ConnectMessage类
ConnectMessage.aidl文件
// ConnectMessage.aidl package com.zhangmiao.aidlservice; // Declare any non-default types here with import statements parcelable ConnectMessage;
ConnectMessage.java文件
package com.zhangmiao.aidlservice; import android.os.Parcel; /** * Created by zhangmiao on 2017/4/25. */ public class ConnectMessage extends Message { protected int isConnect; public ConnectMessage(int status,String content,int isConnect){ super(status,content); this.isConnect = isConnect; } public ConnectMessage(Parcel parcel){ super(parcel); isConnect = parcel.readInt(); } public final static Creator<ConnectMessage> CREATOR = new Creator<ConnectMessage>() { @Override public ConnectMessage createFromParcel(Parcel source) { return new ConnectMessage(source); } @Override public ConnectMessage[] newArray(int size) { return new ConnectMessage[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(status); dest.writeString(content); dest.writeInt(isConnect); } @Override public String toString() { return "status : "+status + ",content : "+content+",isConnect : "+isConnect; } }
注意:客户端与服务端ConnectMessage的aidl文件与java文件都需要添加。
3.1.2 在服务端发送ConnnectMessage消息。
修改AIDLService类中的onCreate()方法:
@Override public void onCreate() { super.onCreate(); Message message1 = new Message(1,"open"); mMessageList.add(message1); Message message2 = new Message(2,"get"); mMessageList.add(message2); Message connMessage = new ConnectMessage(3,"conn",1); mMessageList.add(connMessage); }
3.1.3.在客户端接收ConnnectMessage消息。
修改客户端的MainActivity的ServiceConnect对象的实现。
private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG,"ServiceConnection onServiceConnected"); MessageManager messageManager = MessageManager.Stub.asInterface(service); try { List<Message> messages = messageManager.getMessages(); for(int i = 0;i<messages.size();i++){ if(messages.get(i).getStatus() == 3){ ConnectMessage connectMessage = (ConnectMessage)message; Log.d(TAG,"get conn Message for MessageManager:"+connectMessage.toString()); break; } Log.d(TAG,"get Message for MessageManager:"+message.toString()); } }catch (RemoteException e){ e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG,"ServiceConnection onServiceDisconnected"); } };
这个时候运行就会出现如下错误:
04-26 11:43:20.998 2567-2567/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.zhangmiao.aidldemo, PID: 2567 java.lang.ClassCastException: com.zhangmiao.aidlservice.Message cannot be cast to com.zhangmiao.aidlservice.ConnectMessage
at com.zhangmiao.aidldemo.MainActivity$1.onServiceConnected(MainActivity.java:42)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1101)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1118)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5095)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)
3.2.正确做法
在java中,用子类对象初始化父类,可以将父类强制转换成引用的子类,这样使用是没有问题的,但是在AIDL使用中,在服务器端用子类对象初始化父类,在服务器端将父类强制转换成引用的子类这样是没有问题的,但是在客户端这样强制转换时不可以的,因为客户端只是将父类当做父类,如果还是想要在客户端进行父类强制转换成引用的子类,可以借助序列化中的parcel对象进行实现。
实现方法如下:
3.2.1.实现一个Message的替换类MessageSub类。
MessageSub.aidl文件:
package com.zhangmiao.aidlservice; // Declare any non-default types here with import statements parcelable MessageSub;
MessageSub.java文件:
package com.zhangmiao.aidlservice; import android.os.Parcel; import android.os.Parcelable; /** * Created by zhangmiao on 2017/4/26. */ public class MessageSub implements Parcelable { private Message message; private String messageType; public static final String MESSAGE_TYPE_MESSAGE = "message"; public static final String MESSAGE_TYPE_CONNECT = "connect"; public Message getMessage() { return message; } public void setMessage(Message message) { this.message = message; } public String getMessageType() { return messageType; } public void setMessageType(String messageType) { this.messageType = messageType; } public MessageSub(Message message, String messageType) { this.message = message; this.messageType = messageType; } public MessageSub(Parcel parcel) { messageType = parcel.readString(); switch (messageType) { case MESSAGE_TYPE_CONNECT: message = parcel.readParcelable(ConnectMessage.class.getClassLoader()); break; default: message = parcel.readParcelable(Message.class.getClassLoader()); break; } } public final static Creator<MessageSub> CREATOR = new Creator<MessageSub>() { @Override public MessageSub createFromParcel(Parcel source) { return new MessageSub(source); } @Override public MessageSub[] newArray(int size) { return new MessageSub[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(messageType); dest.writeParcelable(message,flags); } }
3.2.2修改MessageManager.aidl文件。
将添加的信息修改为MessageSub。
package com.zhangmiao.aidlservice; // Declare any non-default types here with import statements import com.zhangmiao.aidlservice.MessageSub; interface MessageManager { List<MessageSub> getMessages(); void addMessage(in MessageSub message); }
3.2.3.修改服务器端AIDL的onCreate()方法。
@Override public void onCreate() { super.onCreate(); Message message1 = new Message(1,"open"); MessageSub messageSub1 = new MessageSub(message1,MessageSub.MESSAGE_TYPE_MESSAGE); mMessageList.add(messageSub1); Message message2 = new Message(2,"get"); MessageSub messageSub2 = new MessageSub(message2,MessageSub.MESSAGE_TYPE_MESSAGE); mMessageList.add(messageSub2); Message connMessage = new ConnectMessage(3,"conn",1); MessageSub messageSub3 = new MessageSub(connMessage,MessageSub.MESSAGE_TYPE_CONNECT); mMessageList.add(messageSub3); }
3.2.4.修改客户端的MainActivity的ServiceConnect对象的实现。
private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.d(TAG,"ServiceConnection onServiceConnected"); MessageManager messageManager = MessageManager.Stub.asInterface(service); try { List<MessageSub> messages = messageManager.getMessages(); for(int i = 0;i<messages.size();i++){ String messageType = messages.get(i).getMessageType(); Message message = messages.get(i).getMessage(); switch (messageType){ case MessageSub.MESSAGE_TYPE_MESSAGE: Log.d(TAG,"get Message for MessageManager:"+message.toString()); break; case MessageSub.MESSAGE_TYPE_CONNECT: ConnectMessage connectMessage = (ConnectMessage)message; Log.d(TAG,"get conn Message for MessageManager:"+connectMessage.toString()); break; default: Log.d(TAG,"get Message for MessageManager:"+message.toString()); break; } } }catch (RemoteException e){ e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG,"ServiceConnection onServiceDisconnected"); } };
显示结果如下图:
4.AIDL使用的注意事项
4.1. 在接口中将序列化的数据类作为接口的参数时,需要添加in/out/inout,不然会如下错误:
Error:Execution failed for task ':aidlservice:compileDebugAidl'.
> java.lang.RuntimeException: com.android.ide.common.process.ProcessException:
Error while executing process D:\sdk\build-tools\25.0.2\aidl.exe with arguments {-pD:\sdk\platforms\android-25\framework.aidl -oD:\AndroidProgram\AIDLDemo\aidlservice\build\generated\source\aidl\debug -ID:\AndroidProgram\AIDLDemo\aidlservice\src\main\aidl
-ID:\AndroidProgram\AIDLDemo\aidlservice\src\debug\aidl -IC:\Users\JackMa\.android\build-cache\2e6503c935d0e7aae874a51ce291501bd59fa172\output\aidl -IC:\Users\JackMa\.android\build-cache\a228985c661fd225e06c74ab4fc1d2ba693ed5d6\output\aidl
-IC:\Users\JackMa\.android\build-cache\04bf36b1eaa6e79d64acf7cc2ed0353331a0adb5\output\aidl -IC:\Users\JackMa\.android\build-cache\0c304484ccb4b4195fb676627e57f7ba92b2bd5e\output\aidl -IC:\Users\JackMa\.android\build-cache\07915f8984a205b1822ca99eaa0be940f26c250e\output\aidl
-IC:\Users\JackMa\.android\build-cache\a3d0bacf6fdfaf349f8eda4b777bd602fa7e5c67\output\aidl
-IC:\Users\JackMa\.android\build-cache\1d232803c59995c7bfdd0a651a144487a3f5db22\output\aidl -IC:\Users\JackMa\.android\build-cache\e18e71aa63be1e08bcc427a44b82a10769bbf421\output\aidl
-IC:\Users\JackMa\.android\build-cache\4d089e6ce66be1802231fc929e45b8b592cfa5c5\output\aidl -IC:\Users\JackMa\.android\build-cache\57b6aa97fbde28ff421267076f032edb60508e5f\output\aidl
-dC:\Users\JackMa\AppData\Local\Temp\aidl4840819892296516909.d D:\AndroidProgram\AIDLDemo\aidlservice\src\main\aidl\com\zhangmiao\aidlservice\MessageManager.aidl}
In、out、inout指的是数据流通的方式,in与out分别表示客户端与服务器之间的两条单向的数据流向,inout则表示两端可双向流通数据。