美文网首页Android知识
Android-服务跨进程通信(Binder/Messenger

Android-服务跨进程通信(Binder/Messenger

作者: lioilwin | 来源:发表于2017-08-27 16:38 被阅读96次

    官方文档中国版: 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

    相关文章

      网友评论

        本文标题:Android-服务跨进程通信(Binder/Messenger

        本文链接:https://www.haomeiwen.com/subject/njxidxtx.html