第十章 進程間的通信 之 多進程(四)

文章目錄

(一)多進程基礎

1.1)多進程定義

當一個應用在開始運行時,系統會爲它創建一個進程,一個應用默認只有一個進程,這個進程(主進程)的名稱就是應用的包名。
爲了徹底地解決應用內存(主進程佔用內存超過內存限制)的問題,Android引入了多進程的概念,它允許在同一個應用內,爲了分擔主進程的壓力,將佔用內存的某些頁面單獨開一個進程,比如Flash、視頻播放頁面,頻繁繪製的頁面等。

1.2)進程等級

進程按優先級可分爲五類,優先級從高到低排列:
在這裏插入圖片描述

(1)前臺進程

該進程包含正在與用戶進行交互的界面組件。

  1. 屏幕頂層運行Activity(處於onResume()狀態),用戶正與之交互
  2. 有BroadcastReceiver正在執行代碼
  3. 有Service在其回調方法(onCreate()、onStart()、onDestroy())中正在執行代碼
    這種進程較少,一般來作爲最後的手段來回收內存

(2)可見進程

該進程中的組件雖然沒有和用戶交互,但是仍然可以被看到。

  1. 屏幕上顯示Activity,但不可操作(處於onPause()狀態)
  2. 有service通過調用Service.startForeground(),作爲一個前臺服務運行
  3. 含有用戶意識到的特定的服務,如動態壁紙、輸入法等

(3)服務進程

該進程包含在執行後臺操作,用戶不直接可見的服務組件,比如播放音樂的Service。對於許多在後臺做處理(如加載數據)而沒有立即成爲前臺服務的應用都屬於這種情況。

(4)緩存/後臺進程

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

(5)空進程

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

1.3)進程創建

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

(1)私有進程

私有進程的名稱前面有冒號,例如:

<service android:name=".MusicService"   
           android:process=":musicservice"/>

(2)全局進程

全局進程的名稱前面沒有冒號,例如:

<service android:name=".MusicService"   
           android:process="com.trampcr.musicdemo.service"/>

(3)初始化

多進程創建好,應用運行時會對進程進行初始化,如果一個application中有多個進程,進行全局初始化時,進程會被初始化多次。此時應該判斷當前進程,做相應的初始化操作。

1.4)UID機制與共享進程

(1)UID機制

衆所周知,Pid是進程ID,Uid是用戶ID,只是Android和計算機不一樣,計算機每個用戶都具有一個Uid,哪個用戶start的程序,這個程序的Uid就是那個那個用戶,而Android中每個程序都有一個Uid,默認情況下,Android會給每個程序分配一個普通級別互不相同的Uid,如果用互相調用,只能是Uid相同纔行,這就使得共享數據具有了一定安全性,每個軟件之間是不能隨意獲得數據的。而同一個application只有一個Uid,所以application下的Activity之間不存在訪問權限的問題。

(2)進程共享

a.一個application中共享service,provider或者activity等數據

1、完全暴露,這就是android:exported=”true”的作用,而一旦設置了intentFilter之後,exported就默認被設置爲true了,除非再強制設爲false。當然,對那些沒有intentFilter的程序體,它的exported屬性默認仍然是false,也就不能共享出去。
2、權限提示暴露,這就是爲什麼經常要設置usePermission的原因,如果人家設置了android:permission=”xxx.xxx.xx”那麼,你就必須在你的application的Manufest中usepermission xxx.xxx.xx才能訪問人家的東西。
3、私有暴露,假如說一個公司做了兩個產品,只想這兩個產品之間可互相調用,那麼這個時候就必須使用shareUserID將兩個軟件的Uid強制設置爲一樣的。這種情況下必須使用具有該公司簽名的簽名文檔才能,如果使用一個系統自帶軟件的ShareUID,例如Contact,那麼無須第三方簽名。

b.通過共享UID,使擁有同一個UID的多個APK可以配置成運行在同一進程中

