从Service中理解进程间通信Messenger / AIDL(上)代码套路

来源:互联网 发布:mysql yum 安装 编辑:程序博客网 时间:2024/06/04 17:42

转载请注意:http://blog.csdn.net/wjzj000/article/details/77931540

本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)


写在前面

今天在看公司项目的时候,发现了一个有意思的效果。app中有一个锁屏的效果,杀死app后,锁屏仍然存在,说明锁屏运行在另一个进程中。
然后厚颜无耻的占用了安卓组boss的工作的时间,让他给讲了一下进程间通讯的原理。boss很有人类灵魂工程师的天赋,了解到我没涉及到c层的东西,所以并没有深到c层,而是在应用层,展开了一顿灵魂深处的缝缝补补…
那么这篇博客存在的意义就是在boss讲授的基础上增加自己的理解并记录下来。


开始


Messenger进程间通讯

我们先看一段官方文档对Messenger介绍:

如需让接口跨不同的进程工作,则可使用 Messenger 为服务创建接口。服务可以这种方式定义对应于不同类型 Message 对象的 Handler。此 Handler 是 Messenger 的基础,后者随后可与客户端分享一个 IBinder,从而让客户端能利用 Message 对象向服务发送命令。此外,客户端还可定义自有 Messenger,以便服务回传消息。

这是执行进程间通信 (IPC) 的最简单方法,因为 Messenger 会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。

https://developer.android.google.cn/guide/components/bound-services.html


简单叙述一下效果:就是Activity的进程向另一个进程的Service传递一个自定义的类,类内部包含一个x,一个y,由另一个进程的Service计算完毕后回传给我们的Activity。

官方文档是Activity给远端的Service发送消息,这里稍作修改,变成Service给Activity发送消息:

双向互通的思路很简单:既然Activity向Service通信是通过Service回调过来的Messenger,那么我们就在Activity中得到Service的Messenger时,通过这个Messenger把在Activity端生成的Messenger,send过去。这样Service就可以通过Activity的Messenger向我们的Activity发送消息了。

Tips:
Messenger使用Message进行传递消息,Message如果涉及到跨进程,那么携带的数据必须得失Parcelable类型的,因此Serializable是不行的(Can’t marshal non-Parcelable objects across processes.)。
所以String就不能以直接使用Message进行跨进程。这里我们可以使用Bundle做数据传递。

