IPC机制<一>

一、Android IPC简介

IPC : Inter-Process-Communication 进程间通信或者跨进程通信。

要想理解进程间通信,那么就需要知道什么是进程,什么是线程?

线程:线程是CPU调度的最小单元,同时也是一种有限的系统资源

进程:一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程。

既然是跨进程通信,那么就会涉及到多进程的问题,多进程的情况分为两种:

  1. 一个应用因为某种原因自身需要采用多进程模式来实现,至于原因有很多种,例如,某些模块因为特殊的原因需要运行在单独的进程中,又或者加大一个应用所需要的内存。
  2. 另一种原因是当前应用需要向其他应用获取数据。

二、Android中的多进程模式

1、开启多进程模式

在Android中,多进程指的是一个应用中存在多个进程的情况。
在Android中使用多进程的方法有两种:

  1. 那就是给四大组件在AndroidMenifest中指定process属性
  2. 另一种非常规的多进程方法,那就是通过JNI在native层fork一个新的多进程,这种方法属于特殊情况,这里不做讨论。

    <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
    
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <activity
        android:name=".SecondActivity"
        android:process=":remote">
    </activity>
    <activity android:name=".ThirdActivity"
        android:process="cn.demo.zx_ipc_learn.remote">
    </activity>
    

如果没有指定进程,那么它的进程的名称就是包名。

指定:remote表示进程的名称为 包名:remote,并且这种属性的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,而进程名不以“:”开头的进程属于全局进程,其他应用通过ShareUID方式可以和它跑在同一个进程中

我们知道Android系统会为每一个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。这里需要说明的是,两个应用需要通过ShareUID跑在同一个进程中是有要求的,这两个应用具有相同的ShareUID并且签名相同才可以。在这种情况下,它们可以相互访问对方的私有数据,比如data目录、组件信息等,不管它们是否跑在同一个进程中。如果跑在同一个进程中,还可以共享内存数据。

2、多进程模式的运行机制

所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败,这是多进程所带来的主要影响。

原因:Android为每一个应用分配了一个独立的虚拟机,或者说为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生很多副本。

一般来说,使用多进程会造成如下几方面的问题:

  1. 静态成员和单例模式完全失效
  2. 线程同步机制完全失效
  3. SharePreferences的可靠性下降
  4. Application会多次创建

三、IPC基础概念介绍

想要很好的理解PIC,那么就需要知道一些IPC的基本概念。Serializable和Parcelable接口以及Binder。

1、Serializable接口

Serializable是java提供的序列化空接口。

如何进行对象的序列号和反序列化呢?

例如:

/**
 * 即使不设置serialVersion也能序列化,因为系统会随机提供一个serialVersionUID,但是却不能反序化。
 * 因为SerialVersionUID的工作机制是这样的:序列化的时候系统会把当前类的serialVersionUID
 * 写入序列化的文件中(也可能是其他中介),当反序列化的时候,系统会去检测文件中的serialVersionUID,
 * 看它是否和当前类的serialVersionUID一致,如果一致就是说明序列化的类的版本和当前类的版本
 * 是相同的,这个时候可以成功序列化。因此想要反序列化,那么就得手动设置serialVersionUID的值。
 * 当我们手动指定了它之后,我们很大程度上避免反序列化失败,例如删除或者增加了新的成员变量的时候,反序列化
 * 也能成功。但是当类的结构发生了根本性变化之后,即使serialVersionUID验证通过,反序列化也会失败,
 * 例如,修改了类名,修改了成员变量的类型等等。
 */
public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    public int userId;
    public String userName;
    public boolean isMale;
        ....
}

 /**
 * 序列号
 * @param view
 */
public void serialize(View view){

    new Thread(){
        @Override
        public void run() {
            super.run();
            User user = new User(0,"zhangx",false);
            File file = new File((Utils.TOTAL_PATH));
            if(!file.exists()){
                file.mkdirs();
            }
            File cacheFile = new File(Utils.PATH);
            ObjectOutputStream out = null;
            try {
                out = new ObjectOutputStream(new FileOutputStream(cacheFile));
                out.writeObject(user);
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }.start();


}

/**
 * 反序列化
 * @param view
 */
public void unSerialize(View view){


    final File file = new File(Utils.PATH);

    new Thread(){
        @Override
        public void run() {
            super.run();
            User user = null;
            if(file.exists()){
                ObjectInputStream in = null;
                try {
                    in = new ObjectInputStream(new FileInputStream(file));
                    user = (User) in.readObject();
                    System.out.println(user.toString());
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }.start();

}

注意:

  1. 静态成员变量属于类不属于对象,所以不会参与序列号过程
  2. 用transient关键字标识的成员变量不参与序列号过程。

2、Parcelable接口

public class Student implements Parcelable {
    public String name;
    public boolean isMale;

    /**
     * Book类也是一个继承了Parcelable的类
     */
    public Book book;

    public Student(String name, boolean isMale, Book book) {
        this.name = name;
        this.isMale = isMale;
        this.book = book;
    }

    /**
     * Parcel内部包装了可序列化的数据,可以在Binder中自由传输
     * @param in
     */
    protected Student(Parcel in) {
        name = in.readString();
        isMale = in.readByte() != 0;
        book = in.readParcelable(Book.class.getClassLoader());
    }

    /**
     * 反序列化
     */
    public static final Creator<Student> CREATOR = new Creator<Student>() {

        /**
         * 创建序列化对象
         * @param in
         * @return
         */
        @Override
        public Student createFromParcel(Parcel in) {
            return new Student(in);
        }

        /**
         * 创建序列号数据
         * @param size
         * @return
         */
        @Override
        public Student[] newArray(int size) {
            return new Student[size];
        }
    };

    /**
     * 内容描述
     * 当当前对象中存在文件描述符时,此方法返回1
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 序列化
     * @param dest
     * @param flags
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeByte((byte) (isMale ? 1 : 0));
        dest.writeParcelable(book, flags);
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", isMale=" + isMale +
                ", book=" + book.toString() +
                '}';
    }
}

3、Binder

从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowMnager、等等)和相应ManagerService的桥梁;

Binder的工作机制:

从Android应用层来说,Binder是客户端和服务端进行通信的媒介。当bindService的时候,服务端会返回一个Binder对象,通过这个对象就能,客户端就能获取服务端的数据与服务,这里的服务包括普通的服务和基于AIDL的服务。

在Android中,创建一个AIDL文件的话,Android系统会生成Binder的java类。

Binder的工作原理:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\AndroidStudioDemo\\zx_telephone_monitor\\zx_remote_service\\src\\main\\aidl\\cn\\demo\\zx_remote_service\\IService.aidl
 */
package cn.demo.zx_remote_service;
// Declare any non-default types here with import statements

public interface IService extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements cn.demo.zx_remote_service.IService
    {
        /**
         * Binder唯一标识,一般用类名表示
         */
        private static final java.lang.String DESCRIPTOR = "cn.demo.zx_remote_service.IService";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an cn.demo.zx_remote_service.IService interface,
         * generating a proxy if needed.
         * 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,
         * 如果客户端与服务端在同一进程中,则返回Stub本身,否则返回的是系统封装后的Stub.Proxy对象。
         */
        public static cn.demo.zx_remote_service.IService asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof cn.demo.zx_remote_service.IService))) {
                return ((cn.demo.zx_remote_service.IService)iin);
            }
            return new cn.demo.zx_remote_service.IService.Stub.Proxy(obj);
        }

        /**
         * 返回当前Binder对象
         */
        @Override 
        public android.os.IBinder asBinder()
        {
            return this;
        }

        /**
         * 这个方法运行在服务端的Binder线程池中,
         * 当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。
         * code:服务端通过code可以确定客户端请求的目标方法是什么
         * 接着从data中取出目标文件所需的参数,然后再执行此方法,
         * 当目标方法执行完成之后,就向reply中写入返回值。
         * 如果返回false表示客户端请求失败。
         */
        @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_callMethod:
                {
                    data.enforceInterface(DESCRIPTOR);
                    this.callMethod();
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
        private static class Proxy implements cn.demo.zx_remote_service.IService
        {
            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;
            }

            /**
             * 这个方法运行在客户端,当客户端调用此方法时候,它的内部实现是这样的,
             * 首先创建此方法的时候所需要的输入型Parcel对象_data和输出对象_reply.
             * 将该方法的参数信息写入_data中
             * 接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起
             * 然后服务端的onTransact方法会被调用,直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果。
             */
            @Override 
            public void callMethod() 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_callMethod, _data, _reply, 0);
                    _reply.readException();
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
        static final int TRANSACTION_callMethod = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    public void callMethod() throws android.os.RemoteException;
}

这里需要注意:

  1. 当客户端发起远程请求的时候,由于当前线程会被挂起知道服务端进程返回数据,所以如果是一个远程方法是很耗时的,那么不能在UI线程中发起此远程请求。
  2. 由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程池中了。

AIDL文件的本质是系统为我们提供了一种快速实现Binder的工具。

还有一点需要注意,在发起的远程调用的时候,服务端因为异常原因终止了,但是客户端却不知道,怎样解决?

需要设置死亡代理:

IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        System.out.println("服务端终止。。。。。。。。。:::"+mIService);

        mIService.asBinder().unlinkToDeath(mDeathRecipient,0);
        mIService=null;
        //TODO 重新绑定服务
    }
};


ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mIService = IService.Stub.asInterface(service);
        try {
            /**
             * 设置死亡代理
             */
            service.linkToDeath(mDeathRecipient,0);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};

四、Android中的IPC方式

1、使用Bundle

简单易用,但是只能传输Bundle支持的数据类型,适用于四大组件间的进程间通信。

2、使用文件共享

简单易用,但是不适合高并发场景,并且无法做到进程间的即时通信。

3、使用Messenger

Messenger是一个轻量级的IPC方案,它的底层是AIDL.它的作用主要是传递消息。无法调用服务端方法。

public class MessengerService extends Service {

    private class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {

                case GlobalUtils.MSG_FROM_CLIENT:

                    /**
                     * 接收客户端发送过来的消息
                     */
                    String msg1 = msg.getData().getString("msg");
                    System.out.println("获取的数据::::::"+msg1);

                    /**
                     * 向服务的发送消息
                     */
                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null,GlobalUtils.MSG_FROM_CLIENT);
                    Bundle data = new Bundle();
                    data.putString("reply","你的消息我已收到,稍后会回复你。");
                    replyMessage.setData(data);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:break;
            }
        }
    }

    public MessengerService() {
    }


    /**
     * 创建与Handler相关联的Messenger对象。
     */
    private Messenger mMessenger = new Messenger(new MessengerHandler());

    /**
     * 返回mMessenger对象中的Binder
     * @param intent
     * @return
     */
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}

Activity::

public class MessengerActivity extends AppCompatActivity {

    /**
     * 创建与Handler相关联的Messenger对象,主要用于接收消息。
     */
    private Messenger mG = new Messenger(new MessengerHandler());

    private class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case GlobalUtils.MSG_FROM_CLIENT:
                    System.out.println("客户端收到的数据::"+msg.getData().getString("reply"));
                    break;

                default:break;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);

        Intent intent = new Intent(this,MessengerService.class);
        bindService(intent,conn,BIND_AUTO_CREATE);

    }

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Messenger messenger = new Messenger(service);


            Message msg = Message.obtain(null,GlobalUtils.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg","这里是客户端。。。。。。。。。。");
            msg.setData(data);

            //注意此处,
            msg.replyTo = mG;
            try {
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
}

4、使用AIDL

5、使用ContentProvider

6、使用Socket

五、Binder连接池

六、选择合适的IPC方式

源码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值