如果一個(gè)進(jìn)程占用內(nèi)存超過了這個(gè)內(nèi)存限制荣挨,就會報(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)存的某些頁面單獨(dú)開一個(gè)進(jìn)程哗脖,比如Flash、視頻播放頁面,頻繁繪制的頁面等才避。
一. 什么是多進(jìn)程橱夭?
多進(jìn)程就是多個(gè)進(jìn)程的意思,那么什么是進(jìn)程呢桑逝?
當(dāng)一個(gè)應(yīng)用在開始運(yùn)行時(shí)棘劣,系統(tǒng)會為它創(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)存會有限制
根據(jù)上邊的引言和進(jìn)程的特點(diǎn)可以看出糙俗,使用多進(jìn)程的場景為:需要使apk所使用的內(nèi)存限制擴(kuò)大。
二. 進(jìn)程的等級
按優(yōu)先級可以分為五類预鬓,優(yōu)先級從高到低排列:
- 前臺進(jìn)程:該進(jìn)程包含正在與用戶進(jìn)行交互的界面組件巧骚,比如一個(gè)Activity。在接收關(guān)鍵生命周期方法時(shí)會讓一個(gè)進(jìn)程臨時(shí)提升為前臺進(jìn)程珊皿,包括任何服務(wù)的生命周期方法onCreate()和onDestroy()和任何廣播接收器onReceive()方法网缝。這樣做確保了這些組件的操作是有效的原子操作,每個(gè)組件都能執(zhí)行完成而不被殺掉蟋定。
- 可見進(jìn)程:該進(jìn)程中的組件雖然沒有和用戶交互,但是仍然可以被看到草添。activity可見的時(shí)候不一定在前臺驶兜。一個(gè)簡單的例子是前臺的 activity 使用對話框啟動了一個(gè)新的 activity 或者一個(gè)透明 activity 。另一個(gè)例子是當(dāng)調(diào)用運(yùn)行時(shí)權(quán)限對話框時(shí)(事實(shí)上它就是一個(gè) activityT洞纭)抄淑。
- 服務(wù)進(jìn)程:該進(jìn)程包含在執(zhí)行后臺操作的服務(wù)組件,比如播放音樂的Service驰后。對于許多在后臺做處理(如加載數(shù)據(jù))而沒有立即成為前臺服務(wù)的應(yīng)用都屬于這種情況肆资。
請?zhí)貏e注意從onStartCommand()返回的常量,如果服務(wù)由于內(nèi)存壓力被殺掉灶芝,它表示控制什么發(fā)生什么:
START_STICKY表示希望系統(tǒng)可用的時(shí)候自動重啟服務(wù)郑原,但不關(guān)心是否能獲得最后一次的 Intent (例如,可以重建自己的狀態(tài)或者控制自己的 start/stop 生命周期)夜涕。
START_REDELIVER_INTENT是為那些在被殺死之后重啟時(shí)重新獲得 Intent 的服務(wù)的犯犁,直到用傳遞給 onStartCommand() 方法的 startId 參數(shù)調(diào)用stopSelf()為止。這里會使用 Intent 和 startId 作為隊(duì)列完成工作女器。
START_NOT_STICKY用于那些殺掉也沒關(guān)系的服務(wù)酸役。這適合那些管理周期性任務(wù)的服務(wù),它們只是等待下一個(gè)時(shí)間窗口工作。 - 后臺進(jìn)程:該進(jìn)程包含的組件沒有與用戶交互涣澡,用戶也看不到 Service贱呐。在一般操作場景下,設(shè)備上的許多內(nèi)存就是用在這上面的入桂,使可以重新回到之前打開過的某個(gè) activity 吼句。
- 空進(jìn)程:沒有任何界面組件、服務(wù)組件事格,或觸發(fā)器組件惕艳,只是出于緩存的目的而被保留(為了更加有效地使用內(nèi)存而不是完全釋放掉),只要 Android 需要可以隨時(shí)殺掉它們驹愚。
三. 多進(jìn)程的創(chuàng)建
Android多進(jìn)程創(chuàng)建很簡單远搪,只需要在AndroidManifest.xml的聲明四大組件的標(biāo)簽中增加”android:process”屬性即可。命名之后逢捺,就成了一個(gè)單獨(dú)的進(jìn)程谁鳍。
process分私有進(jìn)程和全局進(jìn)程:
- 私有進(jìn)程的名稱前面有冒號,例如:
<service android:name=".MusicService"
android:process=":musicservice"/>
- 全局進(jìn)程的名稱前面沒有冒號劫瞳,例如:
<service android:name=".MusicService"
android:process="com.trampcr.musicdemo.service"/>
為了節(jié)省系統(tǒng)內(nèi)存倘潜,在退出該Activity的時(shí)候可以將其殺掉(如果沒有人為殺掉該進(jìn)程,在程序完全退出時(shí)該進(jìn)程會被系統(tǒng)殺掉)志于。
多進(jìn)程被創(chuàng)建好了涮因,應(yīng)用運(yùn)行時(shí)就會對進(jìn)程進(jìn)行初始化,如果一個(gè)application中有多個(gè)進(jìn)程伺绽,在進(jìn)行全局初始化時(shí)养泡,多進(jìn)程會被初始化多次。
解決辦法:判斷當(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對象(對Handler的引用)。
- Messenger創(chuàng)建一個(gè)IBinder琐驴,服務(wù)通過onBind()使其返回客戶端俘种。
- 客戶端使用IBinder將Messenger(引用服務(wù)的Handler)實(shí)例化,然后使用后者將Message對象發(fā)送給服務(wù)绝淡。
- 服務(wù)在其Handler中(具體地講宙刘,是在handleMessage()方法中)接收每個(gè)Message。
這樣牢酵,客戶端并沒有調(diào)用服務(wù)的“方法”悬包。而客戶端傳遞的“消息”(Message對象)是服務(wù)在其Handler中接收的。
以上代碼實(shí)現(xiàn)的應(yīng)用茁帽,剛打開會彈出一個(gè)binding玉罐,binding表示打開應(yīng)用Activity就通過Messenger連接了一個(gè)服務(wù)進(jìn)程,然后點(diǎn)擊say hello會彈出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)會自動為該文件創(chuàng)建一個(gè)包名。
aidl文件中會有一個(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)還會自動生成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中注冊爷怀,并給一個(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:自動生成實(shí)現(xiàn)了Parcelable接口的對象吁朦。