天天看點

Android-服務跨程序通信(Binder/Messenger/AIDL)綁定服務簡介一.使用Binder類(用戶端與服務在同一程序)二.使用Messenger(用戶端與服務在不同程序,服務隻能是單線程)三.使用AIDL(用戶端與服務在不同程序,服務可以是多線程)總結:

官方文檔中國版: https://developer.android.google.cn/guide/components/bound-services.html

谷歌2016年底為中國開發者提供中國版,友善通路!

綁定服務簡介

Android服務與用戶端互相調用(傳遞消息),必須建立綁定服務bindService()提供IBinder接口()!
服務與用戶端互動方式(傳遞消息)有三種:

1.使用Binder類(用戶端與服務在同一程序)
如果服務與用戶端在同一程序中運作,通過擴充Binder類并從onBind()傳回它的一個執行個體來建立接口。
用戶端收到Binder後,可利用它直接通路Binder實作中乃至Service中可用的公共方法!

2.使用Messenger(用戶端與服務在不同程序,服務隻能是單線程)
不同程序工作,可用Messenger為服務建立接口。Message對象内含Handler。
随後可與用戶端分享一個IBinder,進而讓用戶端能利用 Message 對象向服務發送指令。
此外,用戶端還可定義自有 Messenger,以便服務回傳消息!
這是程序間通信(IPC)的最簡單方法(AIDL的簡化),因為Messenger會在服務單一線程建立所有請求隊列。

3.使用AIDL(用戶端與服務在不同程序,服務可以是多線程)
AIDL(Android 接口定義語言)執行所有将對象分解成原語的工作,
作業系統可以識别這些原語并将它們編組到各程序中以執行IPC。
之前的Messenger實際是以AIDL作為其底層結構。如果服務同時處理多個請求(多線程),應該用AIDL。

注:大多數應用“都不會”使用AIDL來建立綁定服務,因為它可能要求具備多線程處理能力,
    并可能導緻實作的複雜性增加。是以,AIDL并不适合大多數應用!

4.此外,還可用廣播這個萬金油元件進行傳遞消息,無論是不同程序,還是不同APP應用,都可用廣播傳遞消息!
但是需要注意的是, 廣播有可能洩露資料、惡意程式發送廣播等安全性問題,
是以應該限制廣播隻在本應用内傳播:
    1.設定permission或Intent.setPackage,不把廣播發送到應用外!
    2.使用LocalBroadcastManager或設定android:exported="false"不接收應用外廣播!
           

一.使用Binder類(用戶端與服務在同一程序)

// 1.本地服務————————————————————————————————————
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();
    }
}

// 2.用戶端————————————————————————————————————
public class BindingActivity extends Activity {    
    boolean mBound = false;

     @Override
    protected void onStart() {
        super.onStart();
        // 綁定本地服務(同一程序)
        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;
            // 擷取本地服務的執行個體,并調用方法,擷取随機數
            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) {
            // 解綁本地服務
            unbindService(mConnection);
            mBound = false;
        }
    }   
}
           

二.使用Messenger(用戶端與服務在不同程序,服務隻能是單線程)

// 1.注冊遠端服務————————————————————————————————————
    <service  
        android:name=".MessengerService"  
        android:process=":remote"> (process指定服務在另一個程序,名叫remote)
    </service>

// 2.遠端服務————————————————————————————————————
public class MessengerService extends Service {

    final Messenger mServiceMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // 接收用戶端消息
            switch (msg.what) {
                case :                 
                    Toast.makeText(getApplicationContext(), "service hello!", Toast.LENGTH_SHORT).show();                   
                    Thread.sleep();
                    // 向用戶端發消息
                    Messenger messenger = msg.replyTo;
                    messenger.send(Message.obtain(null, , , ))
                    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){
            // 接收遠端服務消息
            switch (msg.what){
                case :
                    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;
            // 向遠端服務發消息             
            Messenger messenger = new Messenger(service);
            Message msg = Message.obtain(null, , , ); 
            msg.replyTo = mActivityMessenger
            messenger.send(msg);            
        }

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

    @Override
    protected void onStart() {
        super.onStart();  
        // 綁定遠端服務(不同程序)
        bindService(new Intent(this, MessengerService.class),
                mConnection,
                Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();        
        // 解綁遠端服務
        if (mBound) {
            unbindService(mConnection);           
        }
    }
}
           

三.使用AIDL(用戶端與服務在不同程序,服務可以是多線程)

1.建立AIDL接口

預設情況下AIDL支援下列資料類型
int、long、char、boolean、String、CharSequence、List、Map
List中元素都必須是以上類型、其他AIDL接口、可打包類型(即實作Parcelable接口).
Map中元素都必須是以上類型、其他AIDL接口、可打包類型(即實作了Parcelable接口)
不支援通用Map(如Map<String,Integer>形式的Map)

// IRemoteService.aidl檔案
interface IRemoteService {
    // 注冊回調
    void registerCallback(IRemoteServiceCallBack cb);     
    void unregisterCallback(IRemoteServiceCallBack cb);  
}

// IRemoteServiceCallBack.aidl檔案
interface IRemoteServiceCallBack{
    void valueChanged(int value);
}
           

2.在遠端服務中實作AIDL接口

public class RemoteService extends Service {
    // 一個服務會綁定多個用戶端, 需要集合來存放用戶端的回調接口
    private RemoteCallbackList<IRemoteServiceCallBack> mCallbackList 
                = new RemoteCallbackList<IRemoteServiceCallBack>();

    // 實作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);
            }
        }
    };

    // 通知所有用戶端的回調接口
    public void sendMsg(){      
        int N = mCallbackList.beginBroadcast();
        for(int i=;i<N;i++){
            try {
                // 回調通知用戶端
                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;   

    // 實作IRemoteServiceCallback.AIDL接口
    private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {     
        /**
        * 來自遠端服務的回調通知,
        * 此方法不在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);            
            // 注冊回調接口
            mService.registerCallback(mCallback);       
        }

        public void onServiceDisconnected(ComponentName className) {           
            mIsBound = false;
            mService = null;
        }
    };

    @Override
    protected void onStart() {
        super.onStart();
        // 綁定遠端服務
        bindService(new Intent(BindActivity.this, RemoteService.class),
                    mConnection,
                    Context.BIND_AUTO_CREATE);  
    }

    @Override
    protected void onStop() {
        super.onStop();  
        if (mIsBound){
            // 取消回調接口
            mService.unregisterCallback(mCallback);
            // 解綁遠端服務
            unbindService(mConnection);
        }
    }
}
           

總結:

綜上比較, AIDL實作用戶端與遠端服務通信太繁瑣, 互相通知調用至少需要兩個AIDL接口檔案
在遠端服務中通知用戶端, 需要循環通知, 相當繁瑣!
除非迫不得以(如服務可以是多線程運作), 實在不建議使用AIDL!

用戶端與服務在不同程序時, 建議服務單線程運作,
使用Messenger通信, 這也是Android官方推薦的!
           

簡書: http://www.jianshu.com/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