什么是進程择卦?按照操作系統(tǒng)中的描述,進程一般指一個執(zhí)行單元醉鳖,在PC和移動設備上指一個程序或者一個應用捡硅。android系統(tǒng)是基于Linux的,一個進程就對應一個Dalvik虛擬機盗棵,一般一個應用程序就是一個進程壮韭,這允許應用運行在一個相互隔離的環(huán)境中,不被其他應用程序/進程干擾。通常來說,當你想啟動一個應用程序,Android創(chuàng)建一個進程(從Zygote中fork出來的),并創(chuàng)建一個主線程纹因,然后開始運行MainActivity喷屋。另外,補充一下瞭恰,如果在應用里面開啟別的進程逼蒙,那么這個應用就不僅僅是一個進程了,這時候就需要考慮進程間通信了。
需要用到多進程通信的情況一般分為兩種是牢。第一種情況是一個應用因為某些原因自身需要采用多進程來實現(xiàn)僵井,比如有些模塊由于特殊原因需要運行在單獨的進程中,又或者為了加大一個應用的內(nèi)存所以通過多進程方式來獲取多分內(nèi)存空間驳棱。Android對單個進程所使用的最大空間做了限制批什,不同設備有不同的大小,最開始的android機器一般是16MB社搅,現(xiàn)在的主流手機一般是64MB驻债,128MB,有的手機會更高形葬。第二種情況是當前應用需要向其他應用獲取數(shù)據(jù)合呐,由于是兩個應用,所以必須采用跨進程的方式獲取所需數(shù)據(jù)笙以。
1淌实、多線程的開啟方式
今天先認識一下進程,在應用中開啟多進程非常方便猖腕,通過給四大組件指定 android:process 屬性可以了拆祈,當然,也有一種非常規(guī)的方式倘感,就是通過JNI在native層去fork一個新的進程放坏,這種方式的情況不多。多進程看起來很簡單老玛,但是在使用的時候要非常謹慎淤年。
下面是在一個應用用創(chuàng)建多進程的示例
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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=".SecondActivity"
android:process=":remote"
/>
<activity
android:name=".ThirdActivity"
android:process="com.example.helloapplication.remote"
/>
</application>
在這里為SecondActivity和ThirdActivity分別指定了process屬性,但是寫法不大一樣蜡豹,依次打開MainActivity互亮,SecondActivity,ThirdActivity后余素,進程是這樣的
demo的包名是com.example.helloapplication,其中MainActivity沒有指定process屬性炊昆,默認的進程名就是此包名桨吊,SecondActivity和ThirdActivity的process名分別為:remote和com.example.helloapplication.remote,它們有什么分別呢凤巨,“ :remote” 是指它的進程名要在前面加上包名视乐,所以SecondActivity的進程名就是com.example.helloapplication:remote,另外呢敢茁,還有一個區(qū)別就是佑淀,以“:”開頭的進程屬于當前應用的私有進程,其他應用的組件不可以和它跑在一個進程中彰檬,而不以“:”開頭的進程屬于全局進程伸刃,其他應用通過ShareUID方式可以和它跑在同一個進程中谎砾。
PS:Android系統(tǒng)會為每個應用分配一個唯一的UID,具有相同UID的應用才能共享數(shù)據(jù)捧颅,但是景图,兩個應用跑在一個進程中是有要求的需要這兩個應用擁有相同的ShareUID并且簽名相同才可以,在這種情況下碉哑,它們可以互相訪問對方的私有數(shù)據(jù)挚币,比如data目錄,組件信息等扣典。
2妆毕、多線程模式的運行機制和注意事項
2.1 Application啟動
先看一個列子,我在Application的onCreate里面打印了下進程名贮尖,依次打開三個Activity笛粘,輸出如下:
打印了三條記錄,Application的onCreate方法執(zhí)行了三次远舅?沒錯闰蛔,當開了一個新進程的時候,系統(tǒng)要分配獨立的虛擬機图柏,這個過程其實就是啟動一個應用的過程序六,相當于系統(tǒng)又把這個應用重新啟動了一遍,既然重新啟動了蚤吹,那么自然會創(chuàng)建新的Application例诀,那么如果我們在開發(fā)過程中想要在Application中做一些初始化工作,又要保證這些工作只執(zhí)行一遍怎么辦呢裁着,上代碼
@Override
public void onCreate() {
super.onCreate();
Log.e("info", "application.onCreate:" + getCurProcessName(this));
if (getPackageName().equals(getCurProcessName(this))) {
initApp();
}
}
private void initApp() {
Log.e("info", "初始化");
}
private String getCurProcessName(Context context) {
int pid = android.os.Process.myPid();
ActivityManager mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
if (appProcess.pid == pid) {
return appProcess.processName;
}
}
return null;
}
日志如下:
那么問題又來了繁涂,如果初始化的過程中改變了一些變量的值,比如application里面一個變量i=0;在initApp方法中使i=10二驰;SecondActivity進程開啟后讓i=20;ThirdActivity進程使i=30扔罪;在MainActivity,SecondActivity桶雀,ThirdActivity中分別打印變量i的值矿酵,結(jié)果如何呢,答案是10矗积,20全肮,30,因為三個進程分別對應三個Application實例棘捣,所以辜腺,以后面試就要注意了,一個應用里面不一定只有一個Application實例喲!
2.2 關(guān)于數(shù)據(jù)
剛才已經(jīng)嘗試過改變Application中的成員變量评疗,那么靜態(tài)變量怎么樣呢测砂?
寫了一個靜態(tài)變量如下
public class Person {
public static int age;
}
在MainActivity的onCreate 方法中使Person.age=10;分別在三個Activity鐘打印,結(jié)果會怎么樣壤巷,SecondActivity和ThirdActivity對應進程中Person.age會等于10嗎邑彪?
很顯然,答案是否定的胧华,那么為什么成員變量和靜態(tài)變量都會不一樣呢寄症?Android每個進程都分配一個獨立的虛擬機,不同的虛擬機在內(nèi)存分配上有不同的地址空間矩动,這就導致在不同的虛擬機中訪問同一個類的對象會產(chǎn)生多份副本有巧,就拿Person類來說吧,三個進程中悲没,每個進程都有一個Person類篮迎,并且這三個類是互不干擾的,在一個進程中修改age的值只會影響當前進程示姿,對其他進程不會造成影響甜橱,恩,沒錯栈戳,就是這樣高級岂傲。
那么如果使用多進程的話,應該注意哪些問題嘞子檀?
1.靜態(tài)成員和單例模式完全失效
2.Application會多次創(chuàng)建
3.線程同步機制完全失效
4.SharedPreference的可靠性下降
其中第3個問題镊掖,如果不是一塊內(nèi)存了,那么不管是鎖對象還是鎖全局類都無法保證線程同步褂痰,因為不同進程鎖的不是同一個對象亩进;第4個問題,官方文檔介紹SharedPreference不支持跨進程調(diào)用缩歪,當然了归薛,因為SharedPreference底層是通過讀寫XML文件來實現(xiàn)的,并發(fā)執(zhí)行肯定會出現(xiàn)問題匪蝙。
所以主籍,在使用多進程時候,一定要謹慎骗污,但是我們不能因為多進程有很多問題就不去使用,雖然不能共享內(nèi)存沈条,可是我們可以通過垮進程通信的方式實現(xiàn)數(shù)據(jù)交互需忿,下篇會介紹下Android進程間通信(IPC)的一些方式,敬請期待!
部分參考《Android開發(fā)藝術(shù)探索》