android IPC機(jī)制

生活既是修行

1. android 中多進(jìn)程模式

android 進(jìn)程間通信的方式
1.Intent
2.文件共享
3.AIDL
4.Messenger
5.ContentProvider
6.RemoteViews
7.socket

1.1 多進(jìn)程的開啟方式

android中使用多進(jìn)程只有一種方法,就是給四大組件在AndroidMenifest 中指定android:processs屬性,那么問題來了,我們常常會(huì)使用兩種指定方式:

android:process=":remote"
android:process="com.android.test"

這兩種方式有何區(qū)別乏悄?

  • 1.":"的含義是要在進(jìn)程名“remote”前加上包名的陨晶,加":"是一種簡寫
    1. ":"開頭的進(jìn)程屬于當(dāng)前應(yīng)用的私有進(jìn)程泻蚊,其他應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程中栅哀。不以":"命名的進(jìn)程屬于全局進(jìn)程,其他應(yīng)用通過ShareUID 方式可以和它跑在同一個(gè)進(jìn)程中

android 系統(tǒng)會(huì)為每個(gè)應(yīng)用分配一個(gè)唯一的UID,相同UID的應(yīng)該可以共享數(shù)據(jù)而账,如果兩個(gè)應(yīng)用通過相同ShareUID跑在同一個(gè)進(jìn)程中胰坟,還需要有相同簽名才可以相互訪問私有數(shù)據(jù),比如 data目錄泞辐、組件信息等笔横,跑在同一進(jìn)程中竞滓,當(dāng)然可以共享內(nèi)存數(shù)據(jù)。
所有運(yùn)行在不同進(jìn)程中的四大組件吹缔,如果他們需要通過內(nèi)存來共享數(shù)據(jù)商佑,都會(huì)失敗
一般來說,多進(jìn)程會(huì)照成如下的一些問題:

1.靜態(tài)成員和單例模式完全失效
2.線程同步機(jī)制失效
3.SharePreferences 的可靠性下降
4.Application 可能多次創(chuàng)建

2. IPC中一些基礎(chǔ)概念

2.1Seriailzable 接口

Serializable 是Java 提供的一個(gè)序列化接口厢塘,是一個(gè)空接口茶没,為對象提供標(biāo)準(zhǔn)的序列化和反序列化操作,
SerialVersionUID 需指定晚碾,否則可能會(huì)導(dǎo)致反序列化失敗

2.2 Parcelable 接口

Parcelable接口也是一個(gè)接口抓半,只要實(shí)現(xiàn)這個(gè)接口,這個(gè)類對象就可以實(shí)現(xiàn)序列化并通過Intent格嘁、Binder傳遞

3.下面主要分析下使用AIDL文件進(jìn)行進(jìn)程間通信

  1. 舉一個(gè)使用aidl 進(jìn)行IPC通信的例子
    MainActivity 和PersonService 分屬于兩個(gè)進(jìn)程笛求,我們用aidl 進(jìn)行雙向通信
    完成 MainActivity創(chuàng)建person對象傳到service 中進(jìn)行保存,activity主動(dòng)向service
    拿所有數(shù)據(jù)糕簿,service以集合的方式返回所有數(shù)據(jù)

我們定義的IPersonService.aidl (aidl 文件路徑要和package定義的包名路勁下)

// IPersonService.aidl
 package com.prsioner.androidaidldemo;
 
 // Declare any non-default types here with import statements
 import com.prsioner.androidaidldemo.Person;
 interface IPersonService {
         void savePersonInfo(in Person person);
         List<Person> getAllPerson();
 }

Person.aidl

// Person.aidl
package com.prsioner.androidaidldemo;

// Declare any non-default types here with import statements

parcelable Person;

編譯后自動(dòng)生成的接口類IPersonService 在build/generated/source/aidl/....

activity 作為client 端通過bindService啟動(dòng)service進(jìn)程探入,傳入一個(gè)ServiceConnection 來接受服務(wù)端在
客戶端的引用 iService

IPersonService iService;
ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Log.e(tag,"onServiceConnected");
            iService = IPersonService.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(tag,"onServiceDisconnected()");
        }
};

activity 發(fā)送數(shù)據(jù)和接受數(shù)據(jù)

iService.savePersonInfo(person);

List<Person> personList = iService.getAllPerson();

service 服務(wù)端的部分代碼:

public IBinder onBind(Intent intent) {
        return iBinder;
    }


private final IPersonService.Stub iBinder = new IPersonService.Stub(){

@Override
public void savePersonInfo(Person person) throws RemoteException {
       if(person !=null){
                personList.add(person);
       }
       }

       @Override
       public List<Person> getAllPerson() throws RemoteException {
            return personList;
       }
};

我們來看aidl 完成通信的具體實(shí)現(xiàn)過程

