在計算機操作系統中,進程是進行資源分配和調度的基本單位。這對於基於Linux內核的Android系統也不例外。在Android的設計中,一個應用默認有一個(主)進程。但是我們通過配置可以實現一個應用對應多個進程。
本文將試圖對於Android中應用多進程做一些整理總結。
android:process
-
應用實現多進程需要依賴於android:process這個屬性
-
適用元素:Application, Activity, BroadcastReceiver, Service, ContentProvider。
-
通常情況下,這個屬性的值應該是”:“開頭。表示這個進程是應用私有的。無法在在跨應用之間共用。
-
如果該屬性值以小寫字母開頭,表示這個進程爲全局進程。可以被多個應用共用。(文章結尾會探討這個問題)
一個應用 android:process 簡單示例
1
2
3
|
<activity android:name=".MusicPlayerActivity" android:process=":music"/>
<activity android:name=".AnotherActivity" android:process="droidyue.com"/>
|
應用多進程有什麼好處
增加App可用內存
在Android中,默認情況下系統會爲每個App分配一定大小的內存。比如從最早的16M到後面的32M或者48M等。具體的內存大小取決於硬件和系統版本。
這些有限的內存對於普通的App還算是夠用,但是對於展示大量圖片的應用來說,顯得實在是捉襟見肘。
仔細研究一下,你會發現原來系統的這個限制是作用於進程的(畢竟進程是作爲資源分配的基本單位)。意思就是說,如果一個應用實現多個進程,那麼這個應用可以獲得更多的內存。
於是,增加App可用內存成了應用多進程的重要原因。
獨立於主進程
除了增加App可用內存之外,確保使用多進程,可以獨立於主進程,確保某些任務的執行和完成。
舉一個簡單的例子,之前的一個項目存在退出的功能,其具體實現爲殺掉進程。爲了保證某些統計數據上報正常,不受當前進程退出的影響,我們可以使用獨立的進程來完成。
多進程的不足與缺點
數據共享問題
-
由於處於不同的進程導致了數據無法共享內容,無論是static變量還是單例模式的實現。
-
SharedPreferences 還沒有增加對多進程的支持。
-
跨進程共享數據可以通過Intent,Messenger,AIDL等。
SQLite容易被鎖
-
由於每個進程可能會使用各自的SQLOpenHelper實例,如果兩個進程同時對數據庫操作,則會發生SQLiteDatabaseLockedException等異常。
-
解決方法:可以使用ContentProvider來實現或者使用其他存儲方式。
不必要的初始化
-
多進程之後,每個進程在創建的時候,都會執行自己的Application.onCreate方法。
-
通常情況下,onCreate中包含了我們很多業務相關的初始化,更重要的這其中沒有做按照進程按需初始化,即每個進程都會執行全部的初始化。
-
按需初始化需要根據當前進程名稱,進行最小需要的業務初始化。
-
按需初始化可以選擇簡單的if else判斷,也可以結合工廠模式
一些簡單的代碼示例
獲取當前的進程名
1
2
3
4
5
6
7
8
9
10
11
12
|
private String getCurrentProcessName() {
String currentProcName = "";
int pid = android.os.Process.myPid();
ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
if (processInfo.pid == pid) {
currentProcName = processInfo.processName;
break;
}
}
return currentProcName;
}
|
基本的進程初始化類
這個類用來每個進程共用的業務初始化邏輯。
1
2
3
4
5
6
|
public class AppInitialization {
@CallSuper
public void onAppCreate(Application application) {
Log.i("AppInitialization", "onAppCreate is being executed.");
}
}
|
工廠模式的應用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public class AppInitFactory {
public static AppInitialization getAppInitialization(String processName) {
AppInitialization appInitialization;
if (processName.endsWith(":game")) {
appInitialization = new GameAppInitialization();
} else if (processName.endsWith(":music")) {
appInitialization = new MusicAppInitialization();
} else {
appInitialization = new AppInitialization();
}
return appInitialization;
}
static class GameAppInitialization extends AppInitialization {
@Override
public void onAppCreate(Application application) {
super.onAppCreate(application);
Log.i("GameAppInitialization", "onAppCreate is being executed.");
}
}
static class MusicAppInitialization extends AppInitialization {
@Override
public void onAppCreate(Application application) {
super.onAppCreate(application);
Log.i("MusicAppInitialization", "onAppCreate is being executed.");
}
}
}
|
具體的調用時的代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public class MyApplication extends Application{
private static final String LOGTAG = "MyApplication";
@Override
public void onCreate() {
super.onCreate();
String currentProcessName = getCurrentProcessName();
Log.i(LOGTAG, "onCreate currentProcessName=" + currentProcessName);
AppInitialization appInitialization = AppInitFactory.getAppInitialization(currentProcessName);
if (appInitialization != null) {
appInitialization.onAppCreate(this);
}
}
}
|
是否需要多進程
判斷是否需要多進程,需要視具體情況而定。
內存限制
-
研究內存佔用居高不下的原因
-
如果是由內存泄漏導致,嘗試解決來降低內存佔用
-
如有必要,可以通過配置largeHeap嘗試解決
除了內存限制之外,還需要考慮是否真的需要獨立於主進程來執行某些操作。
關於android:process的其他問題
在android:process部分我們提到,如果這個屬性值以小寫字母開頭,那麼就是全局的進程,可以被其他應用共用。
所謂的共用,指的是不同的App的組件運行在同一個指定的進程中。
準備條件
受制於Android系統的安全機制,我們需要做到以下兩個準備條件纔可以。
-
這個應用使用同樣的簽名
-
兩個應用指定同一個android:sharedUserId的值
具體示例
第一個App的Manifest文件,AnotherActivity運行在名爲droidyue.com的進程中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.droidyue.androidmutipleprocesssample"
android:sharedUserId="droidyue.com"
>
<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=".AnotherActivity" android:process="droidyue.com"/>
</application>
</manifest>
|
第二個App的Manifest文件,SecondActivity運行在名爲droidyue.com的進程中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.jishuxiaoheiwu.accessfromanotherprocess"
android:sharedUserId="droidyue.com"
>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SecondActivity"
android:process="droidyue.com"
/>
</application>
</manifest>
|
上面的AnotherActivity和SecondActivity會運行在一個名爲droidyue.com的進程中,儘管他們位於不同的App中。