Android進程間通信——一步步揭開AIDL的神祕面紗

前言

在我心中AIDL一直是個神祕的存在,它就像武俠世界中的上乘內功心法,讓我這個只會點三腳貓功夫的人一直敬而遠之!雖然說在平時開發中確實很少使用AIDL,但是它在面試中出現的頻率還是相當高的,況且想要成爲一名合格的Android開發人員,是無法避開AIDL的。最近又拿起了《Android開發藝術探索》這本書,看到了“IPC機制”這個章節,就借這個機會徹底揭開AIDL的神祕面紗,看看它究竟是何方神聖吧!

正文

一、什麼是AIDL?

Android Interface Definition Language 直譯:Android接口定義語言。

是 Android 提供的一種進程間通信 (IPC) 機制,通過這種機制,我們只需要寫好aidl接口文件,編譯時系統會幫我們生成Binder接口,此時我們就可以通過Binder去輕鬆實現進程之間傳值或者調用方法啦。

二、爲什麼要使用AIDL?

我們都知道Android對單個應用是有內存限制的,當有需求需要突破這個限制的時候,我們需要另啓進程擴大內存,而兩個進程是完全獨立的,此時AIDL是一種很有效的解決進程間通信的方式;再比如說,某些情況下遠端的服務更適合運算或者更適合執行耗時操作,這時候我們會使用AIDL請求遠程服務等等。

三、AIDL支持哪些數據類型?

1、基本數據類型(int、long、char、boolean、double等);

2、String和CharSequence;

3、List 和 Map 

  • 元素必須是AIDL支持的數據類型
  • 必須是 ArrayList 或者 HashMap

4、所有實現了Parcelable接口的對象;

5、所有的AIDL接口

四、如何使用AIDL?

1、創建AIDL接口:

  • 創建一個後綴爲AIDL的文件
  • 在文件中聲明一個接口和所需要的接口方法

2、創建服務端:

  • 創建Service來監聽客戶端的連接請求
  • 在Service中實現第一步創建的AIDL接口

3、創建客戶端:

  • 綁定服務端的Service
  • 將服務端返回的Binder對象轉換成AIDL接口所屬的類型

五、需要注意什麼?

1、在AIDL中每個實現了Parcelable接口的類,都需要創建相應的AIDL文件(刪除所有默認生產的代碼,替換爲以下兩行代碼)

package 包名;

parcelable 類名;

2、非基本類型的數據需要手動導入:

import 包名.類名

3、AIDL中,除了基本數據類型,其他類型的參數都必須標上方向

  • in(輸入型參數)
  • out(輸出型參數)
  • inout(輸入輸出型參數)

需要注意,不能一概使用inout,因爲在底層實現是有開銷的;

4、AIDL中只支持方法,不支持聲明靜態變量;

5、AIDL中實體類的包名必須與它所對應的aidl文件包名相同。

六、代碼要怎麼寫?

首先要聲明一下,我這篇文章主要是《Android開發藝術探索》的讀書總結,所以這裏關於代碼的部分,我是直接照着書上敲了一遍,主要是想了解編寫AIDL的整個流程,其實真正自己敲的時候纔會發現問題,然後才能更好更快的掌握。因爲第一次寫AIDL,個人理解能力也一般,光在流程上就遇到幾個疑惑的地方,所以在這裏我主要想說說編寫流程。

1、創建一個工程,我們主要關注一下工程目錄,如下圖

2、接着在main目錄下新建一個AIDL文件

文件名就叫IBookManager,看看默認生成的文件:

我們發現默認生成了一下代碼:

/**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

此時,第一個疑惑出現了,這段代碼是幹什麼的?必須存在嗎?

看看註釋,明白了,這是用來演示在AIDL中可以用作參數和返回值的一些基本類型。我們無需關心它,如果看着麻煩,完全可以把它全部刪除,這樣也顯得代碼乾淨!

3、在我們java目錄下創建實體類Book,然後在aidl目錄下創建Book所對應的aidl文件

這時候,第二個疑惑出現了,如下圖:

如果先在java目錄下創建了實體類Book,接着在aidl目錄相同包下創建Book.aidl就無法創建了!難道對於AS來說它們是同一個包嗎?具體原因還請留言。。。我的解決辦法有兩種:

  1. 隨便先創建一個.aidl文件,然後把名字改成Book,接着編譯,是可以成功的;
  2. 把創建順序顛倒一下,先在aidl目錄下創建Book.aidl,然後再在java目錄下創建Book類,編譯成功!

4、將Book.aidl中的代碼替換

這是默認生成的:

該刪的就刪,我們只需要簡單的兩行代碼,替換後:

5、在第二步創建的IBookManager中添加以下代碼:

// IBookManager.aidl
package com.example.kangbaibai.aidldemo;

// Declare any non-default types here with import statements
import com.example.kangbaibai.aidldemo.Book;

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

6、這時候我們build一下工程,然後在Project模式下,打開app/build/generated/source/aidl,結構如下圖:

看看系統爲我們生成的IBookManager:

可以看到我們添加的兩個方法已經存在了。到此AIDL代碼全部寫好了,該寫服務端和客戶端代碼了。

7、服務端代碼

新建一個服務BookManagerService:

package com.example.kangbaibai.aidldemo;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by kangbaibai on 2018/9/18.
 */

public class BookManagerService extends Service {
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(1, "IOS"));
    }

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

別忘記在AndroidManifest.xml文件中註冊,並且設置process,開啓新進程:

<service
            android:name=".BookManagerService"
            android:process=":remote" />

服務端搞定!

8、客戶端代碼

在Activity中添加如下代碼:

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                for (Book book : list) {
                    Log.i(TAG, "before add query book list: " + book.toString());
                }
                Log.i(TAG, "------------------------------------------------------------");
                Book newBook = new Book(3, "Android開發藝術探索");
                bookManager.addBook(newBook);
                List<Book> newList = bookManager.getBookList();
                for (Book book : newList) {
                    Log.i(TAG, "after add query book list: " + book.toString());
                }
            } catch (RemoteException e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

在onCreate中綁定:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

在onDestroy中解綁定:

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

客戶端也搞定了!允許一下,看看log:

09-19 14:51:28.770 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: before add query book list: com.example.kangbaibai.aidldemo.Book@db78307
09-19 14:51:28.770 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: before add query book list: com.example.kangbaibai.aidldemo.Book@f791c34
09-19 14:51:28.770 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: ------------------------------------------------------------
09-19 14:51:28.771 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: after add query book list: com.example.kangbaibai.aidldemo.Book@1610a5d
09-19 14:51:28.771 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: after add query book list: com.example.kangbaibai.aidldemo.Book@867efd2
09-19 14:51:28.771 28287-28287/com.example.kangbaibai.aidldemo I/BookManagerActivity: after add query book list: com.example.kangbaibai.aidldemo.Book@dbf48a3

結語:

到此一個簡單的AIDL程序就搞定了,本文只是簡單做個讀書總結,並提出實際操作中遇到的疑惑和解決方法,之後我會繼續深入瞭解AIDL原理等,希望大家支持~

代碼已上傳至github:https://github.com/kb18519142009/AidlDemo

歡迎star~

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