Android IPC机制

来源:互联网 发布:诺基亚n900运行windows 编辑:程序博客网 时间:2024/06/05 15:59

前言:笔者之前也看了很多关于IPC之类的文章和书籍,发现这个东西真的很难理解清楚,很少有人把它介绍清楚,因为关于IPC真的特别复杂,其实我们应该都明白在Android里面,要想研究一些底层的实现,那么我们不可避免的去看源码。鉴于IPC,要想真正理解透彻你只有从源码角度看起,但是你会发现关于IPC这块底层几乎全部都是C/C++代码,看起来真的很头疼。介于技术笔者这块也只能说上层原理和作用。

IPC是Inter-Process Communication的缩写,是进程间通信意思。在说这个话题之前,我们知道在PC端我们经常也会听到关于IPC机制,Android系统是基于Linux内核的,在Linux上面它是怎么进行进程间通信的呢?在大学里面如果是学过关于操作系统,应该会了解点,进程之间通信的方式有:信号量(Signal),消息队列,消息邮箱,还有传统的管道(Pipe),或者通过文件共享的方式等等,比如说A进程将数据写入文件,B进程再去读取这个文件。其实读者应该明白,关于两个进程之间是不能够直接通信的,这个是肯定的,很简单,这是两个彼此独立的应用程序,拥有不同的内存地址,那么他们的通信其实都是通过操作系统的支持,A进程向操作系统申请一块内存空间,把该共享的数据放入,B进程也向操作系统申请读取这边内存空间,这时候就可以进行数据交换了。
那么在Android中关于进程间通信是否也像上面说的Linux一样的?
答案是否了,Android并没有继承关于Linux IPC之间的秉性,而是采用了一套自己都有的方式——Binder机制

什么是Binder?又该如何了解Binder呢?

Binder是Android之中进行进程间通信最有特色的一种,根据在工作中的性质可能会大量用到,直接来说Binder是Android系统里面提供的一个类,实现了IBinder接口。从IPC角度来说呢Binder是Android中一种跨进程通信方式,从Android Frameword角度来说,Binder是ServiceManager连接各种Manager(ActivityManager,WindowManager等等)和相应ManagerService的桥梁。从Android应用层角度来说是客户端和服务端通信的媒介。可想而知其重要性了,到这里我相信很多人都用过,在bindService的时候服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象我们就可以获取我们想要的数据。

我们知道在Android中进行进程中的通信只有一种方式,那就是在AndroidMenifest文件中给四大组件(Activity,Service,Receiver,ContentProvider)指定android:process属性,这是在一个应用之间,让不同的组件独立于一不同的进程。也就实现了多进程通信了,比如说我可以在我的Activity配置文件中加入android:process=romove属性这样就让我的这个Activity运行在一个独立的进程里面了,此时让他和service通信也就实现了IPC。这和在不同的应用之间进行进程间通信是一样的,这点需要读者注意,理解清楚。读者在网上可以看到很多通过配置android:process属性在一个应用中来实现IPC其实和我刚刚说的不同应用都是一样的。 为了让读者看起来更清楚点,我们采用在不同的应用来进行进程间通信。

Android中IPC方式主要有哪些?

1.Bundle,Intent
2.文件共享
3.AIDL
4.Messenger
5.ContentProvider
6.Socket

接下来会一一讲解,本文主要是讲解关于Android中的IPC(重点:Binder机制),关于一些服务的基本概念将不再叙述,可以参考网上的其他文章。在Android开发中Binder主要是用于服务中,接下来我们先来看一个案例,通过案例来分析。先从大家熟悉的AIDL(Android Interface Definition Language)说起。
新建项目MyProject,里面包含主Activity MainActivity.java,MyService.java,ImiddlePerson.aidl.

MainActivity.java如下:

public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);    }}

MyService.java如下:

public class MyService extends Service {    @Override    public IBinder onBind(Intent arg0) {        return new MyBind();    }    private class MyBind extends ImiddlePerson.Stub{        @Override        public String revers(String str) throws RemoteException {            return str.toUpperCase();        }        @Override        public void print() throws RemoteException {            System.out.println("print...");        }        @Override        public int sun(int a, int b) throws RemoteException {            return a+b;        }    }}

ImiddlePerson.aidl.如下:

package com.xyy.demo; interface ImiddlePerson  {     String revers(String str);     void print();     int sun(int a,int b); }