1、在Manifest節點中增加android:sharedUserId屬性。
2、在Android.mk中增加LOCAL_CERTIFICATE的定義。
如果增加了上面的屬性但沒有定義與之對應的LOCAL_CERTIFICATE的話,APK是安裝不上去的。提示錯誤是:Package com.test.MyTest has no signatures that match those in shared user android.uid.system; ignoring!也就是說,僅有相同簽名和相同sharedUserID標籤的兩個應用程序簽名都會被分配相同的用戶ID。例如所有和media/download相關的APK都使用android.media作爲sharedUserId的話,那麼它們必須有相同的簽名media。
3、把APK的源碼放到packages/apps/目錄下,用mm進行編譯。

(二)多進程通信IPC

2.1)Binder機制

1、定義

a.Linux內核基礎知識

(1)用戶空間/內核空間
用戶空間指的是用戶程序所運行的空間,內核空間是 Linux 內核的運行空間,爲了安全,它們是隔離的,即使用戶的程序崩潰了,內核也不受影響。
(2)系統調用——用戶空間訪問內核空間
用戶空間訪問內核空間的唯一方式就是系統調用;通過這個統一入口接口,所有的資源訪問都是在內核的控制下執行,以免導致對用戶程序對系統資源的越權訪問,從而保障了系統的安全和穩定。
(3)Binder驅動——用戶空間A訪問用戶空間B
在 Android 系統中,這個運行在內核空間的,負責各個用戶進程通過 Binder 通信的內核模塊叫做 Binder 驅動。

b.什麼是Binder

  1. 直觀來說,Binder是Android中的一個類,它繼承了IBinder接口
  2. 從IPC角度來說,Binder是Android中的一種跨進程通信方式,Binder還可以理解爲一種虛擬的物理設備,它的設備驅動是/dev/binder,該通信方式在linux中沒有
  3. 從Android Framework角度來說,Binder是ServiceManager連接各種Manager(ActivityManager、WindowManager,etc)和相應ManagerService的橋樑
  4. 從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當你bindService的時候,服務端會返回一個包含了服務端業務調用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數據,這裏的服務包括普通服務和基於AIDL的服務

c.Binder優勢

在傳統的Linux上,我們還是有很多選擇可以用來實現進程間通信,如管道、SystemV、Socket等。那麼Android爲什麼不使用這些原有的技術,而是要使開發一種新的叫Binder的進程間通信機制呢?
主要有三個方面的原因:

(1)性能方面

在移動設備上(性能受限制的設備,比如要省電),廣泛地使用跨進程通信對通信機制的性能有嚴格的要求,Binder相對於傳統的Socket方式,傳輸效率高、開銷小。Binder數據拷貝只需要一次,而管道、消息隊列、Socket都需要2次;共享內存方式雖然一次內存拷貝都不需要,但控制複雜,難以使用。

(2)安全方面

傳統的進程通信方式對於通信雙方的身份並沒有做出嚴格的驗證,使用傳統IPC只能由用戶在數據包裏填入UID/PID,這樣不可靠,容易被惡意程序利用。可靠的身份標記只有由IPC機制本身在內核中添加。比如Socket通信ip地址是客戶端手動填入,很容易進行僞造,而Binder機制從協議本身就支持對通信雙方做身份校檢,因而大大提升了安全性。

(3)實現面象對象的調用方式

在使用Binder時就和調用一個本地實例一樣。

2、C/S通信方式

Binder使用Client-Server通信方式:一個進程作爲Server提供諸如視頻/音頻解碼,視頻捕獲,地址本查詢,網絡連接等服務;多個進程作爲Client向Server發起服務請求,獲得所需要的服務。
要想實現Client-Server通信據必須實現以下兩點:
一是server必須有確定的訪問接入點或者說地址來接受Client的請求,並且Client可以通過某種途徑獲知Server的地址;
二是制定Command-Reply協議來傳輸數據。Binder可以看成Server提供的實現某個特定服務的訪問接入點, Client通過這個‘地址’向Server發送請求來使用該服務;對Client而言,Binder可以看成是通向Server的管道入口,要想和某個Server通信首先必須建立這個管道並獲得管道入口。遍佈於Client中的入口可以看成指向這個Binder對象的‘指針’,一旦獲得了這個‘指針’就可以調用該對象的方法訪問Server。

3、通信模型

Binder框架定義了四個角色:Server,Client,ServiceManager(以後簡稱SMgr)以及Binder驅動。其中Server,Client,SMgr運行於用戶空間,驅動運行於內核空間。
理解:
這四個角色和互聯網類似:Server是服務器,Client是客戶終端,SMgr是域名服務器(DNS),Binder驅動是路由器。
和電話類似:Client給Server打電話,SMgr是通訊錄,Binder是電話基站。

3.1)Binder驅動

驅動負責進程之間Binder通信的建立,Binder在進程之間的傳遞,Binder引用計數管理,數據包在進程之間的傳遞和交互等一系列底層支持。

3.2)ServiceManger進程

ServiceManager的作用是將字符形式的Binder名字轉化成Client中對該Binder的引用,使得Client能夠通過Binder名字獲得對Server中Binder實體的引用。

3.3)Server進程

提供服務的進程。
Service 向ServerManager 註冊 實名Binder
Server創建了Binder實體,爲其取一個字符形式,可讀易記的名字,將這個Binder連同名字以數據包的形式通過Binder驅動發送給SMgr,通知SMgr註冊一個名叫張三的Binder,它位於某個Server中。驅動爲這個穿過進程邊界的Binder創建位於內核中的實體節點以及SMgr對實體的引用,將名字及新建的引用打包傳遞給SMgr。SMgr收數據包後,從中取出名字和引用填入一張查找表中。

3.4)Client進程

使用服務的進程。
Client 向ServerManager 獲得 實名Binder 的引用
Server向SMgr註冊了Binder實體及其名字後,Client就可以通過名字獲得該Binder的引用了。Client也利用保留的0號引用向SMgr請求訪問某個Binder:我申請獲得名字叫張三的Binder的引用。SMgr收到這個連接請求,從請求數據包裏獲得Binder的名字,在查找表裏找到該名字對應的條目,從條目中取出Binder的引用,將該引用作爲回覆發送給發起請求的Client。
在這裏插入圖片描述

  1. Client、Server和Server Manager實現在用戶空間中,Binder驅動程序實現在內核空間中
  2. Binder驅動程序和Service Manager在Android平臺中已經實現,開發者只需要在用戶空間實現自己的Client和Server
  3. Binder驅動程序提供設備文件/dev/binder與用戶空間交互,Client、Server和Service Manager通過open和ioctl文件操作函數與Binder驅動程序進行通信
  4. Client和Server之間的進程間通信通過Binder驅動程序間接實現
  5. Service Manager是一個守護進程,用來管理Server,並向Client提供查詢Server接口的能力

4、工作流程(代理Proxy模式)

實例:客戶端向調用服務端的add方法,返回一個Object對象
在這裏插入圖片描述

步驟1:ServiceManager內維護了一張表,表存儲着向他註冊過的進程信息

在通信之初,首先需要有一個進程向驅動申請成爲ServerManager,當內核驅動同意之後,這個成爲ServerManager的進程就負責管理所有需要通信的進程信息

步驟2:服務端進程向ServerManager註冊信息

服務端進程首先會向ServerManager註冊一張表,這個表中就存儲了相關信息,告訴ServerManager我這裏有一個返回值爲Object的add方法,

步驟3:客戶端進程向ServerManager取得信息,通過Binder驅動與服務端進程通信

當ServerManger保存完畢後,客戶端進程就會通過Binder驅動向ServerManger查詢服務端進程的信息,ServerManage就會將服務端進程的信息返回給客戶端進程,客戶端與服務端進程之間就可以通過這些信息,利用Binder驅動來進行通信

2.2)AIDL

1、定義

AIDL(Android Interface Define Language) 是IPC進程間通信方式的一種.用於生成可以在Android設備上兩個進程之間進行進程間通信(interprocess communication, IPC)的代碼.

