Android Service基本用法、AIDL、Binder連接池詳解

本文介紹Service與Activity之間的通信,文章包含以下內(nèi)容:

  • 一、Service基本用法
  • 二、通過AIDL實(shí)現(xiàn)Service與Activity跨進(jìn)程通信
  • 三、Binder連接池
  • 四、使用Messenger實(shí)現(xiàn)跨進(jìn)程通信
  • 五、本文的示例源碼地址

文章有點(diǎn)長(zhǎng),主要分為上面5個(gè)部分,由于沒找到在簡(jiǎn)書設(shè)置文內(nèi)鏈接的方法,所以要想直接跳過基礎(chǔ)看后面的部分,翻滾吧 !不過文章整體從簡(jiǎn)到繁,前面的基礎(chǔ)對(duì)后面知識(shí)的理解會(huì)有幫助,所以建議按順序看。


一、Service基本用法

基本用法即同進(jìn)程下Activity與Service雙向通信,先描述整體實(shí)現(xiàn)過程然后直接上代碼:

  1. 新建一個(gè)繼承自Service的類MyService,然后在AndroidManifest.xml里注冊(cè)這個(gè)Service
  2. Activity里面使用bindService方式啟動(dòng)MyService,也就是綁定了MyService
    (到這里實(shí)現(xiàn)了綁定,Activity與Service通信的話繼續(xù)下面的步驟)
  3. 新建一個(gè)繼承自Binder的類MyBinder
  4. 在MyService里實(shí)例化一個(gè)MyBinder對(duì)象mBinder,并在onBind回調(diào)方法里面返回這個(gè)mBinder對(duì)象
  5. 第2步bindService方法需要一個(gè)ServiceConnection類型的參數(shù),在ServiceConnection里可以取到一個(gè)IBinder對(duì)象,就是第4步onBinder返回的mBinder對(duì)象(也就是在Activity里面拿到了Service里面的mBinder對(duì)象)
  6. 在Activity里面拿到mBinder之后就可以調(diào)用這個(gè)binder里面的方法了(也就是可以給Service發(fā)消息了),需要什么方法在MyBinder類里面定義實(shí)現(xiàn)就行了。如果需要Service給Activity發(fā)消息的話,通過這個(gè)binder注冊(cè)一個(gè)自定義回調(diào)即可。

代碼如下,關(guān)鍵部分給出了對(duì)應(yīng)上面步驟的注釋:

Activity

public class MainActivity extends Activity {

    private static final String TAG = "zjy";
    public MyBinder mBinder;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            //第5步所說的在Activity里面取得Service里的binder對(duì)象
            mBinder = (MyBinder)iBinder;
            //第6步注冊(cè)自定義回調(diào)
            mBinder.setOnTestListener(new MyBinder.OnTestListener() {
                @Override
                public void onTest(String str) {
                    Log.d(TAG, "receive msg from service: "+str);
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent,mConnection,BIND_AUTO_CREATE);

        findViewById(R.id.test_bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //點(diǎn)擊按鈕調(diào)用mBinder里面的方法,發(fā)送消息給Service
                mBinder.testMethod("hi, service.");
            }
        });
    }
}

Service

public class MyService extends Service {
    private static final String TAG = "zjy";
    // 第4步,實(shí)例化一個(gè)MyBinder對(duì)象
    private MyBinder mBinder = new MyBinder(this);

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;//第4步,返回這個(gè)mBinder對(duì)象
    }

    public void serviceMethod(String str){
        Log.d(TAG, "receive msg from activity: " + str);
    }
}

Binder

public class MyBinder extends Binder {
    private static final String TAG = "zjy";
    private MyService mService;
    private OnTestListener mListener;

    public MyBinder(MyService service) {
        this.mService = service;
    }

    public void testMethod(String str) {
        // Activity通過Binder來調(diào)用Service的方法將消息傳給Service
        mService.serviceMethod(str);
        // 并回調(diào)mListener.onTest告訴Activity已收到消息
        mListener.onTest("hi, activity.");
    }

    // MyBinder 里面提供一個(gè)注冊(cè)回調(diào)的方法
    public void setOnTestListener(OnTestListener listener) {
        this.mListener = listener;
    }

    //自定義一個(gè)回調(diào)接口
    public interface OnTestListener {
        void onTest(String str);
    }
}

代碼很簡(jiǎn)單,首先Activity綁定Service得到一個(gè)MyBinder實(shí)例并注冊(cè)MyBinder里面的OnTestListener回調(diào)監(jiān)聽,然后點(diǎn)擊按鈕的時(shí)候調(diào)用MyBinder里面的testMethod(String)方法將消息發(fā)出去,MyBinder持有一個(gè)MyService的實(shí)例,testMethod(String)里面調(diào)用MyService里面的方法就可以把Activity的消息傳給Service了,然后testMethod(String)里面回調(diào)mListener.onTest(String)將Service的消息發(fā)給Activity。

MyBinder定義在MyService里面作為內(nèi)部類也是很常見的寫法,這里為了方便后面的講解寫成了普通類的形式。

至此就實(shí)現(xiàn)了同進(jìn)程下Activity與Service的雙向通信,運(yùn)行代碼,點(diǎn)擊按鈕后log如下:

( 2360): receive msg from activity: hi, service.
( 2360): receive msg from service: hi, activity.

