Android開發(fā)高級(jí)進(jìn)階——多進(jìn)程間通信

如果一個(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í)從高到低排列:

Android進(jìn)程分類
  1. 前臺(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í)行完成而不被殺掉。
  2. 可見進(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!)。
  3. 服務(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í)間窗口工作。
  4. 后臺(tái)進(jìn)程:該進(jìn)程包含的組件沒有與用戶交互,用戶也看不到 Service。在一般操作場(chǎng)景下,設(shè)備上的許多內(nèi)存就是用在這上面的,使可以重新回到之前打開過的某個(gè) activity 。
  5. 空進(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的使用方法:

  1. 服務(wù)實(shí)現(xiàn)一個(gè)Handler,由其接收來自客戶端的每個(gè)調(diào)用的回調(diào)。
  2. Handler用于創(chuàng)建Messenger對(duì)象(對(duì)Handler的引用)。
  3. Messenger創(chuàng)建一個(gè)IBinder,服務(wù)通過onBind()使其返回客戶端。
  4. 客戶端使用IBinder將Messenger(引用服務(wù)的Handler)實(shí)例化,然后使用后者將Message對(duì)象發(fā)送給服務(wù)。
  5. 服務(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)程。如下圖:

MessengerService進(jìn)程與MessengerActivity之間的通信

使用AIDL

AIDL是一種接口描述語言,通常用于進(jìn)程間通信。

使用AIDL的步驟:

  1. 創(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文件。

  1. 在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;
    }
}
  1. 在AndroidManifest.xml中注冊(cè),并給一個(gè)進(jìn)程名,是該服務(wù)成為一個(gè)獨(dú)立的進(jìn)程。
<service android:name=".AIDLService"   
            android:process="com.aidl.test.service"/>
  1. 在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)行通信,如下圖所示:

AIDLService進(jìn)程與MainActivity之間的通信.gif

五.序列化插件


Parcelable code generate:自動(dòng)生成實(shí)現(xiàn)了Parcelable接口的對(duì)象。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容