2、使用

2.1)實例說明

假設一個情景我們需要計算a+b,我們需要在客戶端傳遞兩個參數a和b,然後將參數傳遞給服務端(另一個進程)來進行計算,計算結果傳遞給客戶端。

2.2)新建一個項目作爲服務端,在項目中創建AIDL文件

命名爲IImoocAIDL.aidl,點擊同步sycn project,查看build內是否自動生成IImoocAIDL文件

// IImoocAIDL.aidl
package com.mecury.aidltest;
// Declare any non-default types here with import statements
interface IImoocAIDL {    
    //計算num1 + num2    
    int add(int num1,int num2);
}

2.3)自動生成AIDL文件

根據自動生成的AIDL文件分析AIDL通信原理:
文件有一個叫做proxy的類,這是一個代理類,這個類運行在客戶端中,其實AIDL實現的進程間的通信並不是直接的通信,客戶端和服務端都是通過proxy來進行通信的:客戶端調用的方法實際是調用是proxy中的方法,然後proxy通過和服務端通信將返回的結果返回給客戶端。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\AS for android\\AIDLTest\\aidlclient\\src\\main\\aidl\\com\\mecury\\aidltest\\IImoocAIDL.aidl
 */
package com.mecury.aidltest;
// Declare any non-default types here with import statements

public interface IImoocAIDL extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.mecury.aidltest.IImoocAIDL {
        private static final java.lang.String DESCRIPTOR = "com.mecury.aidltest.IImoocAIDL"; //Binder的唯一標識

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.mecury.aidltest.IImoocAIDL interface,
         * generating a proxy if needed.
         */
        //將服務端的Binder對象轉換成客戶需要的AIDL對象,轉換區分進程,客戶端服務端位於同一進程,返回服務端的
        //Stub對象本身;否則返回的是系統的封裝後的Stub.proxy對象。
        public static com.mecury.aidltest.IImoocAIDL asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.mecury.aidltest.IImoocAIDL))) {
                return ((com.mecury.aidltest.IImoocAIDL) iin);
            }
            return new com.mecury.aidltest.IImoocAIDL.Stub.Proxy(obj);
        }
        //返回當前Binder對象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        //運行在服務端的Binder線程池中
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_add: {
                    data.enforceInterface(DESCRIPTOR);
                    //讀取客戶端傳遞過來再data中存儲的方法的參數
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    //調用方法
                    int _result = this.add(_arg0, _arg1);
                    //將計算結果寫入reply中
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags); //向Transact傳遞數據
        }
        
        //代理類,運行在客戶端
        private static class Proxy implements com.mecury.aidltest.IImoocAIDL {
            private android.os.IBinder mRemote; //聲明一個IBinder對象

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            //返回當前Binder對象
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            } 

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }
            
            //客戶端調用此方法,傳遞進來num1和num2兩個參數,
            @Override
            public int add(int num1, int num2) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    //向_data中寫入參數
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(num1);
                    _data.writeInt(num2);
                    //通過transact方法向服務端傳遞參數,並調用了方法,返回的結果寫入_reply中
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        //標識位
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    
    //計算num1 + num2
    public int add(int num1, int num2) throws android.os.RemoteException;
}

代碼中方法說明:
(1)DESCRIPTION
Binderd的唯一標識,一般用當前的類名錶示。
asInterface(android.os.IBinder obj)
用於將服務端的Binder對象轉換爲客戶端需要的AIDL接口類型的對象,轉換區分進程,客戶端服務端位於同一進程,返回服務端的 //Stub對象本身;否則返回的是系統的封裝後的Stub.proxy對象。
(2)asBInder
返回Binder對象
(3)onTransact
此方法運行在服務端中的Binder線程池中,當客戶端發起跨進程請求時,遠程請求會通過系統底層封裝後交由此方法處理。
(4)Proxy#add
此方法運行在客戶端,當客戶端遠程調用此方法時,它的內部實現是這樣的:首先創建該方法所需要的輸入型Parcel對象_data、輸出型Parcel對象_reple和返回值對象_result,然後將該方法的參數信息寫入_data中;接着調用transact方法來發RPC請求,同時當前線程掛起;然後服務端的onTransact方法會被調用,直到RPC過程返回後,當前線程繼續執行,並從_reply中取出RPC過程返回的結果,寫入_result中。

