Android 中Application是單例只嚣,這個問題可能大家會毫不猶豫的回答正確
但是沮稚,如果APP中如果有集成一些第三方SDK的
并且在Application中加了打印的可能就會發(fā)現(xiàn),APP啟動的時候
怎么onCreate中的打印走了多次
不是說Application只會實例化一次的嗎册舞?
因為onCreate走了多次蕴掏,說明創(chuàng)建了多個
那這個問題答案應(yīng)該明朗了,在某種情況下,Application不唯一了
那這種情況是什么情況呢盛杰?
答案是:多進(jìn)程
一般我們開發(fā)可能極少挽荡,除非一些特別的APP,可能我們都不會指定多進(jìn)程
那為啥集成了第三方SDK會出現(xiàn)這種情況呢
是因為有些SDK指定了組件運(yùn)行在特別的進(jìn)程
那為啥第三方SDK會使用多進(jìn)程即供?多進(jìn)程帶來的好處是什么定拟?又有什么壞處呢?
進(jìn)程
Android系統(tǒng)是底層是由Linux改造而來的
進(jìn)程系統(tǒng)也是一致的逗嫡,進(jìn)程青自,就是程序的具體實現(xiàn)
當(dāng)程序第一次啟動,Android會啟動一個Linux進(jìn)程(具體由Zygote fork出來)和一個主線程
默認(rèn)的情況下祸穷,所有組件都將運(yùn)行在該進(jìn)程內(nèi)
同一個應(yīng)用由系統(tǒng)分配一個獨立的Linux賬戶性穿,應(yīng)用的產(chǎn)生的所有進(jìn)程,都會是這同一個Linux賬戶
多進(jìn)程Application會創(chuàng)建多個
很明顯帶來的問題就是Application的onCreate方法會執(zhí)行多次
如果在onCreate方法中雷滚,做了初始化的操作需曾,將會導(dǎo)致多次初始化操作
如果是啟動的時候就執(zhí)行的,將會導(dǎo)致啟動時間延長
將組件指定在單獨的進(jìn)程
指定多進(jìn)程是在AndroidManifest.xml里面配置
Android四大組件activity祈远,service呆万,provider, receiver
可以通過android:process屬性來指定運(yùn)行所在的進(jìn)程
不指定默認(rèn)就是運(yùn)行在系統(tǒng)分配的進(jìn)程
也可以修改默認(rèn)進(jìn)程 設(shè)置Application的android:process屬性,來設(shè)置所有組件的默認(rèn)進(jìn)程
進(jìn)程名分兩種:
- 如果以冒號開頭车份,比如":com.gaode.map"
這種情況下谋减,是該APP私有的,進(jìn)程名是APP包名+冒號+后面的名字
<activity android:name=".TestActivity"
android:process=":com.gaode.map"/>
- 如果小寫字母以:開頭 比如"com.baidu.map"
該組件將運(yùn)行在以這個名字命名的進(jìn)程中
這種方式就可以讓不同應(yīng)用中的組件可以共享一個進(jìn)程
<activity android:name=".TestActivity"
android:process="com.baidu.map"/>
示例:
<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=".TestActivity"
android:process="com.baidu.map"/>
Application 完整代碼
public class BaseApplication extends Application {
private static final String APP_NAME = "com.qingguoguo.baseapp";
@Override
public void onCreate() {
super.onCreate();
Log.e("BaseApplication", "onCreate");
Log.e("BaseApplication", getProcessNameByPID(getApplicationContext(), android.os.Process.myPid()));
DBConfig.initGreenDao(this);
}
/**
* 判斷是否是主進(jìn)程
* @return
*/
public boolean isAppMainProcess() {
try {
int pid = android.os.Process.myPid();
String process = getProcessNameByPID(getApplicationContext(), pid);
return TextUtils.isEmpty(process) || APP_NAME.equalsIgnoreCase(process);
} catch (Exception e) {
return true;
}
}
/**
* 根據(jù) pid 獲取進(jìn)程名
* @param context
* @param pid
* @return
*/
public String getProcessNameByPID(Context context, int pid) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
if (manager == null) {
return "";
}
for (android.app.ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo == null) {
continue;
}
if (processInfo.pid == pid) {
return processInfo.processName;
}
}
return "";
}
}
在ManinActivity中的點擊 事件跳轉(zhuǎn)到TestActivity扫沼,將會導(dǎo)致Application再次初始化
底層原理參考:http://gityuan.com/2016/03/26/app-process-create/
如何避免Application多次初始化
上面的代碼已經(jīng)給出了解決方法出爹,就是判斷當(dāng)前進(jìn)程是不是主進(jìn)程
若不是可以跳過主進(jìn)程的初始化
可以參考文章:https://blog.csdn.net/sz_chrome/article/details/72911392
多進(jìn)程的好處
常駐后臺任務(wù)應(yīng)用
核心后臺服務(wù)模塊和其他UI模塊進(jìn)行分離,保證應(yīng)用能更穩(wěn)定的提供服務(wù)
從而提升用戶體驗
解決OOM問題
吃內(nèi)存的大圖缎除,WebView等另開進(jìn)程严就,避免主進(jìn)程OOM
多模塊開發(fā)
諸如下載服務(wù),監(jiān)控服務(wù)等等另開進(jìn)程
多進(jìn)程帶來的問題
靜態(tài)變量和單例模式完全失效
因為進(jìn)程之間器罐,內(nèi)存是相互獨立的梢为,所以VM方法區(qū)的靜態(tài)變量
也都是獨立的,單例模式基于靜態(tài)變量轰坊,所以單例也會失效
在兩個不同進(jìn)程訪問一個相同類的靜態(tài)變量铸董,值未必相同
線程同步機(jī)制完全失效
Java的同步機(jī)制是VM來進(jìn)行調(diào)度的,兩個進(jìn)程擁有兩個不同的VM
所以肴沫,同步也會在多進(jìn)程環(huán)境下失效粟害,Synchronized,volatile關(guān)鍵字
等都是基于VM級別的同步颤芬,所以不要跨進(jìn)程去使用線程同步我磁,比如
主進(jìn)程有個生產(chǎn)者孽文,子進(jìn)程的消費者是無法正常使用消費功能的,
只能通過跨進(jìn)程通信夺艰,讓主進(jìn)程的消費者去消費,然后再回調(diào)
Application會多次創(chuàng)建
每個新進(jìn)程在創(chuàng)建的時候沉衣,都會新建一個Application郁副,所以多進(jìn)程還
會導(dǎo)致Application多次創(chuàng)建的問題,onCreate方法會多次調(diào)用豌习,一般
我們都會在onCreate里初始化操作存谎,那么會多次初始化,最好也不要在
Application中設(shè)置過多的靜態(tài)變量肥隆,導(dǎo)致內(nèi)存增加
文件讀寫并發(fā)訪問的問題
文件指的泛指所有需要并發(fā)訪問的文件既荚,例如:本地文件,數(shù)據(jù)庫文件栋艳,
sharepreference等恰聘。由于Java中,文件鎖吸占、隊列機(jī)制都是VM級別的晴叨,
所以不同進(jìn)程訪問同一個文件鎖是不管用的。(通過C++可以實現(xiàn)多進(jìn)程
文件鎖機(jī)制矾屯,不過不在文本討論范圍內(nèi)兼蕊。)所以在實際開發(fā)過程中,還是
避免多進(jìn)程同時訪問統(tǒng)一文件件蚕,多利用Android中IPC的C/S思想孙技,提供服務(wù),
接口調(diào)用排作,避免直接去訪問對方進(jìn)程的文件或者數(shù)據(jù)庫牵啦,提升設(shè)計美感,
同時也能提升代碼的穩(wěn)定性