1. 程式人生 > >第九章 多程序(multiprocess)

第九章 多程序(multiprocess)

一、多程序的基礎知識

◆  為什麼使用多程序?

        一個應用預設使用一個程序,這個程序(主程序)的名字就是應用的包名,程序是系統分配資源和排程的基本單位,每個程序都有自己獨立的資源和記憶體空間,其他程序不能任意訪問當前程序的記憶體和資源,系統給每個程序分配的記憶體會有限制。         如果一個程序佔用記憶體超過了這個記憶體限制,就會報OOM的問題。為了解決應用記憶體的問題,Android引入了多程序的概念,它允許在同一個應用內,廢了分擔主程序的壓力,將佔用記憶體的某些頁面單獨開一個程序,比如Flash、視訊播放頁面,頻繁繪製的頁面等。         使用多程序,需要在AndroidMainfest.xml的宣告中新增“android:process"屬性;process分私有程序和全域性程序兩種,私有程序的名稱前面有冒號,全部程序沒有。
        <activity
            android:name=".aidl.AIDLActivity"
            android:label="@string/app_name"
            android:process=":other">
        </activity>
        為了節省系統記憶體,在退出該Activity的時候可以將其殺掉(如果沒有人為殺掉該程序,在程式完全退出時該程序會被系統殺掉):
protected void onDestroy() {
        Process.killProcess(Process.myPid());
        System.exit(0);
}

◆  程序的等級

        我們可以將一些元件執行在其他程序中,並且可以為任意的程序新增執行緒。元件執行在哪個程序中是在manifest檔案裡設定的,其中<Activity>,<Service>,<receiver>和<provider>都有一個process屬性來指定該元件執行在哪個程序之中。我們可以設定這個屬性,使得每個元件執行在它們自己的程序中,或是幾個元件共同享用一個程序,或是不共同享用。<application>元素也有一個process屬性,用來指定所有的元件的預設屬性。
        Android會根據程序中執行的元件類別以及元件的狀態來判斷該程序的重要性,Android會首先停止那些不重要的程序。按照重要性從高到低一共有五個級別: 1. 前臺程序         前臺程序是使用者當前正在使用的程序。只有一些前臺程序可以在任何時候都存在。他們是最後一個被結束的,當記憶體低到根本連他們都不能執行時,裝置才會進行記憶體排程,中止一些前臺程序來保持對使用者互動的響應。 2. 可見程序         可見程序不包含前臺的元件但是會在螢幕上顯示一個可見的程序,除非前臺程序需要獲取它的資源,不然不會被中止。 3. 服務程序 執行著一個通過startService()方法啟動的service,這個service不屬於上面提到的兩種。service所在程序雖然對使用者不是直接可見的,但是他們執行了使用者非常關心的任務(比如播放MP3,從網路下載資料)。只要前臺程序和可見程序有足夠的記憶體,系統不會回收他們。 4. 後臺程序 執行著一個隊使用者不可見的activity(呼叫過onStop()方法),這些程序對使用者體驗沒有直接的影響,可以在服務程序、可見程序、前臺程序需要時回收。 5. 空程序 未執行任何程式元件。執行這些程序唯一的原因是作為一個快取,縮短下次程式需要重新使用的啟動時間。系統經常中止這些程序,這樣可以調節程式快取和系統快取的平衡。

二、程序間的通訊 IPC(inter process communication)

在應用中,程序相當於車間,而執行緒相當於車間裡的生產線。程序之間的記憶體和資源是不共享的,所以我們在多程序的使用中,要實現程序之間的通訊。程序間通訊有兩種實現方式:  用Messenger ——>Handler (適用於多程序單執行緒的情況,此時不用考慮執行緒安全問題 )  自己實現AIDL (Android Interface Definition Language,適用於多程序多執行緒)

