zoukankan      html  css  js  c++  java
  • 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:sdkuild-tools25.0.2aidl.exe with arguments {-pD:sdkplatformsandroid-25framework.aidl -oD:AndroidProgramAIDLDemoaidlserviceuildgeneratedsourceaidldebug -ID:AndroidProgramAIDLDemoaidlservicesrcmainaidl
    -ID:AndroidProgramAIDLDemoaidlservicesrcdebugaidl -IC:UsersJackMa.androiduild-cache2e6503c935d0e7aae874a51ce291501bd59fa172outputaidl -IC:UsersJackMa.androiduild-cachea228985c661fd225e06c74ab4fc1d2ba693ed5d6outputaidl
    -IC:UsersJackMa.androiduild-cache4bf36b1eaa6e79d64acf7cc2ed0353331a0adb5outputaidl -IC:UsersJackMa.androiduild-cachec304484ccb4b4195fb676627e57f7ba92b2bd5eoutputaidl -IC:UsersJackMa.androiduild-cache7915f8984a205b1822ca99eaa0be940f26c250eoutputaidl
    -IC:UsersJackMa.androiduild-cachea3d0bacf6fdfaf349f8eda4b777bd602fa7e5c67outputaidl
    -IC:UsersJackMa.androiduild-cache1d232803c59995c7bfdd0a651a144487a3f5db22outputaidl -IC:UsersJackMa.androiduild-cachee18e71aa63be1e08bcc427a44b82a10769bbf421outputaidl
    -IC:UsersJackMa.androiduild-cache4d089e6ce66be1802231fc929e45b8b592cfa5c5outputaidl -IC:UsersJackMa.androiduild-cache57b6aa97fbde28ff421267076f032edb60508e5foutputaidl
    -dC:UsersJackMaAppDataLocalTempaidl4840819892296516909.d D:AndroidProgramAIDLDemoaidlservicesrcmainaidlcomzhangmiaoaidlserviceMessageManager.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

  • 相关阅读:
    rgbdslam 源代码的实现
    键值对排序并MD5加密
    字符编码
    排序算法
    Bridge Pattern (桥接模式)
    Visitor Pattern 和 doubledispatch
    栈、队列、链表
    如何使用visio
    架构师论文
    英语写作句子
  • 原文地址:https://www.cnblogs.com/zhangmiao14/p/6769113.html
Copyright © 2011-2022 走看看