1. 程式人生 > >AIDL 安卓程序間通訊/跨應用通訊

AIDL 安卓程序間通訊/跨應用通訊

前言

最近出去面試找工作,被人問到AIDL,我就回答這個東西我用過,也大概理解,Android的程序間通訊語言嘛,人家不置可否,那我能咋著呢,畢竟沒深入研究過,也沒辦法,咱只能回來奮發圖強了

寫在前面

我以前就看過的一個部落格,裡面原理程式碼什麼都有,寫的水平肯定比我高
Android開發者指南(6) —— AIDL

首先字面解釋

A=Android
IDL=Interface definition language
意譯就是android介面定義語言,馬丹,完全看不明白
算了,就是Android官方給我們定義出來跨程序,甚至跨應用通訊用的

開發平臺

Android Studio 2.2+Android手機一部

新建工程

這個就不說了,跳過
就是新建工程後再建一個module 也是android app,功能後面再說

aidl語法

這裡請去看我寫在前面,裡面比較詳細,或者自行baidu/google,我也瞭解的不多

程式碼示例

最關鍵的地方到了
這裡寫圖片描述
其實就是新建一個aidl檔案

// IMyAidlInterface.aidl
package com.kikt.aidl;

// Declare any non-default types here with import statements

interface
IMyAidlInterface {
void test(int sum,int sum2); }

接著make project,生成下java程式碼
找到生成的程式碼看下
我靠 好複雜,還是渣格式,這裡格式化一下:

不想看完全程式碼的可以看下後面的結構圖

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: H:\\Git\\ASWorkSpace\\public\\AidlDemo\\app\\src\\main\\aidl\\com\\kikt\\aidl\\IMyAidlInterface.aidl
 */
package com.kikt.aidl; // Declare any non-default types here with import statements public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.kikt.aidl.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.kikt.aidl.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.kikt.aidl.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.kikt.aidl.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.kikt.aidl.IMyAidlInterface))) { return ((com.kikt.aidl.IMyAidlInterface) iin); } return new com.kikt.aidl.IMyAidlInterface.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_test: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); this.test(_arg0, _arg1); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.kikt.aidl.IMyAidlInterface { 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 void test(int sum, int sum2) 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(sum); _data.writeInt(sum2); mRemote.transact(Stub.TRANSACTION_test, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_test = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void test(int sum, int sum2) throws android.os.RemoteException; }

這裡寫圖片描述

這裡生成了一個靜態內部類Stub
方法簽名如下

public static abstract class Stub extends android.os.Binder implements com.kikt.aidl.IMyAidlInterface

這個類是抽象類 這裡類本身實現了對應的介面IMyAidlInterface,但沒有實現我們在aidl中定義的方法test(int,int)方法
這也代表了test方法需要我們自己來實現

另外方法繼承了android.os.Binder,看到這個的時候,我們就知道,這個應該用在什麼地方了
對了,和Service必須實現的那個介面onBind的返回引數一樣!

這裡寫個類繼承下

package com.kikt.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.kikt.aidl.IMyAidlInterface;

public class MyService extends Service {
    private static final String TAG = "MyService";
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
       return new AidlEntity();
    }

    public class AidlEntity extends IMyAidlInterface.Stub{

        @Override
        public void test(int sum, int sum2) throws RemoteException {
            Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
        }
    }
}

為了方便,直接寫在service內部
這裡只是簡單的做一個日誌輸出,其他什麼也沒有幹

同應用呼叫

這裡是同應用呼叫

直接看核心程式碼

bindService(new Intent(this, MyService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
                try {
                    test.test(1, 2);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

這裡就是一個按鈕點選後bindService,然後connection會接受到一個IBinder的例項,這個例項就是上面service的那個

當然還有一個寫法,直接強轉也不會報錯

 bindService(new Intent(this, MyService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
//                IMyAidlInterface test = IMyAidlInterface.Stub.asInterface(service);
//                try {
//                    test.test(1, 2);
//                } catch (RemoteException e) {
//                    e.printStackTrace();
//                }
                MyService.AidlEntity service1 = (MyService.AidlEntity) service;
                try {
                    service1.test(4,5);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

效果都是一樣的,那麼這個什麼aidl還有個毛用啊,強轉就好了

接著就是高潮了

跨應用呼叫

沒錯,我個人認為最重要的也就是這點了,aidl可以在不同應用中呼叫
比如我要做一個提供服務的應用,或者說是同公司的核心元件,而不希望別人知道我的具體實現
這時我直接丟給你我的aidl檔案,告訴你包名和服務名稱,你就去調就好了!

ComponentName componentName = new ComponentName(
                "com.kikt.aidldemo", "com.kikt.aidldemo.MyService");
        Intent intent = new Intent();
        intent.setComponent(componentName);

        bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
                try {
                    iinterface.test(10, 20);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

這裡和上面的程式碼看上去差不多,但是我和原來的不是一個應用,而是第二個應用
這裡使用test方法時,可以看到提供服務的應用可以輸出日誌

而引申下
如果這裡有一個演算法,實現了兩個引數間的計算,那麼我們是不是可以得到計算結果呢,比如說一個加密方法,我不需要知道具體實現,只要呼叫就好了

aidl修改如下:

// IMyAidlInterface.aidl
package com.kikt.aidl;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    void test(int sum,int sum2);

    int add(int num,int num2);
}

內部類實現修改

 public class AidlEntity extends IMyAidlInterface.Stub{

        @Override
        public void test(int sum, int sum2) throws RemoteException {
            Log.d(TAG, "test() called with: sum = [" + sum + "], sum2 = [" + sum2 + "]");
        }

        @Override
        public int add(int num, int num2) throws RemoteException {
            return num + num2;
        }
    }

呼叫者應用的bindService中修改如下:

bindService(intent, new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                IMyAidlInterface iinterface = IMyAidlInterface.Stub.asInterface(service);
                try {
                    iinterface.test(10, 20);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

                try {
                    int add = iinterface.add(1, 2);
                    Log.d("MainActivity", "add:" + add);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);

在第二個中點選可以看到日誌輸出
這裡寫圖片描述

思維發散

其實aidl就像一個核心,模組化開發的時候,核心模組不用給你程式碼,只要你去aidl檔案,和跨應用的呼叫方法即可,而你實現後續的業務邏輯,也算是一種解耦的方式吧

高階/原理

再回到Stub的實現中,看下onTransact方法就可以看出,其實aidl是實現自android提供的序列化
通過約定的方式,將方法名的引數和返回值序列化後再通過約定的方式取出來,這樣來實現程序間通訊
當然具體的內部原理因為我對於framework層沒有深入研究,傳輸的過程我不太瞭解

@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_test: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.test(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_add: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    int _result = this.add(_arg0, _arg1);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

後記

其實aidl很方便也是一種跨應用的解決方案,非常實用,面試和工作中都應該用得到,後續如果有機會可以多使用下,就先寫這麼多吧,謝謝網上的android先驅者和大神們!!!
遨遊在android知識的海洋中不可自拔的博主