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則表示兩端可雙向流通數據。

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM