Android-服務(wù)跨進(jìn)程通信(Binder/Messenger/AIDL)

官方文檔中國(guó)版: https://developer.android.google.cn/guide/components/bound-services.html
谷歌2016年底為中國(guó)開發(fā)者提供中國(guó)版,方便訪問!

綁定服務(wù)簡(jiǎn)介

Android服務(wù)與客戶端相互調(diào)用(傳遞消息),必須創(chuàng)建綁定服務(wù)bindService()提供IBinder接口()!
服務(wù)與客戶端交互方式(傳遞消息)有三種:

1.使用Binder類(客戶端與服務(wù)在同一進(jìn)程)
如果服務(wù)與客戶端在同一進(jìn)程中運(yùn)行,通過(guò)擴(kuò)展Binder類并從onBind()返回它的一個(gè)實(shí)例來(lái)創(chuàng)建接口。
客戶端收到Binder后,可利用它直接訪問Binder實(shí)現(xiàn)中乃至Service中可用的公共方法!

2.使用Messenger(客戶端與服務(wù)在不同進(jìn)程,服務(wù)只能是單線程)
不同進(jìn)程工作,可用Messenger為服務(wù)創(chuàng)建接口。Message對(duì)象內(nèi)含Handler。
隨后可與客戶端分享一個(gè)IBinder,從而讓客戶端能利用 Message 對(duì)象向服務(wù)發(fā)送命令。
此外,客戶端還可定義自有 Messenger,以便服務(wù)回傳消息!
這是進(jìn)程間通信(IPC)的最簡(jiǎn)單方法(AIDL的簡(jiǎn)化),因?yàn)镸essenger會(huì)在服務(wù)單一線程創(chuàng)建所有請(qǐng)求隊(duì)列。

3.使用AIDL(客戶端與服務(wù)在不同進(jìn)程,服務(wù)可以是多線程)
AIDL(Android 接口定義語(yǔ)言)執(zhí)行所有將對(duì)象分解成原語(yǔ)的工作,
操作系統(tǒng)可以識(shí)別這些原語(yǔ)并將它們編組到各進(jìn)程中以執(zhí)行IPC。
之前的Messenger實(shí)際是以AIDL作為其底層結(jié)構(gòu)。如果服務(wù)同時(shí)處理多個(gè)請(qǐng)求(多線程),應(yīng)該用AIDL。

注:大多數(shù)應(yīng)用“都不會(huì)”使用AIDL來(lái)創(chuàng)建綁定服務(wù),因?yàn)樗赡芤缶邆涠嗑€程處理能力,
    并可能導(dǎo)致實(shí)現(xiàn)的復(fù)雜性增加。因此,AIDL并不適合大多數(shù)應(yīng)用!
    
4.此外,還可用廣播這個(gè)萬(wàn)金油組件進(jìn)行傳遞消息,無(wú)論是不同進(jìn)程,還是不同APP應(yīng)用,都可用廣播傳遞消息!
但是需要注意的是, 廣播有可能泄露數(shù)據(jù)、惡意程序發(fā)送廣播等安全性問題,
所以應(yīng)該限制廣播只在本應(yīng)用內(nèi)傳播:
    1.設(shè)置permission或Intent.setPackage,不把廣播發(fā)送到應(yīng)用外!
    2.使用LocalBroadcastManager或設(shè)置android:exported="false"不接收應(yīng)用外廣播!

一.使用Binder類(客戶端與服務(wù)在同一進(jìn)程)


// 1.本地服務(wù)————————————————————————————————————
public class LocalService extends Service {  
    private final IBinder mLocalBinder = new LocalBinder();
    
    public class LocalBinder extends Binder {
        LocalService getService() {           
            return LocalService.this;
        }
    }

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

    public int getRandomNumber() {
      return new Random().nextInt(100);
    }
}

// 2.客戶端————————————————————————————————————
public class BindingActivity extends Activity {    
    boolean mBound = false;
    
     @Override
    protected void onStart() {
        super.onStart();
        // 綁定本地服務(wù)(同一進(jìn)程)
        bindService(new Intent(this, LocalService.class), 
                    mConnection, 
                    Context.BIND_AUTO_CREATE);
    }
    
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mBound = true;
            // 獲取本地服務(wù)的實(shí)例,并調(diào)用方法,獲取隨機(jī)數(shù)
            LocalService localService = ((LocalBinder) service).getService();            
            int num = localService.getRandomNumber();               
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };

    @Override
    protected void onStop() {
        super.onStop();       
        if (mBound) {
            // 解綁本地服務(wù)
            unbindService(mConnection);
            mBound = false;
        }
    }   
}

二.使用Messenger(客戶端與服務(wù)在不同進(jìn)程,服務(wù)只能是單線程)


// 1.注冊(cè)遠(yuǎn)程服務(wù)————————————————————————————————————
    <service  
        android:name=".MessengerService"  
        android:process=":remote"> (process指定服務(wù)在另一個(gè)進(jìn)程,名叫remote)
    </service>

// 2.遠(yuǎn)程服務(wù)————————————————————————————————————
public class MessengerService extends Service {

    final Messenger mServiceMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // 接收客戶端消息
            switch (msg.what) {
                case 1:                 
                    Toast.makeText(getApplicationContext(), "service hello!", Toast.LENGTH_SHORT).show();                   
                    Thread.sleep(3000);
                    // 向客戶端發(fā)消息
                    Messenger messenger = msg.replyTo;
                    messenger.send(Message.obtain(null, 1, 0, 0))
                    break;               
            }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {        
        return mServiceMessenger.getBinder();
    }
}

// 3.客戶端————————————————————————————————————
public class ActivityMessenger extends Activity { 
    boolean mBound = false;
    
