1、進(jìn)程和線程的概念:
在我的理解中乘凸,進(jìn)程是一段被操作系統(tǒng)執(zhí)行的指令集厕诡,操作系統(tǒng)在對(duì)資源>進(jìn)行分配和調(diào)度時(shí),進(jìn)程是基本單位翰意,進(jìn)程其實(shí)就是一個(gè)程序木人。而線程是操作系統(tǒng)調(diào)度的最小單元信柿。進(jìn)程可以包含多個(gè)線程冀偶,多個(gè)線程可共享進(jìn)程中的資源,另外線程的創(chuàng)建代價(jià)比線程要小
2渔嚷、多進(jìn)程的應(yīng)用場(chǎng)景:
通常需要用到多進(jìn)程的是进鸠,有常駐后臺(tái)的應(yīng)用,例如音樂(lè)播放器的后臺(tái)播放服務(wù)形病,健身跑步的計(jì)算步數(shù)和跑步路徑模塊等客年,這些模塊需要脫離界面運(yùn)行霞幅,而且需要長(zhǎng)時(shí)間的在系統(tǒng)中運(yùn)行,所以放在獨(dú)立的進(jìn)程中執(zhí)行是比較好的選擇量瓜。另外一種就是司恳,如果一些應(yīng)用很大,需要拆分模塊绍傲,為了增加應(yīng)用的內(nèi)存用量扔傅,就需要開(kāi)啟多個(gè)進(jìn)程來(lái)擴(kuò)大應(yīng)用的內(nèi)存用量,同時(shí)烫饼,將應(yīng)用模塊化猎塞,也更有利于解耦
3、實(shí)現(xiàn)多進(jìn)程的方式:
在android中實(shí)現(xiàn)多進(jìn)程有兩種方式杠纵,一種是在AndroidManifest文件中給組件設(shè)置android:process屬性荠耽,這樣,這個(gè)組件就運(yùn)行在設(shè)置的進(jìn)程中了比藻,另外一種就是利用jni在native層fork一個(gè)新的進(jìn)程出來(lái)
4铝量、android:process屬性的設(shè)置方式:
設(shè)置方式有兩種,一種是以“:xxx”的方式設(shè)置银亲,這種方式代表該進(jìn)程是應(yīng)用的私有進(jìn)程款违,其他應(yīng)用的組件不可在該進(jìn)程運(yùn)行,另外一種就是以“xxx.xx.xx”類似包名的方式來(lái)設(shè)置群凶,這種方式設(shè)置的進(jìn)程是全局的進(jìn)程插爹,其他應(yīng)用的組件也可以在該進(jìn)程上面運(yùn)行,但是shareUID必須一樣才可以
5请梢、在多進(jìn)程開(kāi)發(fā)中會(huì)遇到的問(wèn)題:
1赠尾、靜態(tài)成員和單例模式完全失效:因?yàn)橄到y(tǒng)會(huì)為每個(gè)進(jìn)程分配獨(dú)立的虛擬機(jī),不同的虛擬機(jī)會(huì)有不同的內(nèi)存空間毅弧,這會(huì)導(dǎo)致不同的虛擬機(jī)訪問(wèn)同一個(gè)類的對(duì)象會(huì)產(chǎn)生多個(gè)副本气嫁,我們修改其中一個(gè)副本只會(huì)影響當(dāng)前進(jìn)程,因此够坐,靜態(tài)成員和單例模式就會(huì)完全失效
2寸宵、線程同步機(jī)制會(huì)完全失效:既然是不同的內(nèi)存空間,那鎖的就不是同一個(gè)對(duì)象元咙,因此無(wú)法保證線程同步
3梯影、Sharedpreferences的可靠性下降:因?yàn)镾haredPreferences不支持兩個(gè)進(jìn)程同時(shí)執(zhí)行寫(xiě)操作
3、Application會(huì)被多次創(chuàng)建:不同的進(jìn)程有不同的虛擬機(jī)庶香,當(dāng)啟動(dòng)一個(gè)新的進(jìn)程時(shí)甲棍,相當(dāng)于一個(gè)應(yīng)用的啟動(dòng)過(guò)程,那么當(dāng)然赶掖,就會(huì)創(chuàng)建一個(gè)新的Application
6感猛、使用Serializable時(shí)需要注意的問(wèn)題:
在實(shí)現(xiàn)Serializable的過(guò)程中七扰,雖然serialVersionUID不寫(xiě)是可以,但是這樣可能會(huì)造成在反序列化的過(guò)程失敗陪白,因?yàn)檫@個(gè)serialVersionUID在序列化的過(guò)程中會(huì)被存儲(chǔ)進(jìn)去颈走,在反序列化的時(shí)候會(huì)先比較對(duì)象中的serialVersionUID是否與類中的serialVersionUID相同,如果在類中沒(méi)有設(shè)置這個(gè)值咱士,那么系統(tǒng)會(huì)自動(dòng)計(jì)算hash值疫鹊,而如果這個(gè)類的結(jié)構(gòu)發(fā)生了改變,那么這個(gè)自動(dòng)計(jì)算的hash值就會(huì)重新計(jì)算司致,就會(huì)造成反序列化的失敗拆吆。因此,設(shè)置這個(gè)值的意義在于盡量讓反序列化可以成功脂矫,就算有時(shí)候這個(gè)類已經(jīng)發(fā)生了部分改變枣耀。另外一個(gè)就是靜態(tài)成員不參與序列化,使用transient關(guān)鍵字標(biāo)記的成員不參與序列化
7庭再、Serializable與Parcelable的區(qū)別:
Parcelable是為了Android平臺(tái)而定制的捞奕,它的操作稍微復(fù)雜,但是效率很高拄轻,因此在內(nèi)存的序列化上推薦使用Parcelable颅围,而Serializable因?yàn)樵谛蛄谢瘯r(shí)需要進(jìn)行大量的IO操作,因此雖然操作簡(jiǎn)單恨搓,但是開(kāi)銷很大院促,因此,在將對(duì)象序列化到存儲(chǔ)設(shè)備中或者在網(wǎng)絡(luò)中傳輸就推薦使用Serializable
8斧抱、實(shí)現(xiàn)多進(jìn)程間通信的方式
實(shí)現(xiàn)多進(jìn)程間的通信有很多種方式常拓,1、利用Bundle進(jìn)行數(shù)據(jù)的傳遞辉浦,它簡(jiǎn)單易用弄抬,但是只支持Bundle支持的數(shù)據(jù)類型,推薦在四大組件的進(jìn)程通信可以使用它宪郊。2掂恕、文件共享,這種方式也比較簡(jiǎn)單易用弛槐,但是這種方式不適合多并發(fā)的場(chǎng)景懊亡,而且無(wú)法進(jìn)行進(jìn)程間的即時(shí)通信。3丐黄、Messenger斋配。這種方式的底層是利用AIDL實(shí)現(xiàn)的孔飒,支持一對(duì)多的串行通信灌闺,支持低并發(fā)的即時(shí)通信艰争。3、ContentProvider桂对,ContentProvider的底層利用Binder實(shí)現(xiàn)甩卓,天生支持跨進(jìn)程通信,但是它只適合用來(lái)作為數(shù)據(jù)共享蕉斜。4逾柿、Socket,這種方式通過(guò)網(wǎng)絡(luò)傳輸字節(jié)流也可以實(shí)現(xiàn)跨進(jìn)程通信宅此,但實(shí)現(xiàn)稍微負(fù)責(zé)机错,適合網(wǎng)絡(luò)間的數(shù)據(jù)交換。5父腕、AIDL弱匪,最后一種,也是最常用的一種方式璧亮,功能強(qiáng)大萧诫,下面會(huì)詳細(xì)介紹
9、AIDL的一些理解
AIDL(Android Interface Difinition Language)是一種Android種獨(dú)有的定義語(yǔ)言枝嘶,它的主要作用是為了簡(jiǎn)化跨進(jìn)程通信代碼的編寫(xiě)帘饶,在編譯階段,我們編寫(xiě)的AIDL文件會(huì)生成相應(yīng)的跨進(jìn)程代碼群扶,簡(jiǎn)單來(lái)說(shuō)及刻,它就是一種簡(jiǎn)化的工具,沒(méi)有AIDL竞阐,我們一樣可以編寫(xiě)出跨進(jìn)程代碼提茁,只是會(huì)稍微繁瑣一點(diǎn)
AIDL只會(huì)接受基本的數(shù)據(jù)類型(通過(guò)實(shí)驗(yàn)short還不行),String和CharSequence類型馁菜,然后如果是自定義的對(duì)象茴扁,需要實(shí)現(xiàn)Parcelable接口,并且需要在AIDL文件中聲明出來(lái)汪疮,這樣才可以使用峭火,當(dāng)然也可以用集合(ArrayList,HashMap)作為參數(shù),但是集合中的元素也必須是AIDL支持的類型智嚷。另外需要注意的就是AIDL文件中的方法參數(shù)卖丸,是有一個(gè)數(shù)據(jù)流向的,通過(guò)in盏道,out稍浆,inout三個(gè)標(biāo)識(shí)來(lái)確定,如果參數(shù)設(shè)置為in,那么數(shù)據(jù)的流向就是客戶端->服務(wù)器(意思就是客戶端傳遞給服務(wù)器的信息衅枫,服務(wù)器可以收到嫁艇,但是服務(wù)器如果改動(dòng)了這個(gè)傳遞的對(duì)象逆粹,客戶端中的原對(duì)象是不會(huì)跟著發(fā)生改變的)空厌,如果參數(shù)設(shè)置為out咽袜,那么數(shù)據(jù)的流向就是服務(wù)端->客戶端(意思就是客戶端傳遞給服務(wù)端的參數(shù)挺庞,服務(wù)端是無(wú)法收到的退敦,但是服務(wù)端改動(dòng)了這個(gè)對(duì)象的內(nèi)容蹋凝,在客戶端的原對(duì)象是會(huì)跟著改變的)年鸳,如果參數(shù)設(shè)置為inout霍掺,那就代表著雙向流動(dòng)感凤,但是一般情況不提倡這樣設(shè)置悯周,因?yàn)闀?huì)增加開(kāi)銷
10、使用AIDL需要注意的地方:
在使用AIDL的過(guò)程中陪竿,會(huì)有一種情形队橙,就是當(dāng)服務(wù)端處理了一些業(yè)務(wù),需要主動(dòng)通知客戶端萨惑,而不是等待客戶端發(fā)起請(qǐng)求才去響應(yīng)捐康,那么這個(gè)時(shí)候就需要觀察者模式了,這個(gè)時(shí)候我們需要注冊(cè)監(jiān)聽(tīng)服務(wù)端的狀態(tài)庸蔼,這個(gè)時(shí)候服務(wù)端需要保存客戶端的監(jiān)聽(tīng)解总,因此,可能會(huì)有并發(fā)的情形姐仅,因此推薦使用RemoteCallbackList進(jìn)行保存花枫。另外就是有時(shí)候服務(wù)端進(jìn)程被kill掉,這時(shí)候會(huì)造成鏈接失敗掏膏,因此我們需要通過(guò)linkToDeath來(lái)綁定服務(wù)端劳翰,如果服務(wù)端進(jìn)程殺死,客戶端可以收到響應(yīng)馒疹,可以進(jìn)行重連操作佳簸。
11、AIDL的實(shí)際操作:
在實(shí)際開(kāi)發(fā)過(guò)程中颖变,我們?nèi)绻泻芏嗄K都需要用到AIDL的話生均,那么不可能每個(gè)模塊開(kāi)啟一個(gè)服務(wù)進(jìn)程來(lái)處理,這樣造成的系統(tǒng)資源損耗是巨大的腥刹,因此我們需要利用Binder連接池來(lái)作為中間媒介马胧,來(lái)鏈接各個(gè)AIDL的處理,下面的例子來(lái)演示相關(guān)實(shí)現(xiàn)
11.1 第一步
首先衔峰,創(chuàng)建兩個(gè)AIDL文件IPlayMedia.aidl和IMonitorDevice.aidl佩脊,代碼如下:
interface IPlayMedia {
void play(in String path);
void puase();
void stop();
String getCurrentMusicName();
}
interface IMonitorDevice {
void monitor(int a);
}
11.2 第二步
實(shí)現(xiàn)AIDL接口蛙粘,代碼如下:
// IPlayMedia接口實(shí)現(xiàn)
public class PlayMediaImpl extends IPlayMedia.Stub{
@Override
public void play(String path) throws RemoteException {
Log.e("TAG","播放音樂(lè):"+path);
}
@Override
public void puase() throws RemoteException {
Log.e("TAG","暫停播放");
}
@Override
public void stop() throws RemoteException {
Log.e("TAG","停止播放");
}
@Override
public String getCurrentMusicName() throws RemoteException {
Log.e("TAG","獲取當(dāng)前歌曲名稱");
return "七里香";
}
}
// IMonitorDevice的實(shí)現(xiàn)
public class MonitorDeviceImpl extends IMonitorDevice.Stub {
@Override
public void monitor(int a) throws RemoteException {
Log.e("TAG","監(jiān)控方法實(shí)現(xiàn)");
}
}
11.3 第三步
接下來(lái)需要?jiǎng)?chuàng)建一個(gè)BinderPool連接池AIDL接口,這個(gè)接口是直接與服務(wù)端進(jìn)程的服務(wù)交互的
interface BinderPool {
IBinder queryBinder(int binderCode);
}
11.4 第四步
接下來(lái)創(chuàng)建一個(gè)服務(wù)AIDLService,設(shè)置android:process屬性威彰,讓其可以在獨(dú)立的進(jìn)程運(yùn)行出牧,先不做任何的實(shí)現(xiàn),接著抱冷,創(chuàng)建BinderPoolUtils工具類崔列,代碼如下:
public class BinderPoolUtils {
public static final int BINDER_PLAY_MEDIA = 1;
public static final int BINDER_MONITOR_DEVICE = 2;
public static volatile BinderPoolUtils mInstance = null;
private Context mContext;
private test.com.testpoj.BinderPool mBinderPool;
private CountDownLatch mConnectBinderPoolDownLatch;
/**
* 單例模式
*/
public static BinderPoolUtils getInstance(Context context) {
if (mInstance == null) {
synchronized (BinderPoolUtils.class) {
if (mInstance == null) {
mInstance = new BinderPoolUtils(context);
}
}
}
return mInstance;
}
/**
* 查詢對(duì)應(yīng)的Binder對(duì)象
*/
public IBinder queryBinder(int binderCode) {
IBinder binder = null;
try {
if (mBinderPool != null) {
binder = mBinderPool.queryBinder(binderCode);
}
} catch (RemoteException e) {
e.printStackTrace();
}
return binder;
}
/**
* 構(gòu)造方法私有化
*/
private BinderPoolUtils(Context context) {
mContext = context;
connectBinderPoolService();
}
/**
* 綁定服務(wù)
*/
private synchronized void connectBinderPoolService() {
mConnectBinderPoolDownLatch = new CountDownLatch(1);
Intent intent = new Intent(mContext, AIDLService.class);
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
try {
mConnectBinderPoolDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mBinderPool = test.com.testpoj.BinderPool.Stub.asInterface(service);
try {
mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
mConnectBinderPoolDownLatch.countDown();
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient, 0);
mBinderPool = null;
connectBinderPoolService();
}
};
/**
* BinderPool鏈接池的實(shí)現(xiàn)
*/
public static class BinderPoolImpl extends test.com.testpoj.BinderPool.Stub {
@Override
public IBinder queryBinder(int binderCode) throws RemoteException {
IBinder binder = null;
switch (binderCode) {
case BINDER_PLAY_MEDIA:
binder = new PlayMediaImpl();
break;
case BINDER_MONITOR_DEVICE:
binder = new MonitorDeviceImpl();
break;
default:
break;
}
return binder;
}
}
}
有了上面的工具類梢褐,我們就可以在AIDLService中的onBinder方法中返回BinderPoolImpl的實(shí)現(xiàn)旺遮,然后我們調(diào)用的時(shí)候就可以這樣來(lái)調(diào)用:
BinderPoolUtils utils = BinderPoolUtils.getInstance(this);
IBinder playBinder = utils.queryBinder(BinderPoolUtils.BINDER_PLAY_MEDIA)
mPlayManager = IPlayMedia.Stub.asInterface(playBinder);
這樣我們就可以調(diào)用IPlayMedia接口中的方法了