好久沒有寫文章了,內心可能有千種理由,可是說到底仍是惰性形成的。學習自己就是一場修行,在沒有人督促的狀況下,更容易產生惰性。因此,要時刻提醒本身要更努力一些。html
因爲IPC機制在Android中屬於比較重要的機制,加之也比較複雜。因此,我會分兩篇文章進行記錄。這篇文章將會從IPC機制概念、進程和線程之間的區別、IPC機制來、常見IPC機制和AIDL實踐來說解IPC機制,剩下的知識點將會在下一篇文章進行記錄。java
IPC的全稱:Inter-Process Communication,翻譯過來就是「進程間通訊」,是指兩個進城之間的相互通訊。講到這裏確定會有人問什麼是進程?進程與線程之間的區別和聯繫是什麼?讓咱們來看一下。android
線程:
根據操做系統的描述,線程是是CPU調度的最小單元,同時線程是一種有限的系統資源。它是進程的一個執行流,每一個線程都有本身的堆棧和局部變量。
面試
進程:
一般狀況下是指一個執行單元,在PC或者移動設備上指一個程序或者一個應用。
shell
二者之間的區別:
一、進程是資源分配的最小單位,線程是程序執行的最小單位。
二、線程之間通訊能夠經過共享局部變量、靜態變量等數據。進程之間通訊須要經過IPC機制。
app
固然二者之間的區別確定不止上面兩條,你們能夠自行查閱。
ide
講到如何在Android中開啓多進程,你們可能有點懵:「兩個進程不就是兩個應用嗎?」。沒錯,這裏只是開啓多進程的一種形式,可是咱們咱們若是想在一個應用中開啓多進程該如何操做呢?最簡單的咱們能夠經過給四大組件設置不一樣的android:process
值來實如今一個應用中的多進程。下面咱們將舉一個例子。源碼分析
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.a00123.ipcdemo">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".FirstActivity"
android:process=":process"> //1
</activity>
<activity
android:name=".SecondActivity"
android:process="com.a00123.ipcdemo.process"> //2
</activity>
</application>
</manifest>
複製代碼
咱們在Manifest文件中建立了三個Activity,分別是:MainActivity
、FirstActivity
和SecondActivity
,其中咱們爲FirstActivity
和SecondActivity
分別設置了android:process
屬性(代碼註釋1和註釋2處),在MainActivity
中分別設置了兩個按鈕,用於打開另外兩個Activity
。學習
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
Intent intent = new Intent(MainActivity.this, FirstActivity.class);
startActivity(intent);
break;
case R.id.btn2:
Intent intent2 = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent2);
break;
}
}
複製代碼
讓咱們來運行一下,看看會有什麼結果。在這裏再多說一句題外話,這裏我用的是adb命令進行查看的進程名稱,至於各位小夥伴的電腦請參照具體方法查看。 ui
由此咱們能夠看出若是沒有設置android:process
屬性的話,默認的進程名就是當前應用的包名。使用「:」來設置的話,會自動在前面加上對應的包名。最後一個就是咱們設置的
android:process="com.a00123.ipcdemo.process"
屬性值。這也其實也很好理解,設置進程名就至關於咱們電腦中文件中的絕對路徑和相對路徑。其中以
「:」
開頭的進程屬於當前應用的私有進程,其餘應用的組件不能夠和它運行在同一個進程中,而不以
「:」
開頭的進程則屬於全局進程,其餘應用經過
ShareUID
方式能夠和它運行在同一個進程中。
Android
在運行一個程序時,會爲其分配一個惟一的UID,具備相同的UID的兩個應用才能共享數據。若是兩個應用想經過
ShareUID
的方式運行在同一進程須要這兩個程序擁有相同的
ShareUID
和相同的簽名才能夠。只有這種狀況下,兩個程序之間才能相互獲取對方的私有數據。
public class IPCManager {
public static int managerId = 1;
}
複製代碼
正如上面這幾張圖所示,咱們運行起來以後有三個進程。可是,只有在默認進程中靜態變量的值是改變的,其餘兩進程中的靜態變量居然仍是原來的值,這事怎麼回事?
從上面咱們也能夠看出,當程序中存在多進程時,容易出現問題。出現上面問題的具體緣由是這三個Activity分別運行在三個進程中,上面咱們也說過,Android系統會給每個應用程序分配一個獨立的虛擬機,不一樣的虛擬機在內存地址的分配上是不一樣的。當訪問一個靜態變量時,不一樣的進程之間相互沒有影響,因此這就是爲何在MainActivity中修改靜態變量的值時其餘Activity沒發生變化的緣由。
當運行在不一樣進程中的四大組件,它們想要經過共享內存的方式來共享數據都會產生失敗。因此說,進程間共享數據時,會出現如下問題:
一、靜態成員和單例模式失效
二、線程同步機制失效
三、SharePreference
的可靠性降低
四、Application
會被重複建立
分別解釋一下 第一個問題在上面中的例子中已經有所說明;第二個問題和第一個問題相同,進程間運行在不一樣的內存上,無論有沒有鎖對象都無法保證線程同步,由於鎖做用的對象不是同一個;第三個問題,SharePreference
文件是不支持兩個進程同時寫數據,由於有可能形成數據的丟失,因此可靠性會下降;第四個問題,咱們說過Android
建立新的進程同時會分配獨立的虛擬機,這就至關因而從新啓動一個應用的過程,啓動應用是會自動建立新的Application
對象,因此會形成Application
對象重複建立。
這裏的基礎知識準備主要講講序列化的知識點,固然還有另一個知識點Binder
,這個知識點準備放在AIDL
實例講解中介紹。
當咱們在四大組件之間使用Intent傳遞對象時,這自己就是一種進程間通訊的方式,須要將對象進行序列化和反序列化。再或者,當咱們須要把對象存儲在手機設備上,這個過程也是須要將對象序列化。咱們來看一下兩種序列化方式Serializable
和Parcelable
。
Serializable
是Java
提供的一種序列化接口,能夠爲對象提供序列化和反序列化操做。使用的時候須要類實現Serializable
接口,同時聲明一個serialVersionUID
來實現序列化。
從聲明上咱們能夠看出,這個serialVersionUID
至關因而類的惟一標識,也就是說,一個類在序列化時保存了惟一標識,在反序列化時會將這個惟一標識要反序列化對象的惟一表示進行對比,若是不一致會報:InvalidClassException
。
Parcelable
是Android
提供的一種序列化方法,相比於Serializable
其效率更高,佔用的內存更少,可是使用起來也更麻煩。Android
官方推薦使用Parcelable
方法進行序列化操做。在Android
中Bundle
、Intent
等都默認實現了Parcelable
接口。
既然是谷歌爸爸推薦的,咱們確定要多使用一下。可是在使用時比較麻煩,怎麼辦?其實不用擔憂,Android Studio已經中可使用插件進行一鍵生成,美滋滋了。
舉個例子吧,看看Parcelable
序列化方式都作了什麼。
public class Dog implements Parcelable {
private String name;
private String color;
public Dog(String name, String color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public String getColor() {
return color;
}
public void setName(String name) {
this.name = name;
}
public void setColor(String color) {
this.color = color;
}
/** * 內容描述 */
@Override
public int describeContents() {
return 0;
}
/** * 序列化操做 */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeString(this.color);
}
//反序列化數據恢復
protected Dog(Parcel in) {
this.name = in.readString();
this.color = in.readString();
}
//建立Parcelable對象
public static final Parcelable.Creator<Dog> CREATOR = new Parcelable.Creator<Dog>() {
@Override
public Dog createFromParcel(Parcel source) {
return new Dog(source);
}
@Override
public Dog[] newArray(int size) {
return new Dog[size];
}
};
}
複製代碼
一、Serializable屬於Java;Parcelable屬於Android
二、Serializable序列化和反序列化過程須要大量的I/O操做;Parcelable不須要
三、Serializable開銷較大;Parcelable開銷較小
四、Serializable效率低;Parcelable效率低
在咱們平常開發中不多使用到AIDL
(也多是本人開發過程當中不多使用),但不論在平時學習仍是面試的過程當中,這都是一個很是重要的知識點。咱們經過一個實例來練習一下,並經過這個例子探究一下Binder
機制。有關AIDL的介紹網上有不少,我在這裏就再也不贅述,有興趣的小夥伴能夠參照AIDL谷歌官方文檔
在寫AIDL例子的時候,我在網上看到不少都是參照任玉剛老師《Android開發工藝探索》的中例子。這個例子很經典,我也打算參照這個例子,可是此次是對電影票進行操做。
public class Tickets implements Parcelable {
private String name;
private float prices;
public Tickets(String name, float prices) {
this.name = name;
this.prices = prices;
}
public String getName() {
return name;
}
public float getPrices() {
return prices;
}
public void setName(String name) {
this.name = name;
}
public void setPrices(float prices) {
this.prices = prices;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeFloat(this.prices);
}
protected Tickets(Parcel in) {
this.name = in.readString();
this.prices = in.readFloat();
}
public static final Parcelable.Creator<Tickets> CREATOR = new Parcelable.Creator<Tickets>() {
@Override
public Tickets createFromParcel(Parcel source) {
return new Tickets(source);
}
@Override
public Tickets[] newArray(int size) {
return new Tickets[size];
}
};
@Override
public String toString() {
return "票名:" + name + "---" + "票價:" + prices + "元";
}
}
複製代碼
在這個類中建立了兩個變量,分別表示名稱和價格。
建立文件很簡單,在項目包目錄下直接右鍵,選擇新建->AIDL->AIDL File,取名爲Tickets.aidl
AndroiStudio
會自動在工程目錄下建立一個aidl包,而且其子包名一工程的包名一致,須要有兩點須要注意的地方。第一,這個
ADIL
文件應該和第一步中建立的實體類的包名保持一致;第二,就是aidl文件中的內容,建立出來以後若是不對內容修改,會報出名稱不惟一的錯誤。
//Tickets.aidl文件內容
package com.a00123.aidlservice;
// Declare any non-default types here with import statements
parcelable Tickets;
複製代碼
在這裏須要再次提醒一下,每新建一個類或者aidl文件,最好從新build一下工程。
建立步驟和第二步相同,這裏要注意的是最好路徑一上一個aidl文件路徑相同,這樣方便後期操做。
// TicketsManager.aidl
package com.a00123.aidlservice;
// Declare any non-default types here with import statements
import com.a00123.aidlservice.Tickets;
interface TicketsManager {
List<Tickets> getTicketsList();
void addTickets(in Tickets tickets);
}
複製代碼
在這裏定義了兩個方法,一個是獲取票列表,另一個是增長票的方法。aidl文件中是不支持自動導包,因此咱們須要手動把Tickets的包名導入。咱們看到在addTickets方法中,在參數以前有一個in,這是什麼意思呢?
實際上in
, out
, inout
是三個定向tag,它們實際的含義是:全部的非基本參數都須要一個定向tag來指出數據的流向,無論是 in
, out
, 仍是 inout
。基本參數的定向tag默認是而且只能是 in
。
在咱們從新build完項目以後,會在項目的build目錄下生成兩個文件。其中,有一個Tickets.java文件,這個文件內容爲空。另外還會生成一個Interface文件,這個文件是比較中要的,它牽扯到AIDL是怎樣在進程間進行通訊的。因爲這個文件比較複雜,放到文章的後部進行講解,咱們先把流程走通。
public class TicketsManagerService extends Service {
private CopyOnWriteArrayList<Tickets> mTickets = new CopyOnWriteArrayList<>();
private Binder mBinder = new TicketsManager.Stub() {
@Override
public List<Tickets> getTicketsList() throws RemoteException {
return mTickets;
}
@Override
public void addTickets(Tickets tickets) throws RemoteException {
mTickets.add(tickets);
}
};
@Override
public void onCreate() {
super.onCreate();
mTickets.add(new Tickets("攀登者",50));
mTickets.add(new Tickets("我和個人祖國",55));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
複製代碼
在建立服務的時候咱們新建一個TicketsManager.Stub對象,同時也實現了其內部的兩個方法,這兩個方法就是咱們在第3步中定義的方法。在服務建立的時候新增了兩張票。到此服務端代碼已經建立完畢,不要忘記在清單文件中註冊服務。
<service
android:name=".TicketsManagerService"
android:enabled="true"
android:exported="true"/>
複製代碼
其中的enable屬性表示是否能夠被系統實例化,exported屬性表示可否被其餘應用隱式調用。
咱們須要將服務端的兩個aidl文件、Tickets文件複製到客戶端,這裏須要注意,這些文件的路徑要和服務端中對應文件路徑保持一致。
public class MainActivity extends AppCompatActivity {
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
TicketsManager ticketsManager = TicketsManager.Stub.asInterface(service);
try {
List<Tickets> list = ticketsManager.getTicketsList();
Log.i("MainActivity", "query tickets list:" + list.toString());
Tickets tickets = new Tickets("中國機長", 45);
ticketsManager.addTickets(tickets);
Log.i("MainActivity", "add tickets:" + tickets);
List<Tickets> newList = ticketsManager.getTicketsList();
Log.i("MainActivity", "query tickets list:" + newList.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setClassName("com.a00123.aidlservice", "com.a00123.aidlservice.TicketsManagerService");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
複製代碼
在服務鏈接以後,咱們首調用了getTicketsList()方法並將結果打印了出來,而後有新增了一個Tickets對象,最後在一次打印。因爲服務是在另一個進程中,因此在綁定的時候須要知道服務所在的包名和全路徑名稱,最後直接綁定。讓咱們看一下打印結果。
雖然咱們已經學會了如何使用AIDL,可是有不少地方感受仍是有些雲裏霧裏,就讓咱們探究一下AIDL的原理。
不知道你們是否還記得,當咱們建立TicketsManager以後系統會在build目錄下爲咱們自動建立的那個TicketsManager文件,這個文件很重要,能夠說這裏面就是AIDL的運行本質,一樣也能夠說是binder機制的原理,讓咱們來看一下。
public interface TicketsManager extends android.os.IInterface {
/** * Default implementation for TicketsManager. */
public static class Default implements com.a00123.aidlservice.TicketsManager {
@Override
public java.util.List<com.a00123.aidlservice.Tickets> getTicketsList() throws android.os.RemoteException {
return null;
}
@Override
public void addTickets(com.a00123.aidlservice.Tickets tickets) throws android.os.RemoteException {
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** * Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.a00123.aidlservice.TicketsManager {
private static final java.lang.String DESCRIPTOR = "com.a00123.aidlservice.TicketsManager";
/** * Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/** * Cast an IBinder object into an com.a00123.aidlservice.TicketsManager interface, * generating a proxy if needed. */
public static com.a00123.aidlservice.TicketsManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.a00123.aidlservice.TicketsManager))) {
return ((com.a00123.aidlservice.TicketsManager) iin);
}
return new com.a00123.aidlservice.TicketsManager.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getTicketsList: {
data.enforceInterface(descriptor);
java.util.List<com.a00123.aidlservice.Tickets> _result = this.getTicketsList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addTickets: {
data.enforceInterface(descriptor);
com.a00123.aidlservice.Tickets _arg0;
if ((0 != data.readInt())) {
_arg0 = com.a00123.aidlservice.Tickets.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addTickets(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.a00123.aidlservice.TicketsManager {
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.a00123.aidlservice.Tickets> getTicketsList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.a00123.aidlservice.Tickets> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getTicketsList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getTicketsList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.a00123.aidlservice.Tickets.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addTickets(com.a00123.aidlservice.Tickets tickets) 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 ((tickets != null)) {
_data.writeInt(1);
tickets.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addTickets, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addTickets(tickets);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public static com.a00123.aidlservice.TicketsManager sDefaultImpl;
}
static final int TRANSACTION_getTicketsList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addTickets = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.a00123.aidlservice.TicketsManager impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.a00123.aidlservice.TicketsManager getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.util.List<com.a00123.aidlservice.Tickets> getTicketsList() throws android.os.RemoteException;
public void addTickets(com.a00123.aidlservice.Tickets tickets) throws android.os.RemoteException;
}
複製代碼
若是你們將方法進行摺疊,你會發現這裏面其實一共作了如下幾件事:
一、定義了getTicketsList()方法
二、定義了addTickets(com.a00123.aidlservice.Tickets tickets)方法
三、建立了一個默認實現TickManager接口的靜態內部類
四、建立了一個名爲Stub的靜態內部類,這個類繼承自Binder,同時也實現了TicketsManager接口
可是當咱們仔細觀察時,有感受無從下手,彆着急,咱們一步一步的看。
咱們看到這個TicketManager接口繼承了一android.os.IInterface接口,這個接口內部作了什麼?
/** * Base class for Binder interfaces. When defining a new interface, * you must derive it from IInterface. */
public interface IInterface {
/** * Retrieve the Binder object associated with this interface. * You must use this instead of a plain cast, so that proxy objects * can return the correct result. */
public IBinder asBinder();
}
複製代碼
從翻譯中咱們不難看出,這個接口是Binder
接口的基類,若是想定義一個新的接口,必需要繼承它。這個接口中有一個asBinder()
方法,默認返回的是IBinder
。也就是說IInterface
能夠把全部繼承它的對象轉換成IBinder
。
既然說到了IBinder
,咱們來看一IBinder
是什麼。
public interface IBinder {
...
/** * Attempt to retrieve a local implementation of an interface * for this Binder object. If null is returned, you will need * to instantiate a proxy class to marshall calls through * the transact() method. */
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor);
/** * Perform a generic operation with the object. * * @param code The action to perform. This should * be a number between {@link #FIRST_CALL_TRANSACTION} and * {@link #LAST_CALL_TRANSACTION}. * @param data Marshalled data to send to the target. Must not be null. * If you are not sending any data, you must create an empty Parcel * that is given here. * @param reply Marshalled data to be received from the target. May be * null if you are not interested in the return value. * @param flags Additional operation flags. Either 0 for a normal * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC. * * @return Returns the result from {@link Binder#onTransact}. A successful call * generally returns true; false generally means the transaction code was not * understood. */
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException;
...
}
複製代碼
咱們能夠看到IBinder也是一個接口,這個接口中定義了不少變量和方法,其中咱們着重看一下上面兩個方法。
這個方法但願返回一個本地實現了該接口的Binder對象,若是返回值爲null,就須要實例化一個代理類去調用transact()方法。其實這個方法使用來判斷AIDL中服務調用這的進程身份,若是是當前進程,返回值不爲null;若是是其餘進程,就須要建立一個代理類去調用transact()方法。
這個方法中傳入了4個參數,咱們分別看一下這4個參數都是什麼意思:
一、
code
表示要執行的操做,它的取值範圍在FIRST_CALL_TRANSACTION
和LAST_CALL_TRANSACTION
之間
二、data
表示傳輸給目標的數據,必定不能爲空,若是爲空,須要建立一個未被初始化的Parcel數據。
三、replay
表示從目標接收的數據,若是你不感興趣能夠返回空
四、flags
附加操做的標識,0是指普通的RPC。或者FLAG_ONEWAY,指單向RPC。
這個方法返回值會經過調用Binder
類中的onTransact
方法。若是成功的執行了,則返回true
;反之表示不清楚要處理的code值是什麼含義。
從上面的方法中咱們又知道了另一個名詞:RPC
關於其定義這裏就很少解釋了,詳情請參照柳樹之大佬的如何給老婆解釋什麼是RPC
上面的transact(...)
方法會調用Binder
中的onTransact
方法,因此咱們來看一下Binder
類。
/** * Base class for a remotable object, the core part of a lightweight * remote procedure call mechanism defined by {@link IBinder}. * This class is an implementation of IBinder that provides * standard local implementation of such an object. * * <p>Most developers will not implement this class directly, instead using the * <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired * interface, having it generate the appropriate Binder subclass. You can, * however, derive directly from Binder to implement your own custom RPC * protocol or simply instantiate a raw Binder object directly to use as a * token that can be shared across processes. * * <p>This class is just a basic IPC primitive; it has no impact on an application's * lifecycle, and is valid only as long as the process that created it continues to run. * To use this correctly, you must be doing so within the context of a top-level * application component (a {@link android.app.Service}, {@link android.app.Activity}, * or {@link android.content.ContentProvider}) that lets the system know your process * should remain running.</p> * * <p>You must keep in mind the situations in which your process * could go away, and thus require that you later re-create a new Binder and re-attach * it when the process starts again. For example, if you are using this within an * {@link android.app.Activity}, your activity's process may be killed any time the * activity is not started; if the activity is later re-created you will need to * create a new Binder and hand it back to the correct place again; you need to be * aware that your process may be started for another reason (for example to receive * a broadcast) that will not involve re-creating the activity and thus run its code * to create a new Binder.</p> * * @see IBinder */
public class Binder implements IBinder {
...
/** * Use information supplied to attachInterface() to return the * associated IInterface if it matches the requested * descriptor. */
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
/** * Default implementation rewinds the parcels and calls onTransact. On * the remote side, transact calls into the binder to do the IPC. */
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
if (code == INTERFACE_TRANSACTION) {
reply.writeString(getInterfaceDescriptor());
return true;
} else if (code == DUMP_TRANSACTION) {
ParcelFileDescriptor fd = data.readFileDescriptor();
String[] args = data.readStringArray();
if (fd != null) {
try {
dump(fd.getFileDescriptor(), args);
} finally {
IoUtils.closeQuietly(fd);
}
}
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
return true;
} else if (code == SHELL_COMMAND_TRANSACTION) {
ParcelFileDescriptor in = data.readFileDescriptor();
ParcelFileDescriptor out = data.readFileDescriptor();
ParcelFileDescriptor err = data.readFileDescriptor();
String[] args = data.readStringArray();
ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
try {
if (out != null) {
shellCommand(in != null ? in.getFileDescriptor() : null,
out.getFileDescriptor(),
err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
args, shellCallback, resultReceiver);
}
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
IoUtils.closeQuietly(err);
// Write the StrictMode header.
if (reply != null) {
reply.writeNoException();
} else {
StrictMode.clearGatheredViolations();
}
}
return true;
}
return false;
}
...
}
複製代碼
咱們瞅一眼註釋就能看出Binder
的重要性,它不只用在AIDL
,同時運用在Service
、Activity
和ContentProvider
中。註釋中說了不少,總結成一句話:這個類很重要,你會不止一次地回頭看它。看到這裏,咱們來分析如下咱們本身寫的代碼。
首先,咱們在建立服務端遠程服務時調用TicketsManager.Stub()
方法建立了一個Binder
對象,這個Binder
對象中重寫了getTicketsList()
和addTickets(Tickets tickets)
方法。最後在服務被綁定時將這個binder
對象轉成IBinder
返回出去。
第二步,咱們須要建立遠程服務,並綁定。
第三步,建立遠程對象時須要傳入一個ServiceConnection
對象,因此咱們在客戶端又建立ServiceConnection
對象,並在其onServiceConnected
方法回調中獲取了一個IBinder
對象,這個IBinder
對象就是第一步一步中傳遞過來的。拿到這個對象以後,咱們又調用了TicketsManager.Stub.asInterface(service)
方法,將IBinder
對象傳入,得到一個TicketsManager
對象。自此,客戶端和服務端算是鏈接到一塊兒。咱們看一下TicketsManager.Stub.asInterface(service)
內部的調用狀況。
public static abstract class Stub extends android.os.Binder implements com.a00123.aidlservice.TicketsManager {
private static final java.lang.String DESCRIPTOR = "com.a00123.aidlservice.TicketsManager";
/** * Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/** * Cast an IBinder object into an com.a00123.aidlservice.TicketsManager interface, * generating a proxy if needed. */
public static com.a00123.aidlservice.TicketsManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.a00123.aidlservice.TicketsManager))) { //1
return ((com.a00123.aidlservice.TicketsManager) iin);
}
return new com.a00123.aidlservice.TicketsManager.Stub.Proxy(obj);
}
private static class Proxy implements com.a00123.aidlservice.TicketsManager {
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 boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getTicketsList: { //3
data.enforceInterface(descriptor);
java.util.List<com.a00123.aidlservice.Tickets> _result = this.getTicketsList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addTickets: { //3
data.enforceInterface(descriptor);
com.a00123.aidlservice.Tickets _arg0;
if ((0 != data.readInt())) {
_arg0 = com.a00123.aidlservice.Tickets.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addTickets(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
@Override
public java.util.List<com.a00123.aidlservice.Tickets> getTicketsList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.a00123.aidlservice.Tickets> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getTicketsList, _data, _reply, 0); //2
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getTicketsList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.a00123.aidlservice.Tickets.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
...
@Override
public void addTickets(com.a00123.aidlservice.Tickets tickets) 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 ((tickets != null)) {
_data.writeInt(1);
tickets.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addTickets, _data, _reply, 0); //2
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addTickets(tickets);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
public static com.a00123.aidlservice.TicketsManager sDefaultImpl;
}
}
複製代碼
上面的代碼咱們已經看過一次了,咱們在來看如下。在調用TicketsManager.Stub.asInterface
方法以後,代碼會來到註釋1處。在以前的源碼分析中咱們知道這裏是對進程進行判斷,若是屬於同一進程,則會返回本地的TicketManager
;若是不屬於同一進程,則會返回Proxy
對象。
第四步,在客戶端的MainActivity
中咱們調用了ticketsManager.getTicketsList()
方法,其實這個方法就是調用了Proxy
對象的getTicketsList()
方法。在這個方法中會調用mRemote.transact
方法,並把參數傳入了進去。咱們看到傳入的第一個參數是Stub.TRANSACTION_addTickets
,這個參數是標識操做類型。同時,這個mRomote
對象,其實就是一個TicketsManager
對象,你們不要被繞暈。在調用transact(...)
方法時,內部會去調用onTransact(...)
方法,也就是在註釋3處。在註釋3處,首先判斷了要操做的類型,這裏的this就是咱們在服務端的TicketManagerService
中定義的那個mBinder
對象,它內部會獲取到一個List
集合,並賦值給_result
變量。因爲有返回值。因此進行了reply.writeTypedList(_result)
方法,該方法。。。。。
第五步,因爲onTransact(...)
方法中已經返回true,回到註釋2處,將結果進行反序列化_reply.createTypedArrayList(com.a00123.aidlservice.Tickets.CREATOR)
,最後將結果返回到客服端。還有另一個addTickets(Tickets tickets)
方法,這個方法執行流程與getTickets()
是一致的,這裏就不作贅述。
至此,咱們已經將AIDL的原理進行了說明,如今就讓咱們進行小結一下。
一、當咱們建立帶有抽象方法的
XXX.aidl
文件時,系統會自動幫咱們建立一個對應的XXXManager
文件,若是有時間的話,咱們能夠本身實現。
二、在服務服務端建立一個Service
對象,並將第一步中的XXXManager
中子類Stub
的實現對象在onBind()
方法返回出去。
三、在客戶端建立一個XXXManager.Stub
對象,並調用asInterface()
方法將服務完成鏈接後的IBinder
對象傳進去,這個IBinder
對象就是咱們在第二步中的onBind()
方法中返回的XXXManager.Stub
對象。在調用asInterface
時,其內部對進程進行了判斷,若是是不一樣的進程,則會返回一個XXXManager
的Proxy
代理對象。
四、咱們在客戶端調用XXXManager
中獲取數據、修改數據方法時,會走到XXXManager.Stub
對象的transact
方法,並傳入參數。在transact
方法內部還將會調用XXXManager.Stub
中的onTransact
方法,在這個方法中回首先根據傳入的參數進行操做類型的判斷,而後對數據進行序列化或者賦值等操做,最後將數據作返回或者存入修改
因爲篇幅有限,這篇文章暫時先分析到這裏。本人資歷尚淺,能力有限,若是有哪裏寫的不對的地方,歡迎各位大佬批評指正。關於Binder機制的總結和其餘IPC方式,我會在後續的Android中的IPC機制(二)中繼續分析,敬請期待。