前言
在我心中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來說它們是同一個包嗎?具體原因還請留言。。。我的解決辦法有兩種:
- 隨便先創建一個.aidl文件,然後把名字改成Book,接着編譯,是可以成功的;
- 把創建順序顛倒一下,先在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~