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

来源:互联网 发布:淘宝买主机靠谱吗 编辑:程序博客网 时间:2024/06/05 15:32

官方文档中国版: 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、MapList中元素都必须是以上类型、其他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
原创粉丝点击