1. messenger傳送訊息的方法

        Messenger的使用包括兩個地方,一個是service端,一個是client端。 1. 先準備好一個Message,這個message可以通過Message.botain(Handler h,int what)獲得,可以新增Bundle資料以及replyTo; 2. 準備一個Messenger,這個Messenger可以通過new Messenger-Handler()或者msg.replyTo獲得; 3. Messenger.send(msg)傳送Message資料; 4. 在Handler的handleMessage()函式中獲得指定what的msg,進行處理。 注意:Messenger可以傳遞的資料型別由Message可以傳遞的資料型別決定,如果想傳遞一個自定義的類,那麼必須要保證這個類通過Parcelable或者Serializable序列化,然後放到Bundle物件中通過Message傳遞。 MessengerService類的實現:
public class MessengerService extends Service {

    private Handler messengerHandler = new Handler() {
       //建立Handler用於處理MessengerActivity中傳遞的Message物件
        public void handleMessage(Message message) {
            switch (message.what) {
                case 0:
                    //獲取內容並Toast
                    Toast.makeText(MessengerService.this, message.getData().getString("content"), Toast.LENGTH_SHORT).show();
                    //獲取MessengerActivity中replyMessenger物件,用來返回訊息
                    Messenger messenger = message.replyTo;
                    //建立返回訊息物件
                    Message replyMessage = Message.obtain(null, 0);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply", "我已經收到訊息");
                    replyMessage.setData(bundle);
                    try {
                        //給MessengerActivity傳送訊息
                        messenger.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    };

    private Messenger mMessenger = new Messenger(messengerHandler);

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回IBinder物件
        return mMessenger.getBinder();
    }
}
MessengerActivity類的實現:
public class MessengerActivity extends Activity {

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通過服務返回的IBinder物件建立Messenger物件
            Messenger messenger = new Messenger(service);
            //建立訊息物件
            Message message = new Message();
            message.what = 0;
            Bundle bundle = new Bundle();
            bundle.putString("content","通過Messenger進行跨程序通訊");
            message.setData(bundle);
            //將接受訊息的replyMessenger傳遞給MessengerService
            message.replyTo = replyMessenger;
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    /**
     * 用於接收MessengerService傳遞來的資料Message物件
     */
    private Messenger replyMessenger = new Messenger(new Handler(){
        public  void handlerMessage(Message message){
            switch (message.what){
                case 0:
                    //獲取資訊內容並Toast
                    Toast.makeText(MessengerActivity.this,message.getData().getString("reply"),Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        //繫結服務
        bindService(new Intent(this,MessengerService.class),mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
    }
}

2. AIDL(Android Interface definition language安卓介面定義語言)實現通訊

因為Messenger只適用於跨程序的單執行緒通訊,當處理多執行緒問題時,我們就需要使用AIDL進行通訊。
1. 定義AIDL檔案 我們可以在main資料夾下建立一個aidle資料夾,在該資料夾下面點選NEW選擇新建AIDL檔案,系統會自動生成一個名叫IMyAidInterface.aidl的檔案(如下)。儲存後Android編譯器會在gen目錄下自動生成IMyAidInterface.java檔案,確保這些AIDL使用的環境建立完成;
// IMyAidlInterface.aidl
package com.example.administrator.threadexercise;

// Declare any non-default types here with import statements

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);
<span style="white-space:pre">	</span>//以上為系統自動生成程式碼,也可以自己增加方法
    String getName(String nickName);
}
2. 定義服務類AIDLService
public class AIDLService extends Service {
    /**
     * 例項化Stub類,Stub是該介面的一個內部類,繼承自bundle
     */
    IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
        //實現IMyAidInterface.aidl中的方法,這樣就可以通過IMyAidlInterface介面實現對Service的控制
        @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 nickName + "aidl_hahaha";
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回IBinder物件
        return mStub;
    }
}
3. 定義AIDLActivity類
public class AIDLActivity extends Activity {

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通過服務返回的IBinder建立IMyAidInterface物件
            mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private IMyAidlInterface mIMyAidlInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mIMyAidlInterface != null){
                    try {
                        //呼叫getName方法
                        String name = mIMyAidlInterface.getName("nice_know_maco");
                        Toast.makeText(AIDLActivity.this,name+"",Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        bindService(new Intent(this, AIDLService.class),mServiceConnection, Context.BIND_AUTO_CREATE);
    }
}