Android 之 Service(二)AIDL

一、介紹

AIDL(Android Interface Definition Language)是一種接口定義語言。
它允許你定義一個編程接口,用于約束兩個進程間的通訊規則,提供給編譯器生成代碼,實現Android設備上的兩個進程間通信(IPC)。
在Android系統中,一個進程通常不能訪問另一個進程的內存。所以他們需要將他們的對象拆解成操作系統所能識別的原語數據(primitives),然后傳入到另一個進程之后再替你組裝成對象。
也就是說進程之間的通信信息,首先會被轉換成AIDL協議消息,然后發送給對方,對方收到AIDL協議消息后再轉換成相應的對象。
由于進程之間的通信信息需要雙向轉換,所以Android采用代理類在背后實現了信息的雙向轉換,代理類由Android編譯器生成。

二、適用場景

一般適用于為其它應用程序提供公共服務的Service,這種Service即為系統常駐的Service(如:天氣服務等)。

三、優缺點

優點
1.AIDL有自己的獨立進程,不會受到其它進程的影響;
2.可以被其它進程復用,提供公共服務;
3.具有很高的靈活性。
缺點
相對普通服務,占用系統資源較多,使用AIDL進行IPC也相對麻煩。

四、具體操作(此處我們創建一個服務端程序,一個客戶端程序)

1.生成AIDL文件(服務端創建)
選擇 new->aidl->aidl file


Studio生成AIDL文件

填寫創建的名稱


創建的 aidl的名稱.png

會在main目錄下為你創建一個aidl文件夾,并在里面與你的包名對應創建了你的aidl文件,此文件默認會給你創建一個方法,該方法只是作為類型案例解釋,可以不需要
創建的aidl文件

Gradle構建

點擊Gradle圖標讓編譯器為我們生成對應AIDL的 文件

2.創建Service提供服務,AIDL涉及到IPC通信,所以需要使用綁定服務。

public class MyService extends Service{

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return aidl;
    }

    IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){

        public static final String TAG = "AIDL";

        //此處重寫所有的抽象方法,實現提供對外暴露的接口
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.i(TAG,"服務正在執行...");
        }
    };
}

3.注冊服務

  <application android:allowBackup="true"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

         <!--注冊服務-->
        <service android:name=".MyService"
                 android:process="com.my.remote.service" >
            <intent-filter>
                <action android:name="com.myaidl.server"/>
            </intent-filter>
        </service>

    </application>

如果客戶端與服務端在同個App中,AndroidManifest.xml中設置Remote Service的andorid:process屬性時,如果被設置的進程名是以一個冒號(:)開頭的,則這個新的進程對于這個應用來說是私有的,當它被需要或者這個服務需要在新進程中運行的時候,這個新進程將會被創建。如果這個進程的名字是以小寫字符開頭的,則這個服務將運行在一個以這個名字命名的全局的進程中,當然前提是它有相應的權限。這將允許在不同應用中的各種組件可以共享一個進程,從而減少資源的占用。

4.拷貝服務端aidl文件到客戶端目錄下(文件夾路徑不可變化)
點擊Gradle圖標讓編譯器為我們生成對應AIDL的 文件

拷貝AIDL文件

5.客戶端連接代碼

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        //服務注冊的行為
        intent.setAction("com.myaidl.server");
        //5.0之后 需要設置服務所在的包名(服務APP的服務所在包名)
        //否則會報 IllegalArgumentException: Service Intent must be explicit
        intent.setPackage("nightingale.aidl_server");
        //綁定服務
        bindService(intent,conn,BIND_AUTO_CREATE);
    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //通過我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務傳遞過來的iBinder對象 返回我們的AIDL對象
            IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);

            try {
                //此處方法為你在AIDL文件中,自己定義的方法 此處對應我們的AIDL文件
                aidl.basicTypes(0,0,true,0,0,null);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}

如果無法調用AIDL對象(自定義的IMyAidlInterface)那么需要使用Gradle 構建一下,或者檢查你是否拷貝了服務端的aidl文件到客戶端

6.安裝服務端,安裝客戶端執行結果

執行結果.png

五、AIDL參數的傳遞(基礎數據類型 與 自定類型)

AIDL默認支持下面幾種數據類型:
Java編程語言中的基礎數據類型(int,long,char,boolean等)
String,CharSequence,List,Map,在List 中的所有元素必須是上面支持的類型或者是其他由AIDL生成的接口,或者你申明的實現了Parcelable接口的類型。
List可能被選用為泛型類,比如 List<String>.實際在接受服務一側生成的類為永遠是 ArrayList。盡管生成的方法使用的是 List接口。
Map中的雖有元素必須是上面類型或者是其他由AIDL生成的接口,或者你申明的實現了Parcelable接口的類型。泛型例如 Map<String,Integer>不支持。實際在接受服務一側生成的類為永遠是HashMap。盡管生成的方法使用的是 Map接口。
你必須要為每一個上面未列出類型添加import申明,盡管他們是作為接口定義在同一個包里面。

示例:
1.寫一個類 實現 Parcelable

public class User implements Parcelable {
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public User(){

    }
    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }
    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };
    @Override
    public int describeContents() {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeString(name);
        parcel.writeInt(age);
    }
}

2.在AIDL中定義獲取該類的方法

package nightingale.aidl_server;

//***導入對應的類,必須***
import nightingale.aidl_server.User;

interface IMyAidlInterface {

   //默認的 可刪除 可不官 這里案例使用 所以 仍在這里吧
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
   //獲取用戶對象
    User getUser();
}

3.在aidl文件夾中聲明User對象

聲明User對象

4.拷貝aidl中的文件到客戶端

拷貝aidl中的文件到客戶端

5.拷貝服務端的User類到客戶端
注意:此處拷貝的時候,拷貝過去的類所在包名需要跟服務器所在包名一致,此處我創建了一個 aidl_server包


拷貝服務端的User類到客戶端

6.服務端Service代碼

public class MyService extends Service{

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return aidl;
    }

    IMyAidlInterface.Stub aidl = new IMyAidlInterface.Stub(){

        public static final String TAG = "AIDL";

        //此處重寫所有的抽象方法,實現提供對外暴露的接口
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.i(TAG,"服務正在執行...");
        }

        @Override
        public User getUser() throws RemoteException {
            return null;
        }
    };
}

7.客戶端使用代碼

public class MainActivity extends AppCompatActivity {

    private String TAG="aidl";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent();
        //服務注冊的行為
        intent.setAction("com.myaidl.server");
        //5.0之后 需要設置服務所在的包名(服務APP的服務所在包名)
        //否則會報 IllegalArgumentException: Service Intent must be explicit
        intent.setPackage("nightingale.aidl_server");
        //綁定服務
        bindService(intent,conn,BIND_AUTO_CREATE);
    }


    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //通過我們自己定義的AIDL文件的Stub.asInterface方法 傳入服務傳遞過來的iBinder對象 返回我們的AIDL對象
            IMyAidlInterface aidl = IMyAidlInterface.Stub.asInterface(iBinder);

            try {

                User user = aidl.getUser();
                String name = user.getName();
                if(!TextUtils.isEmpty(name)){
                    Log.i(TAG,name);
                }

            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onDestroy() {
        unbindService(conn);
        super.onDestroy();
    }
}

執行結果


執行結果

至此 AIDL的相關操作就介紹完了??,不好請吐槽!謝謝

關于Messenger的介紹,推薦閱讀:陳育 Android IPC機制(五):詳解Bundle與“信使”——Messenger:http://www.lxweimin.com/p/6e23037d6d20

本文代碼下載:http://download.csdn.net/detail/kooeasy/9610705

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,947評論 18 139
  • Android跨進程通信IPC整體內容如下 1、Android跨進程通信IPC之1——Linux基礎2、Andro...
    隔壁老李頭閱讀 10,829評論 13 43
  • Jianwei's blog 首頁 分類 關于 歸檔 標簽 巧用Android多進程,微信,微博等主流App都在用...
    justCode_閱讀 5,963評論 1 23
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,337評論 25 708
  • 人生,總是在伴隨著一個個得到與失去。你固然得到了這一個,卻失去了另一個。你沉浸在得到的驚喜當中,卻完全沒有意...
    詩人阿秀閱讀 247評論 0 0