2.4)新建客戶端

File-》new–》new module–》phone & table module。命名爲aidlclient.java。同樣要在客戶端創建AIDL文件,裏面的包名和所在位置要求完全一樣。

2.5)在服務端創建一個Service用來監聽客戶端連接請求

public class IRemoteService extends Service {

    //客戶端綁定service時會執行
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    private IBinder iBinder = new IImoocAIDL.Stub(){

        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.e("TAG","收到了來自客戶端的請求" + num1 + "+" + num2 );
            return num1 + num2;
        }
    };
}

2.6)在AndroidMainfest.xml註冊Service

<service android:name=".IRemoteService"
            android:process=":remote"
            android:exported="true">
            <intent-filter>
                <action android:name="com.mecury.aidltest.IRomoteService"/>
            </intent-filter>
</service>

2.7)客戶端綁定服務並調用服務端方法

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText num1;
    private EditText num2;
    private Button button;
    private TextView text;

    private IImoocAIDL iImoocAIDL;

    private ServiceConnection conn = new ServiceConnection() {

        //綁定服務,回調onBind()方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iImoocAIDL = IImoocAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iImoocAIDL = null;
        }
    };

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

        bindService();
        initView();

    }

    private void initView() {
        num1 = (EditText) findViewById(R.id.num1);
        num2 = (EditText) findViewById(R.id.num2);
        button = (Button) findViewById(R.id.button);
        text = (TextView) findViewById(R.id.text);

        button.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        int num11 = Integer.parseInt(num1.getText().toString());
        int num22 = Integer.parseInt(num2.getText().toString());

        try {
            int res = iImoocAIDL.add(num11,num22);
            text.setText(num11 +"+"+ num22 +"="+ res);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void bindService() {

        Intent intent = new Intent();
        //綁定服務端的service
        intent.setAction("com.mecury.aidltest.IRomoteService");
        //新版本(5.0後)必須顯式intent啓動 綁定服務
        intent.setComponent(new ComponentName("com.mecury.aidltest","com.mecury.aidltest.IRemoteService"));
        //綁定的時候服務端自動創建
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

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

3、AIDL支持的數據類型

  • 基本數據類型(int、long、char 等)
  • String 和 CharSequence
  • List:只支持ArrayList,裏面的每個元素都必須被AIDL支持。
  • Map: 只支持HashMap, 裏面的每個元素都必須被AIDL支持。
  • Parcelable: 所有實現了Parcelable接口的對象
  • AIDL: 所有的AIDL接口本身也可以在AIDL文件中使用

4、自定義類型

AIDL能夠傳輸的數據類型有限制,這裏必須將book序列化才能夠使用,同時Book類在客戶端和服務端都要這樣定義

4.1)自定義類型文件

Book.java

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public String toString() {
        return bookId + ":" + bookName;
    }
}

4.2)AIDL文件

Book.aidl

package com.mecury.aidltest2;
parcelable Book;

IOnNewBookArrivedListener.aidl

package com.mecury.aidltest2;

import com.mecury.aidltest2.book;
interface IOnNewBookArrivedListener {
     void OnNewBookArrivedListener(in Book book);
}

IBookManager.aidl

package com.mecury.aidltest2;
import com.mecury.aidltest2.book;
import com.mecury.aidltest2.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}

4.3)服務端文件

public class BookManagerService extends Service {

    private static final String TAG = "BMS";

    private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    private RemoteCallbackList<IOnNewBookArrivedListener> mListeners = new RemoteCallbackList<>();