    private Messenger mActivityMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg){
            // 接收遠(yuǎn)程服務(wù)消息
            switch (msg.what){
                case 1:
                    Toast.makeText(ActivityMessenger.this, "activity hello!", Toast.LENGTH_SHORT).show();
                    break;              
            }
        }
    });

    private ServiceConnection mConnection = new ServiceConnection(){
        public void onServiceConnected(ComponentName className, IBinder service) {       
            mBound = true;
            // 向遠(yuǎn)程服務(wù)發(fā)消息             
            Messenger messenger = new Messenger(service);
            Message msg = Message.obtain(null, 1, 0, 0); 
            msg.replyTo = mActivityMessenger
            messenger.send(msg);            
        }

        public void onServiceDisconnected(ComponentName className) {
            mBound = false;
        }
    };

    @Override
    protected void onStart() {
        super.onStart();  
        // 綁定遠(yuǎn)程服務(wù)(不同進(jìn)程)
        bindService(new Intent(this, MessengerService.class),
                mConnection,
                Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();        
        // 解綁遠(yuǎn)程服務(wù)
        if (mBound) {
            unbindService(mConnection);           
        }
    }
}

三.使用AIDL(客戶端與服務(wù)在不同進(jìn)程,服務(wù)可以是多線程)

1.創(chuàng)建AIDL接口

默認(rèn)情況下AIDL支持下列數(shù)據(jù)類型
int、long、char、boolean、String、CharSequence、List、Map
List中元素都必須是以上類型、其他AIDL接口、可打包類型(即實(shí)現(xiàn)Parcelable接口).
Map中元素都必須是以上類型、其他AIDL接口、可打包類型(即實(shí)現(xiàn)了Parcelable接口)
不支持通用Map(如Map<String,Integer>形式的Map)

// IRemoteService.aidl文件
interface IRemoteService {
    // 注冊(cè)回調(diào)
    void registerCallback(IRemoteServiceCallBack cb);     
    void unregisterCallback(IRemoteServiceCallBack cb);  
}

// IRemoteServiceCallBack.aidl文件
interface IRemoteServiceCallBack{
    void valueChanged(int value);
}

2.在遠(yuǎn)程服務(wù)中實(shí)現(xiàn)AIDL接口


public class RemoteService extends Service {
    // 一個(gè)服務(wù)會(huì)綁定多個(gè)客戶端, 需要集合來(lái)存放客戶端的回調(diào)接口
    private RemoteCallbackList<IRemoteServiceCallBack> mCallbackList 
                = new RemoteCallbackList<IRemoteServiceCallBack>();
                
    // 實(shí)現(xiàn)IRemoteService.AIDL接口
    private IRemoteService.Stub mBinder = new IRemoteService.Stub() {
        @Override
        public void registerCallback(IRemoteServiceCallBack cb)throws RemoteException {
            if (cb != null) {
                mCallbackList.register(cb);
            }
        }
        
        @Override
        public void unRegisterCallback(IRemoteServiceCallBack cb)throws RemoteException {
            if (cb != null) {
                mCallbackList.unregister(cb);
            }
        }
    };
    
    // 通知所有客戶端的回調(diào)接口
    public void sendMsg(){      
        int N = mCallbackList.beginBroadcast();
        for(int i=0;i<N;i++){
            try {
                // 回調(diào)通知客戶端
                mCallbackList.getBroadcastItem(i).valueChanged(mValue);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }       
        mCallbackList.finishBroadcast(); // 通知完成
    }

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

3.客戶端


public class BindActivity extends Activity {
    IRemoteService mService = null;
    private boolean mIsBound = false;   
    
    // 實(shí)現(xiàn)IRemoteServiceCallback.AIDL接口
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {     
        /**
        * 來(lái)自遠(yuǎn)程服務(wù)的回調(diào)通知,
        * 此方法不在UI線程中, 更新UI需小心!
        */
        public void valueChanged(int value) {
            ...
        }
    };
    
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className,IBinder service){
            mIsBound = true;
            mService = IRemoteService.Stub.asInterface(service);            
            // 注冊(cè)回調(diào)接口
            mService.registerCallback(mCallback);       
        }

        public void onServiceDisconnected(ComponentName className) {           
            mIsBound = false;
            mService = null;
        }
    };
    
    @Override
    protected void onStart() {
        super.onStart();
        // 綁定遠(yuǎn)程服務(wù)
        bindService(new Intent(BindActivity.this, RemoteService.class),
                    mConnection,
                    Context.BIND_AUTO_CREATE);  
    }

    @Override
    protected void onStop() {
        super.onStop();  
        if (mIsBound){
            // 取消回調(diào)接口
            mService.unregisterCallback(mCallback);
            // 解綁遠(yuǎn)程服務(wù)
            unbindService(mConnection);
        }
    }
}

總結(jié):

綜上比較, AIDL實(shí)現(xiàn)客戶端與遠(yuǎn)程服務(wù)通信太繁瑣, 相互通知調(diào)用至少需要兩個(gè)AIDL接口文件
在遠(yuǎn)程服務(wù)中通知客戶端, 需要循環(huán)通知, 相當(dāng)繁瑣!
除非迫不得以(如服務(wù)可以是多線程運(yùn)行), 實(shí)在不建議使用AIDL!

客戶端與服務(wù)在不同進(jìn)程時(shí), 建議服務(wù)單線程運(yùn)行,
使用Messenger通信, 這也是Android官方推薦的!

簡(jiǎn)書: http://www.itdecent.cn/p/aec29a98bc1e
CSDN博客: http://blog.csdn.net/qq_32115439/article/details/72760479
GitHub博客:http://lioil.win/2017/05/25/Android_bindService.html
Coding博客:http://c.lioil.win/2017/05/25/Android_bindService.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容