首页 > 代码库 > Android之AIDL知识总结

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.aidlpackage com.zhangmiao.aidlservice;// Declare any non-default types here with import statementsparcelable 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.aidlpackage com.zhangmiao.aidlservice;// Declare any non-default types here with import statementsimport 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.aidlpackage com.zhangmiao.aidlservice;// Declare any non-default types here with import statementsparcelable 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()方法:

@Overridepublic 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 statementsparcelable 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 statementsimport com.zhangmiao.aidlservice.MessageSub;interface MessageManager {    List<MessageSub> getMessages();    void addMessage(in MessageSub message);}

3.2.3.修改服务器端AIDL的onCreate()方法。

@Overridepublic 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则表示两端可双向流通数据。

4.2. 客户端与服务器端的aidl与aidl实现的java文件要保持一致,包名也要相同。

4.3.在类实现Parcelable的接口时,在writeToParcel()与构造函数参数为Parcel的两个方法中,调用parcel的写与读数据的顺序要一致。

参考文献:

http://www.open-open.com/lib/view/open1469493649028.html

http://www.open-open.com/lib/view/open1469494852171.html

Android之AIDL知识总结