从上面代码可以看出来,代码异常简单,我们建立了ImiddlePerson.aidl.文件,在文件之中我们声明了三个简单的方法,revers方法用于客户端调用传替参数并将其转成大写。print方法无参只是打印了一句字符串,sum方法用户客户端传替两个参数过来将其相加。关于AIDL文件你需要注意,里面没有修饰符一说,所以不能写修饰符,不然就会报错,还有虽然在同一个包下,但是还是需要导包,这就是AIDL文件的特殊之处,读者需注意。
接下来你会在gen目录下看到生成的ImiddlePerson .java文件,代码如下:

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: C:\\TouchTest\\BadgeViewTest\\src\\com\\xyy\\demo\\ImiddlePerson.aidl */package com.xyy.demo;public interface ImiddlePerson extends android.os.IInterface {    /** Local-side IPC implementation stub class. */    public static abstract class Stub extends android.os.Binder implements            com.xyy.demo.ImiddlePerson {        private static final java.lang.String DESCRIPTOR = "com.xyy.demo.ImiddlePerson";        /** Construct the stub at attach it to the interface. */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.xyy.demo.ImiddlePerson interface,         * generating a proxy if needed.         */        public static com.xyy.demo.ImiddlePerson asInterface(                android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.xyy.demo.ImiddlePerson))) {                return ((com.xyy.demo.ImiddlePerson) iin);            }            return new com.xyy.demo.ImiddlePerson.Stub.Proxy(obj);        }        @Override        public android.os.IBinder asBinder() {            return this;        }        @Override        public boolean onTransact(int code, android.os.Parcel data,                android.os.Parcel reply, int flags)                throws android.os.RemoteException {            switch (code) {            case INTERFACE_TRANSACTION: {                reply.writeString(DESCRIPTOR);                return true;            }            case TRANSACTION_revers: {                data.enforceInterface(DESCRIPTOR);                java.lang.String _arg0;                _arg0 = data.readString();                java.lang.String _result = this.revers(_arg0);                reply.writeNoException();                reply.writeString(_result);                return true;            }            case TRANSACTION_print: {                data.enforceInterface(DESCRIPTOR);                this.print();                reply.writeNoException();                return true;            }            case TRANSACTION_sun: {                data.enforceInterface(DESCRIPTOR);                int _arg0;                _arg0 = data.readInt();                int _arg1;                _arg1 = data.readInt();                int _result = this.sun(_arg0, _arg1);                reply.writeNoException();                reply.writeInt(_result);                return true;            }            }            return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements com.xyy.demo.ImiddlePerson {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote) {                mRemote = remote;            }            @Override            public android.os.IBinder asBinder() {                return mRemote;            }            public java.lang.String getInterfaceDescriptor() {                return DESCRIPTOR;            }            @Override            public java.lang.String revers(java.lang.String str)                    throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                java.lang.String _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeString(str);                    mRemote.transact(Stub.TRANSACTION_revers, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readString();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public void print() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_print, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }            @Override            public int sun(int a, int b) throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(a);                    _data.writeInt(b);                    mRemote.transact(Stub.TRANSACTION_sun, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_revers = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_print = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);        static final int TRANSACTION_sun = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);    }    public java.lang.String revers(java.lang.String str)            throws android.os.RemoteException;    public void print() throws android.os.RemoteException;    public int sun(int a, int b) throws android.os.RemoteException;}

可以看到系统为我们自动生成的代码。这就是AIDL文件。
我们发现我们的ImiddlePerson是继承于IInterface接口,接着可以看到

public static abstract class Stub extends android.os.Binder implements com.xyy.demo.ImiddlePerson

到这里我们应该明白了。上面MyService.java里面的代码了。(不再叙述)。
接着我们在Manifest文件之中配置我们的MyService并配置上我们的action

  <service android:name="com.xyy.demo.MyService">          <intent-filter>             <action android:name="com.xyy.demo.MyService"/>          </intent-filter> </service>

将其部署到我们的手机,此时一个简单的远程服务以及部署到我们的手机上了。
接下来新建项目MyProject2客户端应用,这个就比较简单了,首先你需要将之前的AIDL文件copy过来,记住连同包名一起,而且一点不能出错,否则定会反序列化失败。接着在我们的主Activity里面完成如下代码:

/** *  * @author xyy * */public class MainActivity extends Activity {    private ImiddlePerson iMiddPerson;    public static final String TAG = "MainActivity";    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        requestWindowFeature(Window.FEATURE_NO_TITLE);        setContentView(R.layout.activity_main);    }    /**     * 绑定方式开启服务     * @param view     */    public void bind(View view){        Intent intnet = new Intent();        intnet.setAction("com.xyy.demo.MyService");        bindService(intnet, new MyConn(), BIND_AUTO_CREATE);    }    private class MyConn implements ServiceConnection{        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            iMiddPerson = ImiddlePerson.Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    }    /**     * 调用服务里面的方法     */    public void call(View view){        try {            Log.d(TAG, iMiddPerson.revers("android"));            Log.d(TAG, iMiddPerson.sun(20, 30)+"");            iMiddPerson.print();        } catch (RemoteException e) {            e.printStackTrace();        }    }}

可以看到我们以绑定的方式启动服务,在绑定成功后会拿到右Binder通过转换后的AIDL对象iMiddlePerson,最后调用方法。
打印结果如下:

09-17 04:35:38.450: D/MainActivity(20584): ANDROID09-17 04:35:38.450: D/MainActivity(20584): 5009-17 04:35:38.450: I/System.out(20075): print...

这样我们的一个通过AIDL的方式的远程服务也就结束了,(如果不懂没关系,下面可以叙述。)可以发现代码特别简单,实现起来也很快,几乎不需要太多逻辑。。。。其实不然,IPC哪有这样简单,这只不过我写的特别简单的一个案例,接下来我们就开始分析。

原创粉丝点击