    //創建一個Binder對象
    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListeners.register(listener);
        }

        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
            mListeners.unregister(listener);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new serviceWork()).start();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    //將book添加到圖書列表中(mBookList),並通知所有觀察者
    private void onNewBookArrived(Book book) throws RemoteException {
        mBookList.add(book);
        final int N = mListeners.beginBroadcast();
        Log.e("onNewBookArrived","registener listener size:" + N);
        for (int i = 0; i < N; i++){
            IOnNewBookArrivedListener l = mListeners.getBroadcastItem(i);
            if (l!=null){
                l.OnNewBookArrivedListener(book);
            }
        }
        mListeners.finishBroadcast();
    }
    //線程類。在每休眠5秒後,會自動添加一本書, 並通過OnNewBookArrived()方法通知所有觀察者。 
    private class serviceWork implements Runnable{

        @Override
        public void run() {
            while (!mIsServiceDestoryed.get()){
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId,"new Book #" + bookId);

                try {
                    onNewBookArrived(newBook);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onDestroy() {
        mIsServiceDestoryed.set(true);
        super.onDestroy();
    }
}

4.4)客戶端文件

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "BookManagerActivity";
    private IBookManager bookManager;
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;

    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.e(TAG, "received new book:" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }

        }
    };

    private ServiceConnection mService = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                Log.e(TAG, "query book list,list type:" + list.getClass().getCanonicalName());
                Log.e(TAG, "query book list:" + list.toString());
                Book newBook = new Book(3, "android進階");
                bookManager.addBook(newBook);
                Log.e(TAG, "add book:" + newBook);
                List<Book> newList =  bookManager.getBookList();
                Log.e(TAG, "query book list:" + newList.toString());
                bookManager.registerListener(mNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bookManager = null;
            Log.e(TAG, "binder died.");
        }
    };

    private IOnNewBookArrivedListener mNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
        @Override
        public void OnNewBookArrivedListener(Book book) throws RemoteException {
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
        }
    };

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

        bindService();

    }

    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.mecury.aidltest2.BookManagerService");
        intent.setComponent(new ComponentName("com.mecury.aidltest2", "com.mecury.aidltest2.BookManagerService"));
        bindService(intent, mService, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        if (bookManager != null && bookManager.asBinder().isBinderAlive()){
            Log.e(TAG, "unregister listener:" + mNewBookArrivedListener);
            try {
                bookManager.unregisterListener(mNewBookArrivedListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        unbindService(mService);
        super.onDestroy();
    }
}

2.3)Messenger

1、定義

使用Messenger爲服務提供接口,讓服務與遠程進程通信。利用Handler實現。(適用於多進程、單線程,不需要考慮線程安全),其底層基於AIDL。

2、使用

2.1)服務端實現一個Handler,接受來自客戶端每個調用的回調

Handler用於創建Messenger對象(對Handler的引用),Messenger創建一個IBinder.服務通過onBind()使其返回客戶端;並在其Handler(handleMessage())中接收每個Message。

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();
    }
}

2.2)在AndroidManifest.xml聲明

在AndroidManifest.xml中聲明並給一個進程名,使該服務成爲一個單獨的進程

<service android:name=".MessengerService"  
         android:process="com.trampcr.messenger.service"/>

2.3)客戶端使用IBinder將Messenger(引用服務的Handler)實例化,然後使用後者將Message對象發送給服務

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;
        }
    }
}

3、AIDL與Messenger、Binder區別

Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.
(1)只有當你允許來自不同的客戶端訪問你的服務並且需要處理多線程問題時你才必須使用AIDL
(2)如果進程間的通信來自同一應用,應該使用Binder
(3)如果進程間的通信不需要處理多線程,應該使用Messenger
a.Messenger不適用大量併發的請求:Messenger以串行的方式來處理客戶端發來的消息,如果大量的消息同時發送到服務端,服務端仍然只能一個個的去處理。
b.Messenger主要是爲了傳遞消息:對於需要跨進程調用服務端的方法,這種情景不適用Messenger。
c.Messenger的底層實現是AIDL,系統爲我們做了封裝從而方便上層的調用。
d.AIDL適用於大量併發的請求,以及涉及到服務端端方法調用的情況

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章