通過代碼可以看到,Activity和Service之間是通過一個(gè)binder對(duì)象來通信的。


二、通過AIDL實(shí)現(xiàn)Service與Activity跨進(jìn)程通信

上面講了Activity和Service在同進(jìn)程下的通信,結(jié)論是:Activity和Service之間是通過一個(gè)binder對(duì)象來通信的,其實(shí),這句話在多進(jìn)程中同樣有效,接下來就在多進(jìn)程下驗(yàn)證這句話。到這你可能已經(jīng)想到了,AIDL其實(shí)就是利用Binder實(shí)現(xiàn)跨進(jìn)程通信的。先看一下官方文檔是如何介紹AIDL的:

On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. The code to do that marshalling is tedious to write, so Android handles it for you with AIDL.

大概意思就是說Android進(jìn)程之間不能直接通信,需要把對(duì)象轉(zhuǎn)換成計(jì)算機(jī)能識(shí)別的原始語言,然后安排它跨越進(jìn)程邊界。但是做這些事很繁瑣,于是Android提供了AIDL來做這件事。(換句話就是要實(shí)現(xiàn)跨進(jìn)程需要編寫很多復(fù)雜的代碼,于是android提供了AIDL,通過編寫簡(jiǎn)單的AIDL文件,編譯器根據(jù)AIDL的規(guī)則生成那些復(fù)雜的代碼)

總的來說,使用AIDL跨進(jìn)程通信,整體過程和單進(jìn)程一樣,都是通過一個(gè)Binder來通信的,區(qū)別在于單進(jìn)程的Binder是自己通過繼承Binder類來手動(dòng)實(shí)現(xiàn)的,而跨進(jìn)程的Binder是通過AIDL自動(dòng)生成的,那是一個(gè)牛逼的Binder。

對(duì)AIDL有個(gè)初步認(rèn)識(shí)之后,開始實(shí)踐,這里使用AndroidStudio實(shí)現(xiàn)AIDL,參考文章:Android Studio中AIDL使用方法

首先修改上面的代碼,在AndroidManifest.xml里面用android:process=":remote"屬性把Service指定到另一個(gè)進(jìn)程中,這時(shí)候直接運(yùn)行代碼會(huì)報(bào)錯(cuò),因?yàn)樽远x的MyBinder不具有跨進(jìn)程的能力,綁定Service的時(shí)候無法得到Binder。那么接下來就使用AIDL生成一個(gè)可以跨進(jìn)程的Binder,然后用這個(gè)可跨進(jìn)程的Binder替換MyBinder。

1、新建一個(gè)AIDL文件

和新建類文件相似:右鍵 -> new -> AIDL -> AIDL File,然后輸入文件名點(diǎn)擊finish完成(這里的示例代碼是IMyAidlInterface)

上面的操作不管右鍵哪個(gè)目錄,完成之后都會(huì)在src/main目錄下生成了一個(gè)aidl目錄,新建的IMyAidlInterface.aidl文件就在這個(gè)目錄下,注意和eclipse的不同。
打開這個(gè)文件發(fā)現(xiàn)就是一個(gè)接口(可能會(huì)默認(rèn)生成一個(gè)basicTypes方法,這是示例方法,不用管,可以刪掉),然后在里面定義一個(gè)自己的方法(需要其他的方法的話自己看著加)

代碼如下:

interface IMyAidlInterface {
    void testMethod(String str);
}

2、編譯項(xiàng)目

Build -> Make Project
完成之后會(huì)在 app/build/generated/source/debug/ 目錄下生成一個(gè)和AIDL文件同名的java文件 IMyAidlInterface.java

這個(gè)類文件就是用來提供進(jìn)程間通信的,需要的Binder類就在這里面。
簡(jiǎn)單來說,AIDL就是一個(gè)用來生成代碼的工具,最終的目的就是得到IMyAidlInterface.java這個(gè)類。這個(gè)和數(shù)據(jù)庫(kù)框架GreenDao很像,都是通過一些簡(jiǎn)單的做法生成很多復(fù)雜而有用的代碼,然后拿來直接用。當(dāng)然那些復(fù)雜的代碼也是可以手動(dòng)編寫的,比如可以嘗試仿照IMyAidlInterface.java或者直接把IMyAidlInterface.java復(fù)制到j(luò)ava目錄然后刪掉aidl文件實(shí)現(xiàn)進(jìn)程間通信。

3、分析 IMyAidlInterface.java

AndroidStudio切換到Project工程模式在app/build/generated/source/debug/路徑下找到IMyAidlInterface.java文件并打開。生成的代碼格式很亂,為了方便查看,可以使用格式化代碼的快捷鍵格式化一下。

IMyAidlInterface.java里面是一個(gè)接口,接口里面有一個(gè)內(nèi)部抽象類和一個(gè)方法。這個(gè)方法就是我們?cè)赼idl文件里定義的那個(gè)方法。內(nèi)部抽象類就是我們要的Binder類,類名Stub。到這里不難想象接下來的工作:(1)Service里面new一個(gè)Stub實(shí)例并在onBinder里面返回這個(gè)Stub(或者說Binder)的實(shí)例 。(2)Activity里面綁定Service的時(shí)候取到這個(gè)Binder(強(qiáng)轉(zhuǎn)成Stub類型)。(3)調(diào)用這個(gè)Binder里面的testMethod方法實(shí)現(xiàn)Activity和Service的通信。大的思路是這樣,不過細(xì)節(jié)上還是有很多不同的。

IMyAidlInterface.java里面的其他代碼(主要是一些方法)暫時(shí)不用看,用到的時(shí)候會(huì)說。到這里只需要知道這個(gè)java文件里面有一個(gè)Stub類,有一個(gè)自定義的方法。

4、修改Service代碼

到這AIDL相關(guān)的代碼已經(jīng)完成,接下來就是使用AIDL為我們生成的代碼。首先修改MyService只需把MyBinder替換成Stub,但是Stub是個(gè)抽象類,需要我們自己實(shí)現(xiàn),那么新建一個(gè)繼承自Stub的類,類名隨意,這里取名AidlBinder,然后仿照同進(jìn)程下的MyBinder實(shí)現(xiàn)testMethod()方法,代碼如下:

public class AidlBinder extends IMyAidlInterface.Stub {

    private MyService mService;

    public AidlBinder(MyService service) {
        this.mService = service;
    }

    @Override
    public void testMethod(String str) throws RemoteException {
        mService.serviceMethod(str);
    }
}

上面代碼中沒有回調(diào)相關(guān)的代碼,因?yàn)榭邕M(jìn)程的回調(diào)和同進(jìn)程下是不一樣的,后面會(huì)說到。另外,這里為了方便講解,專門定義了AidlBinder類作為Stub 的實(shí)現(xiàn)類,另一種在Service里面直接使用匿名內(nèi)部類的方式實(shí)現(xiàn)Stub 也是很常見的。至于AidlBinder里面的代碼和同進(jìn)程下很像,不解釋了。
然后MyService里面使用AidlBinder即可,代碼如下:

public class MyService extends Service {

    private static final String TAG = "zjy";

    private AidlBinder mBinder = new AidlBinder(this);

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    public void serviceMethod(String str) {
        Log.d(TAG, "receive msg from activity: " + str);
    }
}

和同進(jìn)程基本一樣,不解釋了。
總結(jié)一下:到這里為止,除去理論的分析之外,實(shí)際的操作只有兩步:(1)新建一個(gè)AIDL文件用來生成一些代碼。(2)實(shí)現(xiàn)抽象類Stub,實(shí)現(xiàn)類是AidlBinder。(3)Service里面使用Stub的實(shí)現(xiàn)類AidlBinder替換原來的MyBinder。

5、修改Activity代碼

先來分析一下,按照同進(jìn)程通信的思路就是:聲明一個(gè)IMyAidlInterface.Stub類型的Binder,然后在綁定Service的時(shí)候初始化這個(gè)Binder:mBinder = (IMyAidlInterface.Stub)service; 然后使用這個(gè)Binder來跟Service通信。
其實(shí)這樣是不行的,如果這樣做,綁定服務(wù)的時(shí)候 mBinder = (IMyAidlInterface.Stub)service; 這行代碼會(huì)報(bào)一個(gè)異常java.lang.ClassCastException: android.os.BinderProxy cannot be cast to com.zjy.servicedemo.IMyAidlInterface$Stub
意思是傳過來的Binder是BinderProxy類型的不能轉(zhuǎn)換成Stub類型(因?yàn)镾tub不是BinderProxy的子類而是Binder的子類)。

關(guān)于BinderProxy,我也不懂,通過一些資料了解到它與C++層有關(guān),源碼中無對(duì)應(yīng)的java類,編譯源碼后會(huì)生成BinderProxy.class類,和Binder一樣實(shí)現(xiàn)了IBinder接口。

源碼位置\frameworks\base\core\jni\android_util_Binder.cpp->static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,jint code, jobject dataObj,jobject replyObj, jint flags)
出自:Android FrameWork——Binder機(jī)制詳解(1)

Java層的Activity透過BinderProxy來與遠(yuǎn)距的(Remote)服務(wù)進(jìn)行溝通。
從Java層而觀之,myActivity可以經(jīng)由bindService()而建立它與myBinder之間的連結(jié)。然而,這個(gè)連結(jié)是透過C++層的機(jī)制而達(dá)成的。
出自:認(rèn)識(shí)Android的BinderProxy和Binder類別 (應(yīng)該是臺(tái)灣人寫的,繁體字不是亂碼 ^ ^!)

Activit如何使用傳過來的Binder呢?AIDL生成的代碼中提供了一個(gè)靜態(tài)方法asInterface(IBinder),可以將IBinder轉(zhuǎn)換成Aidl接口,所以可以這樣做:IMyAidlInterface mService = IMyAidlInterface.Stub.asInterface(service);

藝術(shù)探索這本書中是這樣介紹asInterface方法的:用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶端所需的AIDL接口類型的對(duì)象,這種轉(zhuǎn)換是區(qū)分進(jìn)程的,如果客戶端和服務(wù)端位于同一進(jìn)程,那么此方法返回的就是服務(wù)端的Stub對(duì)象本身,否則返回的是系統(tǒng)封裝后的Stub.proxy對(duì)象。

所以同進(jìn)程下,Activity有以下3種方式使用Service傳過來的Binder:
IMyAidlInterface mService = IMyAidlInterface.Stub.asInterface(service);
IMyAidlInterface.Stub mBinder = (IMyAidlInterface.Stub)service;
IMyAidlInterface.Stub mService = (IMyAidlInterface.Stub)IMyAidlInterface.Stub.asInterface(service);
而跨進(jìn)程只能使用第一種方式,最終Activity的代碼如下:

public class MainActivity extends Activity {

    private static final String TAG = "zjy";
    public IMyAidlInterface mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = IMyAidlInterface.Stub.asInterface(iBinder);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        findViewById(R.id.test_bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    mService.testMethod("hi, service.");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}


6、跨進(jìn)程回調(diào)接口的實(shí)現(xiàn)

至此,實(shí)現(xiàn)了跨進(jìn)程Activity給Service發(fā)送消息,接下來實(shí)現(xiàn)Service收到消息后回應(yīng)Activity。大的方向還是和單進(jìn)程一樣使用回調(diào)實(shí)現(xiàn),不一樣的是細(xì)節(jié)。
首先,回調(diào)接口需要定義成aidl接口而不是普通接口,所以新建一個(gè)IMyCallbackListener.aidl文件,里面定義一個(gè)onRespond方法作為回調(diào)函數(shù):

interface IMyCallbackListener {
    void onRespond(String str);
}

擴(kuò)展IMyAidlInterface.aidl,里面定義一個(gè)注冊(cè)回調(diào)監(jiān)聽的方法(相當(dāng)于基礎(chǔ)篇里面的那個(gè)setOnTestListener方法)

import com.zjy.servicedemo.IMyCallbackListener;

interface IMyAidlInterface {
    void testMethod(String msg);
    void registerListener(IMyCallbackListener listener);
}

注意aidl的語法規(guī)則,非系統(tǒng)的類即使在同一個(gè)包下也要import,比如上面代碼的IMyCallbackListener,而系統(tǒng)的類String就不用import

這時(shí)編譯會(huì)提示AidlBinder實(shí)現(xiàn)父類的抽象方法registerListener(),仿照同進(jìn)程下的MyBinder里面的回調(diào)相關(guān)的代碼,修改AidlBinder如下:

public class AidlBinder extends IMyAidlInterface.Stub {

    private MyService mService;
    private IMyCallbackListener mListener;

    public AidlBinder(MyService service) {
        this.mService = service;
    }

    @Override
    public void testMethod(String str) throws RemoteException {
        mService.serviceMethod(str);
        mListener.onRespond("hi, activity");
    }

    @Override
    public void registerListener(IMyCallbackListener listener) throws RemoteException {
        mListener = listener;
    }
}

有同進(jìn)程通信的基礎(chǔ),看懂這個(gè)代碼很容易。然后Activity里面在合適的地方注冊(cè)回調(diào),用來接收服務(wù)端的消息:

try{
    mService.registerListener(new IMyCallbackListener.Stub() {
        @Override
        public void onRespond(String str) throws RemoteException {
            Log.d(TAG, "receive message from service: "+str);
        }
    });
} catch (RemoteException e){
    e.printStackTrace();
}

至此,跨進(jìn)程下Activity與Service的雙向通信就完成了,運(yùn)行代碼,點(diǎn)擊按鈕log如下:

(11597): receive msg from activity: hi, service.
(11579): receive message from service: hi, activity

就本應(yīng)用中的代碼來看,代碼的執(zhí)行流程和單進(jìn)程一樣,只是一些實(shí)現(xiàn)的細(xì)節(jié)不同。另外,可以使用adb shell ps | grep "本應(yīng)用的包名"命令查看進(jìn)程信息,會(huì)看到如下兩個(gè)進(jìn)程:
com.zjy.servicetest
com.zjy.servicetest:remote
com.zjy.servicetest:remote 是Service所在的進(jìn)程。如果是不同應(yīng)用下的多進(jìn)程,使用AIDL通信和同應(yīng)用多進(jìn)程無本質(zhì)區(qū)別。

7、跨進(jìn)程下解注冊(cè)回調(diào)

Service回應(yīng)Activity消息是通過注冊(cè)回調(diào)接口實(shí)現(xiàn)的,接下來介紹解注冊(cè),和同進(jìn)程的解注冊(cè)不同,多進(jìn)程需要借助RemoteCallbackList來完成,所以注冊(cè)回調(diào)的相關(guān)方法也要改一下,改成使用RemoteCallbackList來注冊(cè)回調(diào),AidlBinder代碼修改如下:

public class AidlBinder extends IMyAidlInterface.Stub {

    private MyService mService;
    private RemoteCallbackList<IMyCallbackListener> mListenerList = new RemoteCallbackList<>();

    public AidlBinder(MyService service) {
        this.mService = service;
    }

    @Override
    public void testMethod(String str) throws RemoteException {
        mService.serviceMethod(str);

        // 調(diào)用mListenerList里面所有已注冊(cè)的監(jiān)聽
        int count = mListenerList.beginBroadcast();
        for (int i = 0; i < count; i++) {
            mListenerList.getBroadcastItem(i).onRespond("hi, activity");
        }
        mListenerList.finishBroadcast();
    }

    @Override
    public void registerListener(IMyCallbackListener listener) throws RemoteException {
        mListenerList.register(listener);
    }

    @Override
    public void unregisterListener(IMyCallbackListener listener) throws RemoteException {
        mListenerList.unregister(listener);
    }
}

上面代碼里的unregisterListener方法像registerListener一樣添加進(jìn)去,并在里面實(shí)現(xiàn)解注冊(cè)的功能。RemoteCallbackList的用法很簡(jiǎn)單,看代碼就行了。

最后在Activity里面加一些測(cè)試解注冊(cè)的代碼即可,比如加一個(gè)按鈕,點(diǎn)擊的時(shí)候調(diào)用遠(yuǎn)程的解注冊(cè)方法,下面是Activity里面的最終完整代碼:

public class MainActivity extends Activity {

    private static final String TAG = "zjy";
    public IMyAidlInterface mService;

    private IMyCallbackListener.Stub mListener = new IMyCallbackListener.Stub() {
        @Override
        public void onRespond(String str) throws RemoteException {
            Log.d(TAG, "receive message from service: "+str);
        }
    };

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = IMyAidlInterface.Stub.asInterface(iBinder);
            try{
                //注冊(cè)回調(diào)
                mService.registerListener(mListener);
            } catch (RemoteException e){
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent, mConnection, BIND_AUTO_CREATE);

        findViewById(R.id.test_bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    mService.testMethod("hi, service.");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });

        findViewById(R.id.test2_bt).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //解注冊(cè)回調(diào)
                    mService.unregisterListener(mListener);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

整個(gè)代碼最終的功能是:?jiǎn)?dòng)Activity的時(shí)候綁定Service并注冊(cè)一個(gè)回調(diào),點(diǎn)擊 send message 按鈕后Activity向Service發(fā)送消息"hi, service",然后Service收到消息后log打印 "receive message from activity: hi, service",并恢復(fù)一個(gè)消息 "hi, activity",Activity收到消息后log打印 "receive message from service: hi, activity"。然后點(diǎn)擊unregisterListener按鈕解注冊(cè)回調(diào)監(jiān)聽,再點(diǎn)擊 send message 后就只打印log "receive message from activity: hi, service",說明解注冊(cè)成功。

AIDL的基本用法就介紹到這里,關(guān)于傳遞自定義的序列化對(duì)象和不同應(yīng)用的多進(jìn)程通信可以參考文章Android Studio中AIDL使用方法


這里總結(jié)一下:同進(jìn)程下自定義MyBinder可以輕松實(shí)現(xiàn)Activity與Service通信,跨進(jìn)程的話需要使用AIDL生成可以跨進(jìn)程的Binder。至于Activity與Service里面的代碼,流程套路基本相同,不相同的只是一些很簡(jiǎn)單的細(xì)節(jié)。



三、Binder連接池

通過上面的介紹,不難發(fā)現(xiàn),一個(gè)Service對(duì)應(yīng)一個(gè)Binder,實(shí)際項(xiàng)目總不能把所有邏輯都寫在一起的,不同業(yè)務(wù)邏輯是要分類的,難免會(huì)出現(xiàn)多個(gè)Binder的情況,總不能一個(gè)Binder對(duì)應(yīng)一個(gè)Service,這時(shí),就可以使用Binder連接池了。

首先,用一種簡(jiǎn)單的方式介紹一下什么是Binder連接池:Binder連接池是一種類似設(shè)計(jì)模式的代碼結(jié)構(gòu)。可以直接把它當(dāng)作一種設(shè)計(jì)模式來看待。
然后,這種模式要解決的問題:用一個(gè)Service管理多個(gè)AIDL(或者說管理多個(gè)Binder),而不是一個(gè)AIDL對(duì)應(yīng)一個(gè)Service。

再解釋一下,增強(qiáng)理解:我們知道設(shè)計(jì)模式對(duì)于編寫代碼、實(shí)現(xiàn)功能等并不是必須的,但是它有很多優(yōu)點(diǎn)。Binder連接池也是一樣,要實(shí)現(xiàn)一個(gè)Service管理多個(gè)AIDL也可以不使用它。但是它可以讓代碼結(jié)構(gòu)優(yōu)雅清晰,使代碼維護(hù)擴(kuò)展更加容易等。


簡(jiǎn)單了解連接池之后,接下來動(dòng)手實(shí)現(xiàn)一個(gè)例子。在動(dòng)手之前先整體了解一下最終的項(xiàng)目的目錄結(jié)構(gòu),看下圖:


項(xiàng)目結(jié)構(gòu)圖

如圖,這里拿動(dòng)物來舉例。下面一步步來實(shí)現(xiàn)圖中的代碼。

1、首先準(zhǔn)備相應(yīng)的類:新建一個(gè)Activity和一個(gè)Service,新建多個(gè)AIDL文件。

(1)Activity和Service先什么都不用做,它們與要實(shí)現(xiàn)的Binder連接池?zé)o關(guān),它們只是用來使用Binder連接池的。
新建多個(gè)AIDL,文件名如下:

  • IAnimal.aidl
  • IBird.aidl
  • IFish.aidl
  • IMonkey.aidl

它們的代碼如下:

interface IAnimal {
    IBinder queryAnimal(int animalCode);
}
interface IBird {
    void fly();
}
interface IFish {
    void swim();
}
interface IMonkey {
    void climbTree();
}

以上代碼不難理解,每種動(dòng)物包含一個(gè)它的專有方法,IAnimal接口管理其它三種動(dòng)物,它里面的方法接收一個(gè)參數(shù),這個(gè)參數(shù)代表動(dòng)物種類,后面的實(shí)現(xiàn)會(huì)根據(jù)動(dòng)物種類返回一個(gè)對(duì)應(yīng)的動(dòng)物的Binder。

(2)編譯項(xiàng)目,生成AIDL文件對(duì)應(yīng)的Binder,AIDL生成的Binder是抽象類,接下來定義每個(gè)抽象Binder的實(shí)現(xiàn)類,類名分別為:AnimalBinder.java,BirdBinder.java,F(xiàn)ishBinder.java,MonkeyBinder.java。代碼如下:

public class AnimalBinder extends IAnimal.Stub{

    public static final int ANIMAL_CODE_BIRD = 1;
    public static final int ANIMAL_CODE_FISH = 2;
    public static final int ANIMAL_CODE_MONKEY = 3;

    @Override
    public IBinder queryAnimal(int animalCode) throws RemoteException {
        IBinder binder = null;
        switch (animalCode) {
            case ANIMAL_CODE_BIRD:
                binder = new BirdBinder();
                break;
            case ANIMAL_CODE_FISH:
                binder = new FishBinder();
                break;
            case ANIMAL_CODE_MONKEY:
                binder = new MonkeyBinder();
                break;
            default:
                break;
        }
        return binder;
    }
}
public class BirdBinder extends IBird.Stub{
    private static final String TAG = "zjy";
    @Override
    public void fly() throws RemoteException {
        Log.d(TAG, "I'm bird, I can fly.");
    }
}
public class FishBinder extends IFish.Stub{
    private static final String TAG = "zjy";
    @Override
    public void swim() throws RemoteException {
        Log.d(TAG, "I'm fish, I can swim.");
    }
}
public class MonkeyBinder extends IMonkey.Stub {
    private static final String TAG = "zjy";
    @Override
    public void climbTree() throws RemoteException {
        Log.d(TAG, "I'm monkey, I can climb the tree.");
    }
}

代碼很簡(jiǎn)單,不解釋了。有一點(diǎn)要說明一下,AnimalBinder作為管理,和三種動(dòng)物Binder要區(qū)分開,更好的寫法是把AnimalBinder寫在代表連接池的類BinderPool里面作為內(nèi)部類(BinderPool類是后面要講的),那樣的話結(jié)構(gòu)上更加好看合理,示例的最終代碼是以內(nèi)部類的方式來寫的。

2、編寫連接池代碼

連接池就是一個(gè)普通的java類,類名隨意取,這里取名:BinderPool.java
類里面的代碼主要分為幾個(gè)簡(jiǎn)單的部分:

  • 給BinderPool.java實(shí)現(xiàn)單例模式
  • 綁定一個(gè)Service(綁定Service需要的Context是使用它的Activity傳過來的)
  • 提供一個(gè)queryAnimal方法,根據(jù)參數(shù)給用戶提供不同的binder
  • 以及前面說的把AnimalBinder作為BinderPool的內(nèi)部類

BinderPool.java的全部代碼如下:

public class BinderPool {

    private static final String TAG = "zjy";

    public static final int NO_ANIMAL = 0;
    public static final int ANIMAL_CODE_BIRD = 1;
    public static final int ANIMAL_CODE_FISH = 2;
    public static final int ANIMAL_CODE_MONKEY = 3;

    private Context mContext;
    @SuppressWarnings("all")
    private static BinderPool sInstance;
    private CountDownLatch mCountDownLatch;
    private IAnimal mAnimalPool;

    private BinderPool(Context context) {
        mContext = context.getApplicationContext();
        connectBinderPoolService();
    }

