Android開發高級進階——多進程間通信

如果一個進程占用內存超過了這個內存限制,就會報OOM的問題,很多涉及到大圖片的頻繁操作或者需要讀取一大段數據在內存中使用時,很容易報OOM的問題。為了徹底地解決應用內存的問題,Android引入了多進程的概念,它允許在同一個應用內,為了分擔主進程的壓力,將占用內存的某些頁面單獨開一個進程,比如Flash、視頻播放頁面,頻繁繪制的頁面等。

一. 什么是多進程?

多進程就是多個進程的意思,那么什么是進程呢?

當一個應用在開始運行時,系統會為它創建一個進程,一個應用默認只有一個進程,這個進程(主進程)的名稱就是應用的包名。

進程的特點:

  • 進程是系統資源和分配的基本單位,而線程是調度的基本單位。

  • 每個進程都有自己獨立的資源和內存空間

  • 其它進程不能任意訪問當前進程的內存和資源

  • 系統給每個進程分配的內存會有限制

根據上邊的引言和進程的特點可以看出,使用多進程的場景為: 需要使apk所使用的內存限制擴大。

二. 進程的等級

按優先級可以分為五類,優先級從高到低排列:

Android進程分類

  1. 前臺進程:該進程包含正在與用戶進行交互的界面組件,比如一個Activity。在接收關鍵生命周期方法時會讓一個進程臨時提升為前臺進程,包括任何服務的生命周期方法onCreate()和onDestroy()和任何廣播接收器onReceive()方法。這樣做確保了這些組件的操作是有效的原子操作,每個組件都能執行完成而不被殺掉。

  2. 可見進程:該進程中的組件雖然沒有和用戶交互,但是仍然可以被看到。 activity可見的時候不一定在前臺 。一個簡單的例子是前臺的 activity 使用對話框啟動了一個新的 activity 或者一個透明 activity 。另一個例子是當調用運行時權限對話框時(事實上它就是一個 activity!)。

  3. 服務進程:該進程包含在執行后臺操作的服務組件,比如播放音樂的Service。對于許多在后臺做處理(如加載數據)而沒有立即成為前臺服務的應用都屬于這種情況。
    請特別注意從onStartCommand()返回的常量,如果服務由于內存壓力被殺掉,它表示控制什么發生什么:
    START_STICKY 表示希望系統可用的時候自動重啟服務,但不關心是否能獲得最后一次的 Intent (例如,可以重建自己的狀態或者控制自己的 start/stop 生命周期)。
    START_REDELIVER_INTENT 是為那些在被殺死之后重啟時重新獲得 Intent 的服務的,直到用傳遞給 onStartCommand() 方法的 startId 參數調用stopSelf()為止。這里會使用 Intent 和 startId 作為隊列完成工作。
    START_NOT_STICKY 用于那些殺掉也沒關系的服務。這適合那些管理周期性任務的服務,它們只是等待下一個時間窗口工作。

  4. 后臺進程:該進程包含的組件沒有與用戶交互,用戶也看不到 Service。在一般操作場景下,設備上的許多內存就是用在這上面的,使可以重新回到之前打開過的某個 activity 。

  5. 空進程:沒有任何界面組件、服務組件,或觸發器組件,只是出于緩存的目的而被保留(為了更加有效地使用內存而不是完全釋放掉),只要 Android 需要可以隨時殺掉它們。

三. 多進程的創建

Android多進程創建很簡單,只需要在AndroidManifest.xml的聲明四大組件的標簽中增加”android:process”屬性即可。命名之后,就成了一個單獨的進程。

process分私有進程和全局進程:

  • 私有進程的名稱前面有冒號,例如:
    <service android:name=".MusicService"   
             android:process="com.trampcr.musicdemo.service"/>
  • 全局進程的名稱前面沒有冒號,例如:
    <service android:name=".MusicService"   
             android:process=":musicservice"/>

為了節省系統內存,在退出該Activity的時候可以將其殺掉(如果沒有人為殺掉該進程,在程序完全退出時該進程會被系統殺掉)。

多進程被創建好了,應用運行時就會對進程進行初始化,如果一個application中有多個進程,在進行全局初始化時,多進程會被初始化多次。

解決辦法:判斷當前進程,然后做相應的初始化操作。

四. 多進程間的通信IPC

IPC:InterProcess Communication,即進程間通信。

我們知道,同一個進程的多個線程是共享該進程的所有資源,但多個進程間內存是不可見的,也就是說多個進程間內存是不共享的。那么進程間是如何進行通信的呢?

Android中提供了三種方法:

  • 系統實現。

  • AIDL(Android Interface Definition Language,Android接口定義語言):大部分應用程序不應該使用AIDL去創建一個綁定服務,因為它需要多線程能力,并可能導致一個更復雜的實現。

  • Messenger:利用Handler實現。(適用于多進程、單線程,不需要考慮線程安全),其底層基于AIDL。

使用Messenger

如需讓服務與遠程進程通信,則可使用Messenger為服務提供接口。

定義一個MessengerService繼承自Service,并在AndroidManifest.xml中聲明并給一個進程名,使該服務成為一個單獨的進程。代碼如下:

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. 服務實現一個Handler,由其接收來自客戶端的每個調用的回調。
  2. Handler用于創建Messenger對象(對Handler的引用)。
  3. Messenger創建一個IBinder,服務通過onBind()使其返回客戶端。
  4. 客戶端使用IBinder將Messenger(引用服務的Handler)實例化,然后使用后者將Message對象發送給服務。
  5. 服務在其Handler中(具體地講,是在handleMessage()方法中)接收每個Message。

這樣,客戶端并沒有調用服務的“方法”。而客戶端傳遞的“消息”(Message對象)是服務在其Handler中接收的。

以上代碼實現的應用,剛打開會彈出一個binding,binding表示打開應用Activity就通過Messenger連接了一個服務進程,然后點擊say hello會彈出hello,trampcr,這表示了Activity通過Messenger將Message發送給了服務進程。如下圖:

MessengerService進程與MessengerActivity之間的通信

使用AIDL

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

使用AIDL的步驟:

  1. 創建AIDL,在main下新建一個文件夾aidl,然后在aidl下新建AIDL文件,這時系統會自動為該文件創建一個包名。

    aidl文件中會有一個默認的basicType方法,我們為它增加一個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);
    }

    以上是我們自己創建的aidl文件,系統還會自動生成aidl代碼,所在位置為:build/generated/source/aidl下debug和release,但是此時debug下沒有任何東西,可以rebuild或運行一下程序,再次打開debug,發現生成了一個包和一個aidl文件。

  2. 在java下新建一個類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;
     }
    }
  3. 在AndroidManifest.xml中注冊,并給一個進程名,是該服務成為一個獨立的進程。

    <service android:name=".AIDLService"   
             android:process="com.aidl.test.service"/>
  4. 在MainActivity中進行與AIDLService之間的進程間通信。代碼如下:

    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進行連接,通過IMyAidlInterface實例與AIDLService進程進行通信,如下圖所示:

AIDLService進程與MainActivity之間的通信.gif

五.序列化插件

Parcelable code generate:自動生成實現了Parcelable接口的對象。

 

來自:http://www.jianshu.com/p/ce1e35c84134

 

 本文由用戶 winglion 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!