Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Studio中使用AIDL進行進程間通信

Android Studio中使用AIDL進行進程間通信

編輯:關於Android編程

什麼是AIDL
aidl是 Android Interface definition language的縮寫,也就是安卓接口定義語言
為什麼要有AIDL
AIDL允許你定義客戶端與服務端達成一致的程序接口使用進程間通信相互交流。 在Android上面,一個進程不能正常的訪問另一個進程的內存。 所以說,他們需要分解他們的對象為操作系統可以理解的基本單位,然後為你把這些對象按次序跨越進程邊界 ,書寫這些代碼是單調冗長的,所以android使用AIDL為你處理這個問題。

注意:使用AIDL只有在你允許來自不同應用的客戶端跨進程通信訪問你的service,並且想要在你的service種處理多線程的時候才是必要的。 如果你不需要執行不同應用之間的IPC並發,你應該通過實現Binder建立你的接口,或者如果你想執行IPC,但是不需要處理多線程。那麼使用Messenger實現你的接口。

如何使用aidl完成進程間通信
1.建立.aidl文件
AIDL使用一個簡單的語法讓你聲明一個帶有一個或者多個帶有參數和返回值方法的接口 參數和返回值可以是任何類型,甚至是AIDL生成的接口

你必須使用java語言構建.aidl文件 每一個.aidl文件必須定義一個簡單的接口並且要求只有接口聲明和方法簽名

默認的,AIDL支持下面數據類型

ava語言中的所有基本數據類型(比如int、long、char、boolean等等)

String CharSequence List
List中的所有元素必須是AIDL支持的類型之一,或者是一個其他AIDL生成的接口,或者是你定義的parcelable List可以使用范型(例如,List) 接收端的實際類經常是一個ArrayList,盡管方法是使用List接口生成的 Map
Map中的所有元素必須是AIDL支持的類型之一,或者是一個其他AIDL生成的接口,或者是你定義的parcelable 范型map是不被支持的(比如這種形式Map) 接收端的實際類經常是一個HashMap,盡管方法是使用Map接口生成的

當定義你的service接口的時候,注意:

方法可以接收0或多個參數,並且有返回值或者返回void 所有非基本數據類型要求要求一個定向的tag來指定數據是去往哪個方向的 無論是輸入、輸出,還是輸入輸出(參加下面的例子) 基本數據類型是默認支持的,並且不能是其他的。 .aidl文件中的所有的代碼注釋都在生成的IBinder接口中(除了在import和包聲明之前的注釋) 只支持方法,你不可以在AIDL暴露靜態域

下面給出代碼
以Android Stduio為例,我們看看如何使用AIDL進行進程間通信
1.創建IRemoteService .aidl文件
這裡寫圖片描述

這裡寫圖片描述

package com.example.aidlserver;


interface IRemoteService {
     int getPid();
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

但是此時並沒有AIDL的java文件產生,其實android studio也是帶有自動生成的,只不過需要確認一些信息後才能生成。此時,我們可以在目錄 build–>generated–>source–>aidl–>test–>debug下面發現還沒有任何文件

這裡寫圖片描述

此時,打開AndroidManifest.xml,確認package的值,如我這個<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">

關鍵性的一步,確認aidl文件所在的包名和AndroidMainifest.xml的package名是否一致。如果一致,點擊 Build–>Make Project,生成相應的java文件。如果不一致,則改aidl的包名,改成一致,再點擊生成,生成效果如圖。
這裡寫圖片描述

我們看看生成的IRemoteService.java的內容

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.aidlserver.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.example.aidlserver.IRemoteService";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.aidlserver.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.example.aidlserver.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidlserver.IRemoteService))) {
                return ((com.example.aidlserver.IRemoteService) iin);
            }
            return new com.example.aidlserver.IRemoteService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getPid: {
                    data.enforceInterface(DESCRIPTOR);
                    int _result = this.getPid();
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.aidlserver.IRemoteService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public int getPid() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public int getPid() throws android.os.RemoteException;

    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}

我們只看重點部分,這裡有一個內部類Stub繼承Binder,實現了IRemoteService接口,這個類中有一個方法asInterface,接收一個IBinder對象,並且返回一個IRemoteService類型的對象。

定義遠程服務,暴漏接口給客戶端

public class RemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int getPid() throws RemoteException {
            return Process.myPid();
        }
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    };
}

同時,我們需要在清單文件中注冊這個服務,並且添加一個action,因為我們是通過遠程來調用服務的,所以必須指定一個action,用來隱式啟動。

  
            
                
            
        

現在,當一個客戶端(比如一個activity)調用bindService()來連接到這個service,這個客戶端的onServiceConnected()回調函數接收service中onBind()方法返回的mBinder實例

客戶端必須可以訪問接口類,所以如果客戶端和服務端在不同的應用中,那麼客戶端所在的應用必須有一份.aidl文件的副本,同樣,在客戶端我們創建一個aidl文件夾,然後把服務器端的aidl文件拷貝到這個目錄下,注意:這個時候要保證文件所在的包名和服務器端aidl的包名一樣,而不是和當前應用的包名一樣,否則會報錯。

當客戶端在onServiceConnected()回調方法中接收到IBinder時,它必須調用你的ServiceInterface.Stub.asInterface(service)來把返回參數映射到你的ServiceInterface類型上。例如:

  private IRemoteService remoteService;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = remoteService.getPid();
                int currentPid = Process.myPid();
                System.out.println("currentPid: "+currentPid+", remotePid: "+pid);
                remoteService.basicTypes(1,12,true,3,3,"aa");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("=====bind Success");
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(MainActivity.this, "service disConneted unexpected", Toast.LENGTH_SHORT).show();
            remoteService = null;
        }
    };

完整代碼
服務端:

public class RemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public int getPid() throws RemoteException {
            System.out.println("=====Thread getPid: "+Thread.currentThread().getName());
            return Process.myPid();
        }
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            System.out.println("=====Thread basicTypes: "+Thread.currentThread().getName());
        }
    };
}

客戶端:

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void bindService(View view){
        System.out.println("=====begin bindService");
        Intent service = new Intent("com.lxn.remote");
        //通過bindService綁定服務
        bindService(service,conn,BIND_AUTO_CREATE);
    }
    private IRemoteService remoteService;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            remoteService = IRemoteService.Stub.asInterface(service);
            try {
                int pid = remoteService.getPid();
                int currentPid = Process.myPid();
                System.out.println("===currentPid: "+currentPid+", remotePid: "+pid);
                remoteService.basicTypes(1,12,true,3,3,"aa");
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            System.out.println("=====bind Success");
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Toast.makeText(MainActivity.this, "service disConneted unexpected", Toast.LENGTH_SHORT).show();
            remoteService = null;
        }
    };
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

這裡寫圖片描述

我們點擊客戶端的綁定服務按鈕,然後在控制台進行結果的輸出

這裡寫圖片描述

那麼AIDL是什麼原理呢?
AIDL其實通過我們寫的aidl文件,幫助我們生成了一個接口,一個Stub類用於服務端,一個Proxy類用於客戶端調用。關於詳細的細節,我在這裡就不討論了

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved