如果一個(gè)進(jìn)程占用內(nèi)存超過了這個(gè)內(nèi)存限制,就會(huì)報(bào)OOM的問題,很多涉及到大圖片的頻繁操作或者需要讀取一大段數(shù)據(jù)在內(nèi)存中使用時(shí),很容易報(bào)OOM的問題。為了徹底地解決應(yīng)用內(nèi)存的問題,Android引入了多進(jìn)程的概念,它允許在同一個(gè)應(yīng)用內(nèi),為了分擔(dān)主進(jìn)程的壓力,將占用內(nèi)存的某些頁(yè)面單獨(dú)開一個(gè)進(jìn)程,比如Flash、視頻播放頁(yè)面,頻繁繪制的頁(yè)面等。
一. 什么是多進(jìn)程?
多進(jìn)程就是多個(gè)進(jìn)程的意思,那么什么是進(jìn)程呢?
當(dāng)一個(gè)應(yīng)用在開始運(yùn)行時(shí),系統(tǒng)會(huì)為它創(chuàng)建一個(gè)進(jìn)程,一個(gè)應(yīng)用默認(rèn)只有一個(gè)進(jìn)程,這個(gè)進(jìn)程(主進(jìn)程)的名稱就是應(yīng)用的包名。
進(jìn)程的特點(diǎn):
- 進(jìn)程是系統(tǒng)資源和分配的基本單位,而線程是調(diào)度的基本單位。
- 每個(gè)進(jìn)程都有自己獨(dú)立的資源和內(nèi)存空間
- 其它進(jìn)程不能任意訪問當(dāng)前進(jìn)程的內(nèi)存和資源
- 系統(tǒng)給每個(gè)進(jìn)程分配的內(nèi)存會(huì)有限制
根據(jù)上邊的引言和進(jìn)程的特點(diǎn)可以看出,使用多進(jìn)程的場(chǎng)景為:需要使apk所使用的內(nèi)存限制擴(kuò)大。
二. 進(jìn)程的等級(jí)
按優(yōu)先級(jí)可以分為五類,優(yōu)先級(jí)從高到低排列:
- 前臺(tái)進(jìn)程:該進(jìn)程包含正在與用戶進(jìn)行交互的界面組件,比如一個(gè)Activity。在接收關(guān)鍵生命周期方法時(shí)會(huì)讓一個(gè)進(jìn)程臨時(shí)提升為前臺(tái)進(jìn)程,包括任何服務(wù)的生命周期方法onCreate()和onDestroy()和任何廣播接收器onReceive()方法。這樣做確保了這些組件的操作是有效的原子操作,每個(gè)組件都能執(zhí)行完成而不被殺掉。
- 可見進(jìn)程:該進(jìn)程中的組件雖然沒有和用戶交互,但是仍然可以被看到。activity可見的時(shí)候不一定在前臺(tái)。一個(gè)簡(jiǎn)單的例子是前臺(tái)的 activity 使用對(duì)話框啟動(dòng)了一個(gè)新的 activity 或者一個(gè)透明 activity 。另一個(gè)例子是當(dāng)調(diào)用運(yùn)行時(shí)權(quán)限對(duì)話框時(shí)(事實(shí)上它就是一個(gè) activity!)。
- 服務(wù)進(jìn)程:該進(jìn)程包含在執(zhí)行后臺(tái)操作的服務(wù)組件,比如播放音樂的Service。對(duì)于許多在后臺(tái)做處理(如加載數(shù)據(jù))而沒有立即成為前臺(tái)服務(wù)的應(yīng)用都屬于這種情況。
請(qǐng)?zhí)貏e注意從onStartCommand()返回的常量,如果服務(wù)由于內(nèi)存壓力被殺掉,它表示控制什么發(fā)生什么:
START_STICKY表示希望系統(tǒng)可用的時(shí)候自動(dòng)重啟服務(wù),但不關(guān)心是否能獲得最后一次的 Intent (例如,可以重建自己的狀態(tài)或者控制自己的 start/stop 生命周期)。
START_REDELIVER_INTENT是為那些在被殺死之后重啟時(shí)重新獲得 Intent 的服務(wù)的,直到用傳遞給 onStartCommand() 方法的 startId 參數(shù)調(diào)用stopSelf()為止。這里會(huì)使用 Intent 和 startId 作為隊(duì)列完成工作。
START_NOT_STICKY用于那些殺掉也沒關(guān)系的服務(wù)。這適合那些管理周期性任務(wù)的服務(wù),它們只是等待下一個(gè)時(shí)間窗口工作。 - 后臺(tái)進(jìn)程:該進(jìn)程包含的組件沒有與用戶交互,用戶也看不到 Service。在一般操作場(chǎng)景下,設(shè)備上的許多內(nèi)存就是用在這上面的,使可以重新回到之前打開過的某個(gè) activity 。
- 空進(jìn)程:沒有任何界面組件、服務(wù)組件,或觸發(fā)器組件,只是出于緩存的目的而被保留(為了更加有效地使用內(nèi)存而不是完全釋放掉),只要 Android 需要可以隨時(shí)殺掉它們。
三. 多進(jìn)程的創(chuàng)建
Android多進(jìn)程創(chuàng)建很簡(jiǎn)單,只需要在AndroidManifest.xml的聲明四大組件的標(biāo)簽中增加”android:process”屬性即可。命名之后,就成了一個(gè)單獨(dú)的進(jìn)程。
process分私有進(jìn)程和全局進(jìn)程:
- 私有進(jìn)程的名稱前面有冒號(hào),例如:
<service android:name=".MusicService"
android:process=":musicservice"/>
- 全局進(jìn)程的名稱前面沒有冒號(hào),例如:
<service android:name=".MusicService"
android:process="com.trampcr.musicdemo.service"/>
為了節(jié)省系統(tǒng)內(nèi)存,在退出該Activity的時(shí)候可以將其殺掉(如果沒有人為殺掉該進(jìn)程,在程序完全退出時(shí)該進(jìn)程會(huì)被系統(tǒng)殺掉)。
多進(jìn)程被創(chuàng)建好了,應(yīng)用運(yùn)行時(shí)就會(huì)對(duì)進(jìn)程進(jìn)行初始化,如果一個(gè)application中有多個(gè)進(jìn)程,在進(jìn)行全局初始化時(shí),多進(jìn)程會(huì)被初始化多次。
解決辦法:判斷當(dāng)前進(jìn)程,然后做相應(yīng)的初始化操作。
四. 多進(jìn)程間的通信IPC
IPC:InterProcess Communication,即進(jìn)程間通信。
我們知道,同一個(gè)進(jìn)程的多個(gè)線程是共享該進(jìn)程的所有資源,但多個(gè)進(jìn)程間內(nèi)存是不可見的,也就是說多個(gè)進(jìn)程間內(nèi)存是不共享的。那么進(jìn)程間是如何進(jìn)行通信的呢?
Android中提供了三種方法:
- 系統(tǒng)實(shí)現(xiàn)。
- AIDL(Android Interface Definition Language,Android接口定義語言):大部分應(yīng)用程序不應(yīng)該使用AIDL去創(chuàng)建一個(gè)綁定服務(wù),因?yàn)樗枰嗑€程能力,并可能導(dǎo)致一個(gè)更復(fù)雜的實(shí)現(xiàn)。
- Messenger:利用Handler實(shí)現(xiàn)。(適用于多進(jìn)程、單線程,不需要考慮線程安全),其底層基于AIDL。
使用Messenger
如需讓服務(wù)與遠(yuǎn)程進(jìn)程通信,則可使用Messenger為服務(wù)提供接口。
定義一個(gè)MessengerService繼承自Service,并在AndroidManifest.xml中聲明并給一個(gè)進(jìn)程名,使該服務(wù)成為一個(gè)單獨(dú)的進(jìn)程。代碼如下:
MessengerService.java
public class MessengerService extends Service{
class IncomingHandler extends Handler{
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 0:
Toast.makeText(getApplicationContext(), "hello, trampcr", Toast.LENGTH_SHORT).show();
break;
}
}
}
Messenger mMessenger = new Messenger(new IncomingHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}
AndroidManifest.xml文件的配置如下:
<service android:name=".MessengerService"
android:process="com.trampcr.messenger.service"/>
MessengerActivity.java
public class MessengerActivity extends Activity{
private boolean mBound;
private Messenger mMessenger;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMessenger = null;
mBound = false;
}
};
public void sayHello(View v){
if(!mBound){
return;
}
Message msg = Message.obtain(null, 0 , 0, 0);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
if(mBound){
unbindService(mServiceConnection);
mBound = false;
}
}
}
通過以上代碼,可以看到Messenger的使用方法:
- 服務(wù)實(shí)現(xiàn)一個(gè)Handler,由其接收來自客戶端的每個(gè)調(diào)用的回調(diào)。
- Handler用于創(chuàng)建Messenger對(duì)象(對(duì)Handler的引用)。
- Messenger創(chuàng)建一個(gè)IBinder,服務(wù)通過onBind()使其返回客戶端。
- 客戶端使用IBinder將Messenger(引用服務(wù)的Handler)實(shí)例化,然后使用后者將Message對(duì)象發(fā)送給服務(wù)。
- 服務(wù)在其Handler中(具體地講,是在handleMessage()方法中)接收每個(gè)Message。
這樣,客戶端并沒有調(diào)用服務(wù)的“方法”。而客戶端傳遞的“消息”(Message對(duì)象)是服務(wù)在其Handler中接收的。
以上代碼實(shí)現(xiàn)的應(yīng)用,剛打開會(huì)彈出一個(gè)binding,binding表示打開應(yīng)用Activity就通過Messenger連接了一個(gè)服務(wù)進(jìn)程,然后點(diǎn)擊say hello會(huì)彈出hello,trampcr,這表示了Activity通過Messenger將Message發(fā)送給了服務(wù)進(jìn)程。如下圖:
使用AIDL
AIDL是一種接口描述語言,通常用于進(jìn)程間通信。
使用AIDL的步驟:
- 創(chuàng)建AIDL,在main下新建一個(gè)文件夾aidl,然后在aidl下新建AIDL文件,這時(shí)系統(tǒng)會(huì)自動(dòng)為該文件創(chuàng)建一個(gè)包名。
aidl文件中會(huì)有一個(gè)默認(rèn)的basicType方法,我們?yōu)樗黾右粋€(gè)getName方法。代碼如下:
interface IMyAidlInterface {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getName(String nickName);
}
以上是我們自己創(chuàng)建的aidl文件,系統(tǒng)還會(huì)自動(dòng)生成aidl代碼,所在位置為:build/generated/source/aidl下debug和release,但是此時(shí)debug下沒有任何東西,可以rebuild或運(yùn)行一下程序,再次打開debug,發(fā)現(xiàn)生成了一個(gè)包和一個(gè)aidl文件。
- 在java下新建一個(gè)類AIDLService繼承自Service。代碼如下:
public class AIDLService extends Service {
IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getName(String nickName) throws RemoteException {
return "aidl " + nickName;
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
}
- 在AndroidManifest.xml中注冊(cè),并給一個(gè)進(jìn)程名,是該服務(wù)成為一個(gè)獨(dú)立的進(jìn)程。
<service android:name=".AIDLService"
android:process="com.aidl.test.service"/>
- 在MainActivity中進(jìn)行與AIDLService之間的進(jìn)程間通信。代碼如下:
public class MainActivity extends AppCompatActivity {
private Button mBtnAidl;
private IMyAidlInterface mIMyAidlInterface;
ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnAidl = (Button) findViewById(R.id.btn_aidl);
bindService(new Intent(MainActivity.this, AIDLService.class), mServiceConnection, BIND_AUTO_CREATE);
mBtnAidl.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mIMyAidlInterface != null){
try {
String name = mIMyAidlInterface.getName("I'm nick");
Toast.makeText(MainActivity.this, "name = " + name, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
}
在Activity中利用bindService與AIDLService進(jìn)行連接,通過IMyAidlInterface實(shí)例與AIDLService進(jìn)程進(jìn)行通信,如下圖所示:
五.序列化插件
Parcelable code generate:自動(dòng)生成實(shí)現(xiàn)了Parcelable接口的對(duì)象。