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.aidl
package 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中除了基本類型外其他類型需要設置數據流的方向(inout,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<TestData> CREATOR = new Creator<TestData>() {
        @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 .aidl
package com.kubo.aidlproject;

// Declare any non-default types here with import statements
parcelable 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中註冊該服務:

    <service android:name=".AIDLService"
            android:exported="true"
            android:process=":test"/>

由於本次是爲了測試多進程通信,且是在同一個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實現。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章