    public static BinderPool getInstance(Context context) {
        if (sInstance == null) {
            synchronized (BinderPool.class) {
                if (sInstance == null) {
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }

    private synchronized void connectBinderPoolService() {
        mCountDownLatch = new CountDownLatch(1);
        Intent intent = new Intent(mContext, MyService.class);
        mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

        try {
            mCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mAnimalPool = IAnimal.Stub.asInterface(service);
            mCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d(TAG, "onServiceDisconnected: ");
        }
    };

    public IBinder queryAnimal(int animalCode) {
        IBinder binder = null;
        try {
            if (mAnimalPool != null) {
                binder = mAnimalPool.queryAnimal(animalCode);
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }

    public static class AnimalBinder extends IAnimal.Stub {

        @Override
        public IBinder queryAnimal(int animalCode) throws RemoteException {
            IBinder binder = null;
            switch (animalCode) {
                case ANIMAL_CODE_BIRD:
                    binder = new BirdBinder();
                    break;
                case ANIMAL_CODE_FISH:
                    binder = new FishBinder();
                    break;
                case ANIMAL_CODE_MONKEY:
                    binder = new MonkeyBinder();
                    break;
                default:
                    break;
            }
            return binder;
        }
    }
}

根據(jù)劃分的幾個(gè)部分來看代碼是很容易的,不過有一些細(xì)節(jié)需要注意:

  • 關(guān)于單例的內(nèi)存泄漏風(fēng)險(xiǎn),代碼里把context成員轉(zhuǎn)換成了Application的context
  • AIDL是支持并發(fā)訪問的,代碼里在綁定Service的時(shí)候使用synchronized和CountDownLatch做了線程同步處理,所以獲取BinderPool單例對(duì)象的時(shí)候不能在主線程里面。

3、使用Binder連接池

到這里Binder連接池的代碼就完成了,主要就是一個(gè)BinderPool類,接下來在Service和Activity中使用它。

Service的代碼:

public class MyService extends Service {
    private BinderPool.AnimalBinder mBinder = new BinderPool.AnimalBinder();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}

注:不要忘記在AndroidManifest.xml里面用android:process=":remote"屬性把Service指定到另一個(gè)進(jìn)程中。

Activity的代碼:

public class MainActivity extends Activity {
    private static final String TAG = "zjy";
    private BinderPool mBinderPool;

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

        findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        mBinderPool = BinderPool.getInstance(MainActivity.this);
                        IBinder birdBinder = mBinderPool.queryAnimal(BinderPool.ANIMAL_CODE_BIRD);
                        IBinder fishBinder = mBinderPool.queryAnimal(BinderPool.ANIMAL_CODE_FISH);
                        IBinder monkeyBinder = mBinderPool.queryAnimal(BinderPool.ANIMAL_CODE_MONKEY);

                        IBird bird = IBird.Stub.asInterface(birdBinder);
                        IFish fish = IFish.Stub.asInterface(fishBinder);
                        IMonkey monkey = IMonkey.Stub.asInterface(monkeyBinder);

                        try {
                            bird.fly();
                            fish.swim();
                            monkey.climbTree();
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        });
    }
}

通過Service和Activity的代碼可以看到,BinderPool使用起來很簡(jiǎn)單。從使用者的角度來看,Binder連接池就是把應(yīng)該在Activity里面做的事封裝成了BinderPool類,比如綁定Service、客戶端通過Binder調(diào)用遠(yuǎn)程服務(wù)端的方法等。

4、測(cè)試

通過測(cè)試代碼可以知道,Service是在Activity中點(diǎn)擊按鈕的時(shí)候通過初始化BinderPool單例對(duì)象的時(shí)候綁定的(也可以在其他地方初始化BinderPool對(duì)象,隨意,這里只是一種測(cè)試代碼,但是不要在主線程里面),所以程序剛運(yùn)行的時(shí)候只有一個(gè)Activity所在的進(jìn)程,點(diǎn)擊按鈕之后才會(huì)開啟Service進(jìn)程。

(1)運(yùn)行代碼,執(zhí)行命令 adb shell ps | grep "com.zjy.servicedemo" 可以看到一個(gè)進(jìn)程

u0_a97    2228  523   1012056 57324 00000000 f774c915 S com.zjy.servicedemo

(2)點(diǎn)擊按鈕,可以看到打印log

D/zjy  ( 2264): I'm bird, I can fly.
D/zjy  ( 2264): I'm fish, I can swim.
D/zjy  ( 2264): I'm monkey, I can climb the tree.

(3)再次執(zhí)行命令 adb shell ps | grep "com.zjy.servicedemo" ,此時(shí)可以看到有兩個(gè)進(jìn)程,說明點(diǎn)擊按鈕后啟動(dòng)了service并且service是運(yùn)行在另一個(gè)進(jìn)程的。

u0_a97    2228  523   1012056 57324 00000000 f774c915 S com.zjy.servicedemo
u0_a97    2264  523   995804 42180 00000000 f774c915 S com.zjy.servicedemo:remote

Binder連接池到此結(jié)束,主要就是一個(gè)BinderPool.java類


四、使用Messenger實(shí)現(xiàn)跨進(jìn)程通信

Messenger也是用來做進(jìn)程間通信的,與AIDL的區(qū)別,看官方文檔的一段話:

When you need to perform IPC, using a Messenger for your interface is simpler than implementing it with AIDL, because Messenger queues all calls to the service, whereas, a pure AIDL interface sends simultaneous requests to the service, which must then handle multi-threading.
For most applications, the service doesn't need to perform multi-threading, so using a Messenger allows the service to handle one call at a time. If it's important that your service be multi-threaded, then you should use AIDL to define your interface.

意思就是Messenger比AIDL用起來簡(jiǎn)單,但是如果多個(gè)客戶端同時(shí)給服務(wù)發(fā)消息的話,Messenger一次只能處理一個(gè)消息,而AIDL可以多線程處理。

Messenger本質(zhì)也是用AIDL實(shí)現(xiàn)的,可以瀏覽下Messenger的源碼(只有100多行),會(huì)看到一些AIDL相關(guān)的東西。

然后簡(jiǎn)單介紹一下Messenger的使用,首先列一下使用流程:

  1. Service里面實(shí)現(xiàn)一個(gè)Handler用來接收消息用
  2. 使用這個(gè)Handler創(chuàng)建一個(gè)Messenger對(duì)象
  3. 使用這個(gè)Messenger對(duì)象創(chuàng)建一個(gè)Binder對(duì)象,并在onBind方法返回
  4. Activity里面綁定Service的時(shí)候使用傳過來的Binder創(chuàng)建一個(gè)Messenger對(duì)象
  5. Activity里面使用這個(gè)Messenger對(duì)象給Service發(fā)消息
  6. Service里面的Handler收到消息并處理
  7. Activity里面實(shí)現(xiàn)一個(gè)Handler用來接收Service回復(fù)的消息
  8. 第5步發(fā)送消息的時(shí)候消息中攜帶一個(gè)Messenger對(duì)象,這個(gè)Messenger是用第7步的Handler創(chuàng)建的
  9. 第6步Service收到消息的時(shí)候取出消息中攜帶的Messenger
  10. 用第9步取出的Messenger給Activity發(fā)消息
  11. Activity中第7步的Handler處理Service回復(fù)的消息

整個(gè)流程和單進(jìn)程通信的過程很像,都是圍繞Binder完成的。上面第7步以后都是Service回復(fù)消息相關(guān)的。下面直接給出完整代碼,注釋與上面的流程相對(duì)應(yīng)。

Service代碼

public class MyService extends Service {
    private static final String TAG = "zjy";

    //1.Service里面實(shí)現(xiàn)一個(gè)Handler用來接收消息用
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //6.Service里面的Handler收到消息并處理
            if (msg.what==1) {
                Bundle bundle = msg.getData();
                Log.d(TAG, "receive message from activity: "+bundle.getString("string"));

                //9.取出消息中的Messenger對(duì)象
                Messenger replyMessenger = msg.replyTo;

                Message  replyMsg= new Message();
                replyMsg.what = 2;
                Bundle b = new Bundle();
                b.putString("string", "hi, activity");
                replyMsg.setData(b);
                try {
                    //10.使用Messenger給Activity發(fā)消息
                    replyMessenger.send(replyMsg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    // 2.使用這個(gè)Handler創(chuàng)建一個(gè)Messenger對(duì)象
    private Messenger mMessenger = new Messenger(mHandler);

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //3.使用這個(gè)Messenger對(duì)象創(chuàng)建一個(gè)Binder對(duì)象,并在onBind方法返回
        return mMessenger.getBinder();
    }
}

Activity代碼

public class MainActivity extends Activity {

    private static final String TAG = "zjy";

    private Messenger mMessenger;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //4.Activity里面綁定Service的時(shí)候使用傳過來的Binder創(chuàng)建一個(gè)Messenger對(duì)象
            mMessenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(MainActivity.this, MyService.class);
        bindService(intent,mConnection,BIND_AUTO_CREATE);

        findViewById(R.id.bt1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Message msg = new Message();
                msg.what = 1;

                Bundle bundle = new Bundle();
                bundle.putString("string", "hi, service");
                msg.setData(bundle);
                //8.發(fā)送消息的時(shí)候攜帶一個(gè)Messenger對(duì)象
                msg.replyTo = new Messenger(mGetReplyMsg);

                try {
                    //5.Activity里面使用這個(gè)Messenger對(duì)象給Service發(fā)消息
                    mMessenger.send(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    //7.Activity里面實(shí)現(xiàn)一個(gè)Handler用來接收Service回復(fù)的消息
    private Handler mGetReplyMsg = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            //11.處理Service回復(fù)的消息
            if (msg.what==2) {
                Bundle bundle = msg.getData();
                Log.d(TAG, "receive message from service: "+bundle.getString("string"));
            }
        }
    };
}
需要注意的問題

(1)Messenger發(fā)送的消息是Message對(duì)象,組裝Message消息的時(shí)候不要使用Message的obj字段,而是借用Bundle來組裝數(shù)據(jù)。下面是《Android開發(fā)藝術(shù)探索》里面的一段話:

使用Messenger來傳輸Message,Message中能使用的載體只有what, arg1, arg2, Bundle以及replyTo。Message中的另一字段obj在同一個(gè)進(jìn)程中很實(shí)用,但是在進(jìn)程間通信的時(shí)候,在android2.2以前obj不支持跨進(jìn)程,即便是2.2以后,也僅僅是系統(tǒng)提供的實(shí)現(xiàn)了Parcelable接口的對(duì)象才能通過它來傳輸。這就意味著自定義的Parcelable對(duì)象是無法通過obj字段來傳輸?shù)摹?/p>

(2)在接收端的代碼中,取消息的時(shí)候是先從Message里面取出Bundle,然后直接從Bundle取數(shù)據(jù)。如果數(shù)據(jù)是自定義的Parcelable對(duì)象,是不能直接從Bundle里面取的,需要在取數(shù)據(jù)之前先給Bundle設(shè)置一個(gè)ClassLoader。“取數(shù)據(jù)之前”的意思不單單是指取自定義的Parcelable對(duì)象,而是包括基本數(shù)據(jù)類型和系統(tǒng)提供的Parcelable對(duì)象等所有數(shù)據(jù)之前。示例代碼如下:

Bundle bundle = msg.getData();
bundle.setClassLoader(getClassLoader());//設(shè)置ClassLoader
bundle.getxxx(key);//取數(shù)據(jù)

關(guān)于這一點(diǎn)源碼里面已經(jīng)有相關(guān)注釋說明了,Message類的getData方法注釋如下:

/** 
 * Obtains a Bundle of arbitrary data associated with this
 * event, lazily creating it if necessary. Set this value by calling
 * {@link #setData(Bundle)}.  Note that when transferring data across
 * processes via {@link Messenger}, you will need to set your ClassLoader
 * on the Bundle via {@link Bundle#setClassLoader(ClassLoader)
 * Bundle.setClassLoader()} so that it can instantiate your objects when
 * you retrieve them.
 * @see #peekData()
 * @see #setData(Bundle)
 */
public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }
    
    return data;
}


五、本文的示例源碼地址

https://github.com/developerzjy/ServiceDemo





最后編輯于
?著作權(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)容