未經博主同意,不得轉載該篇文章
前言
IPC-進程間通信。安卓雖然是一個基于linux內核的系統,但是安卓卻有自己的一套IPC機制。想要弄懂安卓的IPC機制首先要理解幾個framework層的概念,安卓的序列化機制以及binder和AIDL的實現流程(其它基于binder的ipc實現方式就不再多說了~)。在寫這篇博客之前反復看了幾遍《安卓開發藝術探索》的第二章,以及凱子哥(csdn)的framework層的文章and與一位大神學長交流,以保證文章的可靠性。這篇文章我將以自己的語言總結對于ipc的學習成果~
Demo地址:IPC-demo
幾個概念
1.Binder:
Binder是安卓的一個類,實現了Binder接口(后面多次出現)。Binder我理解成安卓IPC機制的核心,也就是實現IPC的核心工具。它不僅用于安卓開發層還用于安卓的framework層。在架構層里面binder是各種系統manager之間連接的工具(WindowManager, ActivityManager等等)。開發層用于service,別告訴我你不知道service里面會返回一個IBinder。
2.AIDL:
前不久在一個安卓群看到有個面試官問別人aidl是什么。。別人當時就蒙蔽了。。
AIDL(Android Interface Definition Language),安卓接口定義語言。它是安卓實現IPC通信的一種比較重要的方式,并且底層基于binder。所以我們就講這個啦!
3.linux的進程:
這個問題我專門請教了一個學長~
Linux沒有很嚴格的純粹進程概念。一堆線程共享一塊內存區域就是一個進程。當你的手機啟動的時候,系統會啟動一個init進程,這個應該可以成為Linux的主進程了。之后的所有進程都是從init進程fork出來的,比如zygote進程和SystemServer進程。
4.zygote進程
顧名思義(受精卵),這個進程會像個受精卵一樣不停的“分裂”,去fork出別的進程。幾乎后面出現的所有進程都是從這個進程fork出來的。包括SystemServer進程,ActivityManagerService等等。但是具體來說,AMS是SystemServer里面fork出來的。也許你會問為什么要這樣做,那是因為這樣設計更高效(當然我只是個普通的開發者,并不懂那些大神是怎么想的哈哈)。
4.ActivityManagerService:
這個玩意,我覺得是相當重要的,為什么這么說呢,因為它管理著手機中所有Activity的生死。你說重不重要???當你打開一個app后,AMS會立馬在zygote里面fork一個進程出來,并且復制一個虛擬機(Dalvik or ART)和一些資源以及一個線程(是的,這就是UI線程~,不要懷疑自己!)。啟動一個app是AMS和Lanucher, ActivityThread一起合作做到的。具體的實現,自己可以去看看別的文章,這不是我們要講的重點。另外說一點,AMS, activity之間也是通過binder來進行通信,你要知道,AMS, zygote, activity都是在不同的進程里面。
5.App與進程:
一個app對應一個進程。這種說法我不太敢茍同。首先一個app,一個進程這種說法太模糊,因為app是可以設置多進程的哇。。(設置組件的process),所以我覺得多進程的app應該看成共享apk資源的多個應用。
6.ShareUID:
之前在一個群里面聽前輩們討論app資源共享的問題,多次看到這個單詞,當時在想,臥槽,這tm什么鬼?!
這個東西你可以大概理解成每個apk的ID。一個apk對應一個uid,所以一個app里面跑在同一個進程里面的組件數據可以共享。如果一個app里面某個組件讓他跑在別的進程里面,相當于是創建了一個新的application,這個組件跟自己app里面的其它組件并沒有多大關系。然后ipc就可以起作用了,通過binder進行進程間通信。如果兩個屬于不同app的組件,自然是有不同的application和虛擬機了,然后通過簽名文件和uid來進行數據共享。普通的資源文件比如string, color這些文件是不需要相同的uid就可以訪問的,但是data里面的數據是需要這樣的。具體怎么做,自己有興趣也可以去查查資料~
7.序列化與反序列化:
首先你要知道,數據的傳輸都是要把數據轉換成字節碼,不管你是什么類型的數據。(嗯,沒有例外!)序列化就是把數據轉換成字節碼的過程,而反序列化自然就是在數據傳輸的目的地把字節碼轉換成原始數據。之前自己為了方便,所有都使用Serializable來做序列化。后來知道Parcelable的效率更高。因為Serializable要做大量的IO操作。所以以后都要使用安卓里面的Parcelable來做序列化哦~
好了,基本的概念介紹完畢。!如果我有說的不合理的地方請大家無情給我指出!!啪啪的打我的臉
Binder的工作原理
我們直接通過aidl文件生成的源碼來理解binder的工作原理!
首先新建一個aidl的文件包。聲明三個aidl文件。并且在java包里面聲明我們要傳輸的類Book。
代碼如下:
/**
* Created by Zane on 16/3/16.
*/
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(bookId);
dest.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>(){
public Book createFromParcel(Parcel in){
return new Book(in);
}
public Book[] newArray(int size){
return new Book[size];
}
};
private Book(Parcel in){
bookId = in.readInt();
bookName = in.readString();
}
@Override
public String toString() {
return "bookId " + bookId +" bookName " + bookName;
}
}
// Book.aidl
package com.example.zane.ipc_test;
parcelable Book;
// IBookManager.aidl
package com.example.zane.ipc_test;
import com.example.zane.ipc_test.Book;
import com.example.zane.ipc_test.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unRegisterListener(IOnNewBookArrivedListener listener);
}
// IOnNewBookArrivedListener.aidl
package com.example.zane.ipc_test;
//監聽服務端是否有新書籍,如果有新書籍就立即推送到客戶端,觀察者模式
import com.example.zane.ipc_test.Book;
interface IOnNewBookArrivedListener {
void newBookArrived(in Book book);
}
好了,咱先不管IOnNewBookArrivedListener.aidl這個文件。我們分析IBookManager.aidl的生成源碼。項目包里面的gen目錄下面有一個xxx.aidl包里面會有一個IBookManager.java的文件。我們就是要分析它!嗯,搞掂它!
當我用sublime打開它之后,老子差點吐掉。。
。。然后我憑借我的強迫癥一個個的給它縮進!我就是這么雷鋒。。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/Zane/編程/AndroidStudioProjects 13-52-23-071/IPC_Test/app/src/main/aidl/com/example/zane/ipc_test/IBookManager.aidl
*/
package com.example.zane.ipc_test;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.zane.ipc_test.IBookManager
{
//binder的唯一標識符
private static final java.lang.String DESCRIPTOR = "com.example.zane.ipc_test.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.zane.ipc_test.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.zane.ipc_test.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.zane.ipc_test.IBookManager))) {
return ((com.example.zane.ipc_test.IBookManager)iin);
}
return new com.example.zane.ipc_test.IBookManager.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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.zane.ipc_test.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.example.zane.ipc_test.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.zane.ipc_test.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.zane.ipc_test.IBookManager
{
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 java.util.List<com.example.zane.ipc_test.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.zane.ipc_test.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.zane.ipc_test.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.zane.ipc_test.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.zane.ipc_test.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.zane.ipc_test.Book book) throws android.os.RemoteException;
}
如果,你是第一次接觸這個,或者以前沒看過什么源碼,內心應該跟我之前一樣,也是崩潰的。但是,我們是程序員,在源碼面前千萬不能低頭!大概看一遍,Proxy這個類看到沒,嗯,沒錯了,用到了代理。再看Stub這個這個內部類,繼承了什么?大聲告訴我!嗯,就是Binder類。沒錯,這個Stub就是后面多次用到的Binder!其實這么多代碼,只需要理解Stub, Proxy這兩個類就差不多了。我們來細看代碼:
asInterface(Binder obj):
public static com.example.zane.ipc_test.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//查詢本地的binder
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.zane.ipc_test.IBookManager))) {
return ((com.example.zane.ipc_test.IBookManager)iin);
}
return new com.example.zane.ipc_test.IBookManager.Stub.Proxy(obj);
}
這個方法在后面使用的部分會多次用到。作用就是將服務端的Binder對象轉換成客戶端所需要的AIDL接口類型的對象。不知道你有沒有感受到,這其實就是一種類似接口回調的過程。在客戶端使用這個方法去得到服務端的binder類型的接口,然后調用服務端的方法。
代碼很簡單,如果客戶端和服務端在一個進程那么就返回這個binder,如果是多進程就返回遠程代理類Proxy的實例。
寫到這里,突然感冒加重有點發燒的感覺。。。orz,我還是堅持下去!
我們再來看Proxy類里面的兩個實現了IBookManager接口的方法:
@Override
public java.util.List<com.example.zane.ipc_test.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.zane.ipc_test.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//調用onTransact()方法
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
//讀取異常
_reply.readException();
//獲得返回值并返回給客戶端
_result = _reply.createTypedArrayList(com.example.zane.ipc_test.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
data是輸入對象,reply是輸出對象,result是最后返回給客戶端的數據。這個是getBookList的方法,沒有參數,只有返回值,所以data寫入token標識符之后就直接調用了Stub類里面的onTransact()方法。在onTransact()方法里面把結果值寫入reply并且返回true表示客戶端與服務端連接成功。如果返回false就表示連接失敗!再看addBook(Book book)的方法:
@Override
public void addBook(com.example.zane.ipc_test.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
//防止傳入的參數為null
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
//調用onTransact()方法
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
我想如果你不是特別傻,應該可以類比上面看得懂這個吧!我在上面寫了一些注釋。然后就來看Stub是來如何響應RPC(遠程過程調用)的。
@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_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.zane.ipc_test.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.example.zane.ipc_test.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.zane.ipc_test.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
這個方法通過傳進來的不同code來響應客戶端不同的請求。
TRANSACTION_getBookList里面,首先調用獲得服務端的List,然后寫入reply。返回true表示連接成功,并且讓客戶端從reply里面read出來返回過來的數據。
TRANSACTION_addBook里面,通過Parcelable去new出了一個新的Book實例。然后調用服務端的addBook方法,把這個實例返回給服務端。你如果第一次使用Parceable可能會不理解怎么new出新的Book的。你回頭去看看你的Book類你就懂了!
嗯!就是這么簡單!源碼我們基本就分析完了!我們再來總結一下流程。
- 客戶端發出請求,然后將客戶端發送請求的線程掛起(這個下篇文章再說)
- Binder寫入參數(從客戶端傳入)到data,如果沒有則不寫入。
- 調用transact()方法
- onTransact()響應,調用Service(服務端)的實現方法,并且把客戶端需要的數據寫入reply,如果沒有則不寫入。
- result讀出數據返回客戶端,喚醒客戶端,客戶端獲得數據。
總結
開始準備一篇文章直接寫完。。發現得需要兩篇了!
這篇文章我們解決了一些IPC的基本概念知識and通過aidl學習binder的工作原理。
啊啊,,,突然發燒了臥槽,好難受,下篇我們接著說AIDL的使用!
未經博主同意,不得轉載該篇文章