public class RemoteService extends Service {    static final int ACTIVITY_MESSENGER = 1;    static final String SEND_CONTENT = "send_content";    private Messenger activityMessenger;    //构建Messenger,并用来响应绑定组件发送的消息。    class IncomingHandler extends Handler {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case ACTIVITY_MESSENGER:                    if (activityMessenger==null){                        //得到绑定组件发过来的它的Messenger,有了这个Messenger,服务就可以向绑定组件发送消息了。                        activityMessenger= msg.replyTo;                        Message message=Message.obtain();                        message.what=1;                        //此处仅仅发送一个String,但是String不是Parcelable对象,不能使用Message进行跨进程,所以这里用Bundle进行处理。                        Bundle bundle=new Bundle();                        bundle.putString(SEND_CONTENT,"我是来自服务进程的...");                        message.setData(bundle);                        try {                            activityMessenger.send(message);                        } catch (RemoteException e) {                            e.printStackTrace();                        }                    }                    break;                default:                    super.handleMessage(msg);            }        }    }    //声明一个自己的Messenger,用于给绑定服务的组件发送消息。    final Messenger mMessenger = new Messenger(new IncomingHandler());    @Override    public IBinder onBind(Intent intent) {        //回调ServiceConnection,让绑定服务的组件得到Messenger对象        return mMessenger.getBinder();    }}
//用于构建组件的Messenger,并响应服务发送过来的消息private Handler handler=new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            if (msg.what==1){              tvShow.setText(msg.getData().getString(RemoteService.SEND_CONTENT));            }        }    };    private Messenger messenger;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tvShow= (TextView) findViewById(R.id.tv_show);        tvShow.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                //构建自己的Messenger对象,通过Service回调过来的Messenger把自己的Messenger发送给Service,使Service拥有给自己发送消息的能力。                Messenger activityMessenger=new Messenger(handler);                Message message=Message.obtain();                message.what=RemoteService.ACTIVITY_MESSENGER;                message.replyTo=activityMessenger;                try {                    messenger.send(message);                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });        ServiceConnection conn = new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {                //获取Service端的Messenger对象                messenger = new Messenger(iBinder);            }            @Override            public void onServiceDisconnected(ComponentName componentName) {                messenger = null;            }        };        Intent toSerivce = new Intent(this, RemoteService.class);        bindService(toSerivce,conn, Context.BIND_AUTO_CREATE);    }

接下来让我们看一看AIDL方式的进程通讯。


AIDL进程间通讯

简单叙述一下效果:就是Activity的进程向另一个进程的Service传递一个自定义的类,类内部包含一个x,一个y,由另一个进程的Service计算完毕后回传给我们的Activity。

首先我们先new一个AIDL文件:

这里写图片描述

有几个tips:
1、要注意包名问题,需要保持和主项目一直,因此为了不出问题,我们还是使用系统的new AIDL的方式,交给系统去处理吧。
2、如果我们需要用到非基础类型,也就是一些类的时候,我们同样要声明同名的AIDL文件,并且不是使用inteface,而是parcelable(下文会有举例)。这里有个先后顺序的问题:如果先声明了class,那么声明AIDL的时候系统会提示换个名字。解决方法:1、先声明AIDL,在声明class。2、随便给AIDL起个名字,然后再改成class的同名。

生成的第一个AIDL文件:

package com.yang.myapplication;//这个就是我们使用的对应class的AIDL,虽然是同一个包下,但也要导入!!import com.yang.myapplication.RemoteBean;interface IMyAdd {    //注意这里的in,没有的话无法编译通过。    int add(in RemoteBean bean);}

第二个AIDL,也就是我们的同名class的AIDL:

package com.yang.myapplication;parcelable RemoteBean;

然后是我们的class的RemoteBean,因为涉及到进程间通讯,所以我们要对这个类进行序列化:Parcelable(Android特有的序列化方式,性能好,相对实现比较复杂,但是也无所谓的,AS会提示自动都生成) / Serializable(Java):

public class RemoteBean implements Parcelable{    public int x;    public int y;    public RemoteBean(){    }    protected RemoteBean(Parcel in) {        x = in.readInt();        y = in.readInt();    }    public static final Creator<RemoteBean> CREATOR = new Creator<RemoteBean>() {        @Override        public RemoteBean createFromParcel(Parcel in) {            return new RemoteBean(in);        }        @Override        public RemoteBean[] newArray(int size) {            return new RemoteBean[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel parcel, int i) {        parcel.writeInt(x);        parcel.writeInt(y);    }}

定义一个Service:

注册Service设置另一个进程运行:

<service android:name=".RemoteService"    android:process=":remote"/>
public class RemoteService extends Service {    private IBinder iBinder = new MyAddStub();    @Nullable    @Override    public IBinder onBind(Intent intent) {        //返回我们的Stub对象        return iBinder;    }    //注意这里我们继承了AIDL文件编译后生成的同名class对象的一个内部类Stub(具体作用会在详解时展开)    private static final class MyAddStub extends IMyAdd.Stub {        @Override        public int add(RemoteBean bean) throws RemoteException {            return bean.x + bean.y;        }    }}

Activity中使用:
这里随便给一个View设置一个监听事件:

tvShow.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                try {                    RemoteBean bean=new RemoteBean();                    bean.x=11;                    bean.y=11;                    tvShow.setText("远端进程提供的服务:"+iMyAdd.add(bean));                } catch (RemoteException e) {                    e.printStackTrace();                }            }        });        ServiceConnection conn=new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {                //注意!这里是进程间通讯和非进程间通讯的区别                iMyAdd = IMyAdd.Stub.asInterface(iBinder);            }            @Override            public void onServiceDisconnected(ComponentName componentName) {                iMyAdd=null;            }        };        Intent toSerivce = new Intent(this, RemoteService.class);        bindService(toSerivce,conn, Context.BIND_AUTO_CREATE);

到这里,我们这个进程间通讯的代码套路就完成了。接下来的部分,就开始分析进程间通讯的原理。但是由于篇幅过长,解析篇的内容将在下部分中展开。


尾声

最后希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp##

阅读全文
0 0
原创粉丝点击