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

转载请注意: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##

猜你喜欢

转载自blog.csdn.net/wjzj000/article/details/77931540