Android-服务跨进程通信(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(100);
    }
}

// 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 1:                 
                    Toast.makeText(getApplicationContext(), "service hello!", Toast.LENGTH_SHORT).show();                   
                    Thread.sleep(3000);
                    // 向客户端发消息
                    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){
            // 接收远程服务消息
            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;
            // 向远程服务发消息             
            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();  
        // 绑定远程服务(不同进程)
        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=0;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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值