package com.prsioner.androidaidldemo;
public interface IPersonService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.prsioner.androidaidldemo.IPersonService
{
private static final java.lang.String DESCRIPTOR = "com.prsioner.androidaidldemo.IPersonService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
 * Cast an IBinder object into an com.prsioner.androidaidldemo.IPersonService interface,
 * generating a proxy if needed.
 */
public static com.prsioner.androidaidldemo.IPersonService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.prsioner.androidaidldemo.IPersonService))) {
return ((com.prsioner.androidaidldemo.IPersonService)iin);
}
return new com.prsioner.androidaidldemo.IPersonService.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_savePersonInfo:
{
data.enforceInterface(descriptor);
com.prsioner.androidaidldemo.Person _arg0;
if ((0!=data.readInt())) {
_arg0 = com.prsioner.androidaidldemo.Person.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.savePersonInfo(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_getAllPerson:
{
data.enforceInterface(descriptor);
java.util.List<com.prsioner.androidaidldemo.Person> _result = this.getAllPerson();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.prsioner.androidaidldemo.IPersonService
{
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 void savePersonInfo(com.prsioner.androidaidldemo.Person person) 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 ((person!=null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_savePersonInfo, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.prsioner.androidaidldemo.Person> getAllPerson() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.prsioner.androidaidldemo.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAllPerson, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.prsioner.androidaidldemo.Person.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_savePersonInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getAllPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public void savePersonInfo(com.prsioner.androidaidldemo.Person person) throws android.os.RemoteException;
public java.util.List<com.prsioner.androidaidldemo.Person> getAllPerson() throws android.os.RemoteException;
}

IInterface接口: 所有用Binder傳輸數(shù)據(jù)的接口都必須繼承這個(gè)接口

Stub: 繼承自Binder 實(shí)現(xiàn)我們定義的aidl類接口IPersonService,其實(shí)就是作為服務(wù)端的一個(gè)binder對象
Stub 中有一個(gè)DESCRIPTOR,它是Binder的唯一標(biāo)識,其中兩個(gè)int常量是用來標(biāo)識我們在接口中定義的方法的

asInterface()方法 用于將服務(wù)端的Binder對象轉(zhuǎn)換為客戶端所需要的接口對象,該過程區(qū)分進(jìn)程懂诗,如果進(jìn)程一樣新症,
就返回服務(wù)端Stub對象本身,否則呢就返回封裝后的Stub.Proxy對象(服務(wù)端在客戶度的代理對象)

onTransact() 方法 是運(yùn)行在服務(wù)端的Binder線程中的响禽,當(dāng)客戶端發(fā)起遠(yuǎn)程請求后,在底層封裝后會(huì)交由此方法來處理荚醒。
通過code來區(qū)分客戶端請求的方法

Proxy代理對象類我們主要看一下我們定義的方法savePersonInfo()和getAllPerson()就可以了芋类,這兩個(gè)方法都是運(yùn)行在客戶端,
當(dāng)客戶端發(fā)起遠(yuǎn)程請求時(shí)界阁,_data會(huì)寫入?yún)?shù)侯繁,然后調(diào)用transact方法發(fā)起RPC(遠(yuǎn)程過程調(diào)用)請求,同時(shí)掛起當(dāng)前線程泡躯,
然后服務(wù)端的onTransact方法就會(huì)被調(diào)起贮竟,直到RPC過程返回后,當(dāng)前線程繼續(xù)執(zhí)行较剃,并從_reply取出返回值(如果有的話)咕别,并返回結(jié)果

最后附上示例代碼地址:https://github.com/prsioner/androidAIDLDemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市写穴,隨后出現(xiàn)的幾起案子惰拱,更是在濱河造成了極大的恐慌,老刑警劉巖啊送,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件偿短,死亡現(xiàn)場離奇詭異欣孤,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昔逗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門降传,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人勾怒,你說我怎么就攤上這事婆排。” “怎么了控硼?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵泽论,是天一觀的道長。 經(jīng)常有香客問我卡乾,道長翼悴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任幔妨,我火速辦了婚禮鹦赎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘误堡。我一直安慰自己古话,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布锁施。 她就那樣靜靜地躺著陪踩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悉抵。 梳的紋絲不亂的頭發(fā)上肩狂,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音姥饰,去河邊找鬼傻谁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛列粪,可吹牛的內(nèi)容都是我干的审磁。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岂座,長吁一口氣:“原來是場噩夢啊……” “哼态蒂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掺逼,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吃媒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后赘那,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體募舟,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年鸯乃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸟悴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片细诸。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屏歹,死狀恐怖季希,靈堂內(nèi)的尸體忽然破棺而出博敬,到底是詐尸還是另有隱情,我是刑警寧澤祭往,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布已骇,位于F島的核電站卵渴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蛹屿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岩榆。 院中可真熱鬧错负,春花似錦、人聲如沸勇边。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粒褒。三九已至识颊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間奕坟,已是汗流浹背祥款。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留月杉,地道東北人刃跛。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像苛萎,于是被迫代替她去往敵國和親桨昙。 傳聞我的和親對象是個(gè)殘疾皇子检号,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容