Android IPC机制

    IPC含义是进程间通信或者说跨进程通信。

    在介绍进程通信之前,我们先理解android中的多进程。我们给指定的四大组件设置androdi:process属性,就开启了多进程模式。一眼看去还是很简单,实际运用时会遇到不少问题。

    开启android多进程方法常用的也就是给四大组件设置androdi:process属性,有两种声明方式当然含义也不同。还有通过jni在native层fork一个新的进程(我没用过,暂时不介绍)。下面是开启多进程的代码。

         <activity
            android:name=".TwoActivity"
            android:process=":remote"
            android:label="@string/title_activity_two" >
        </activity>
        <activity
            android:name=".ThirdActivity"
            android:process="com.unifou.demo.remote"
            android:label="@string/title_activity_third" >
        </activity>

    可以看到两个Activity的process属性分别是“:remote”和“:com.unifou.demo.remote”,两者的区别如下

 “:”是一种简写,TwoActivity进程名为com.unifou.demo:remote,包含包名信息。ThirdActivity进程名为com.unifou.demo.remote。

    以“:”开头的进程是私有进程,其他的应用组件不能跟他跑在同一个进程。而完整声明的进程(不以“:”开头的进程)则是全局进程,其他的应用可以ShareUID的方式跟他跑在同一个进程。

    多进程会造成以下问题

    1.单例模式和静态成员都会失效。

    2.线程同步机制也会失效。

    3.SharedPreferences可靠性下降(不支持两个进程同步读写)。

    4.Appliceation会多次创建。

    IPC基础概念。

    主要包含三方面内容:Parcelable接口、Serializable接口、Binder。Parcelable和Serializeable使用来完成对象序列化和反序列化。

    Serializable接口是java提供的序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。使用起来简单,不过注意使用时声明一个serialVersionUID,虽然不声明也不会序列化失败,但是如果不声明涉及版本更新,一旦更改了实现了Serializeable接口类中的成员变量数量,则会导致反序列化失败。这个serialVersionUID使用来辅助序列化和反序列化的。工作机制是每次序列化会将serialVersionUID一起写入到文件,反序列化的时候则会去检查serialVersionUID,相同则可以反序列化,不同则不可以。如果声明了serialVersionUID,当我们增加了或者删减了成员变量,仍然可以序列化成功,最大限度的还原数据,当然改变了类名或者改变了成员变量类型,serialVersionUID验证可以通过,但还会失败,类的结构发生改变,老版本数据无法还原出新的对象。

    Parcelable接口是android提供的序列化接口写比Serializebale稍微复杂点。写法就不介绍了,将鼠标放在Parcelabel接口上会有提示。二者的区别是Serializable是通过大量I/O操作,比较慢,Parcelable是android独有的序列化,Parcelable主要用于内存,效率高。

    Binder是android中的一个类,实现了IBinder接口。从IPC的角度看是android中一种跨进程间通信的方式,从应用层看Binder是服务端跟客户端通信的媒介。主要用于AIDL和Service(Messenger底层也是AIDL)。这里就用AIDL举例,写法如下

   aidl文件,服务端客户端都需要。(本例是在两个应用中)接口参数类型只支持八种基本类型、String和CharSequence、实现Parcelable的自定义类型以及list和Map。然后参数如果是输入,前面加in,如果是输出前面加out,底层实现有开销。AIDL接口只支持方法,不支持声明静态长量。

   package com.unifou.ClientAIDL;

    interface Testaidl{
	String getName();
	int setID(in int id,out byte[] name);
    }

   服务端的 service 。service需要声明action,客户端bindService需要action以及包名(这是两个应用bindService的办法),如果service 有增加自定义权限,也需要在客户端增加相应权限。

   

    public class TestService extends Service{
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return new testBinder();
	
	class testBinder extends Testaidl.Stub{
		@Override
		public String getName() throws RemoteException {
			// TODO Auto-generated method stub
			return null;
		}
		@Override
		public int setID(int id, byte[] name) throws RemoteException {
			// TODO Auto-generated method stub
			return 0;
		}
	 }
    }

客户端bindService类

public class TestClient {
	
	public Testaidl testaidl;
	private TestService service;
	
	class TestService implements ServiceConnection{
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub
			testaidl = Testaidl.Stub.asInterface(service);
			Log.i("unifou", "成功");
		}
		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub
		}
	}
	
	public TestClient(Context context){
		service = new TestService();
		Intent intent = new Intent();
		intent.setAction("com.example.server.remote.TestService");
		intent.setPackage("com.example.server");
		context.bindService(intent, service, Context.BIND_AUTO_CREATE);
	}
	
	public void TestonDestroy(Context context){
		context.unbindService(service);
	}
}

然后我们来分析一下根据Testaidl .aidl系统自动生成的java代码。源码如下:

package com.unifou.ClientAIDL;
public interface Testaidl extends android.os.IInterface{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.unifou.ClientAIDL.Testaidl{
            private static final java.lang.String DESCRIPTOR = "com.unifou.ClientAIDL.Testaidl";
            /** Construct the stub at attach it to the interface. */

            public Stub(){

                this.attachInterface(this, DESCRIPTOR);
            }
        /**
         * Cast an IBinder object into an com.unifou.ClientAIDL.Testaidl interface,
         * generating a proxy if needed.
         */
        public static com.unifou.ClientAIDL.Testaidl asInterface(android.os.IBinder obj){
            if ((obj==null)) {
                    return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.unifou.ClientAIDL.Testaidl))) {
                    return ((com.unifou.ClientAIDL.Testaidl)iin);
            }
            return new com.unifou.ClientAIDL.Testaidl.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_getName:
            {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _result = this.getName();
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            case TRANSACTION_setID:
            {
                data.enforceInterface(DESCRIPTOR);
                int _arg0;
                _arg0 = data.readInt();
                byte[] _arg1;
                int _arg1_length = data.readInt();
                if ((_arg1_length<0)) {
                    _arg1 = null;
                }else {
                    _arg1 = new byte[_arg1_length];
                }
                int _result = this.setID(_arg0, _arg1);
                reply.writeNoException();
                reply.writeInt(_result);
                reply.writeByteArray(_arg1);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }
    private static class Proxy implements com.unifou.ClientAIDL.Testaidl{
        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 getName() 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);
                mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
                }
            finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }
    @Override

         public int setID(int id, byte[] name) 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(id);
                    if ((name==null)) {
                        _data.writeInt(-1);
                    }else {
                        _data.writeInt(name.length);
                    }
                mRemote.transact(Stub.TRANSACTION_setID, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readInt();
                _reply.readByteArray(name);
                  }finally {
                        _reply.recycle();
                        _data.recycle();
                  }
                return _result;
            }
        }
     static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
     static final int TRANSACTION_setID = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
      }
public java.lang.String getName() throws android.os.RemoteException;
public int setID(int id, byte[] name) throws android.os.RemoteException;
}

    将它排版一下,仔细看看,发现Testaidl 类的结构还是比较简单的,它实现了IInterface接口,并自身也是一个接口,所有在Binder传输的接口都需要继承IInterface,它声明了两个方法getName和setID,在内部类中给两个声明了两个ID,用于标识transact调用的哪个方法。DESCRIPTOR是该Binder唯一标识。asInterface该静态方法返回一个Testaidl对象,当服务端跟客户端在同一个进程返回的是Testaidl对象,在不同进程时返回的是Stub.Proxy对象。asBinder返回的是当前Binder对象。onTransact这个方法运行在服务端Binder线程池中,客户端发起跨进程请求时,远程请求会由系统交给该方法执行,通过code可以知道请求的是哪个方法,android.os.Parcel data和android.os.Parcel reply分别是读取目标方法传过来的参数和写入返回值(如果有返回值)以及返回结果。

    通过以上的分析,我们可以了解到Binder的工作机制。客户端发起跨进程请求,根据code调用Proxy中的实现,将传入的参数写入_data,然后执行transact,该方法会将发起RPC(远程过程调用)请求,同时将该线程挂起;系统会调用onTransact,同样根据code调用服务端的实现,知道RPC过程返回后,当前线程继续执行,并从_reply取出返回的结果返回。我们要注意的地方是远程方法是耗时的话,不能再UI线程调用。其次,由于服务端Binder运行在Binder线程池中,所以不管是否耗时,我们都应该采取同步的方式实现。

    


发布了12 篇原创文章 · 获赞 6 · 访问量 938

猜你喜欢

转载自blog.csdn.net/qq_30867605/article/details/80207386