IPC

IPC是Inter-Process Communication的缩写,含义为进程间通信或跨进程通信,是指两个进城之间进行数据交换的过程.

IPC方式的优缺点和适用场景

名称优点缺点适用场景
Bundle简单易用只能传输Bundle支持的数据类型四大组件之间的通信
文件共享简单易用不适合高并发场景,并且无法做到进程间的即使通信无并发访问情形,交换简单的数据实时性不高的场景
AIDL功能强大,支持一对多并发通信,支持实时通信使用稍复杂,需要处理好线程同步一对多通信且有RPC需求
Messenger功能一般,支持一对多串行通信,支持实时通信不能很好处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型低并发的一对多即时通信,无RPC需求,或者无需要返回结果的RPC需求
ContentProvider在数据源访问页面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作可以理解为受约束的AIDL,只要提供数据源的CRUD操作一对多进程间的数据共享
Socket功能强大,可以通过网络传输字节流,支持一对多并发实时通信实现细节稍微有点繁琐,不支持直接的RPC网络数据交换

开启多进程

<activity
   android:name=".FirstActivity"
   android:process=":remote"
   />
<activity
   android:name=".SecondActivity"
   android:process="com.test.bt.remote"
   />

AndroidManifest中添加process属性即可在activity启动时新建一个进程并且运行在新进程中,process的命名有2种方式:

  • :为前缀命名的activity启动的进程默认前面会加上包名,即FirstActivity会启动在包名:remote的进程中.此进程为本应用的私有进程,即其他应用的组件无法运行在该进程内.
  • SecondActivitycom.test.bt.remote的完整的命名方式,不会附加包名信息.

对象序列化

Intent和Binder传输数据时需要序列化数据,然后将获取的数据反序列化,通过Serilizable接口和Parcelable接口可以完成序列化和反序列化

Serializable接口
public class User implements Serializable{
    private static final long serialVersionUID = 11242141L;
    public String name;
    public int age;
    ...
}

所有操作系统自动完成,只需加上serialVersionUID
:务必加上serialVersionUID ,可手动指定或系统生成,作为序列化和反序列化的识别码.一般情况下不加serialVersionUID 也可序列化成功,但是当类的结构发生改变,或者未来升级过程中新增删除了某些成员变量,反序列化仍然可以成功,可最大限度方式数据丢失.

Parcelable接口
public class User implements Parcelable{
    public String name;
    public int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    /*返回当前对象的内容描述
    * 当前对象中存在对象描述符时返回1
    * 其余情况均返回0
    */
    @Override
    public int describeContents() {
        return 0;
    }

    //序列化
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
    //反序列化
    public  static  final  Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){
        //从序列化的对象中还原原始对象
        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }
        //创建指定长度的原始对象数组
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    private  User(Parcel in){
        name = in.readString();
        age = in.readInt();
    }
}

主要序列化和反序列化在实现了Parcelable接口的类内实现

Parcelable和Serializable对比
Serializable是java中的序列化接口简单易用,但是在序列化和反序列化过程中需要大量I/O操作,开销很大
Parcelable是Android中的序列化接口,使用时的效率很高,但是缺点是使用起来较为麻烦.
推荐在进行文件化存储和网络传输使用Serializable,而在内存序列化上使用Parcelable


Binder

Binder是Android中的一个类,实现了IBinder的接口.
从IPC角度来说Binder是Android中的一种跨进程方式

AIDL

使用AIDL分析Binder的工作机制
User.java文件使用上文中的代码

//User.aidl
package com.test.bt.aidl

parcelable User;
//UserManager.aidl
package com.test.bt.aidl

import com.test.bt.aidl.User;
interface UserManager{
    User getUser(in int position);
}

需要在User.aidl中声明User类.
UserManager.aidl是一个自定义的接口,尽管和User.aidl在同一个包内,但是仍需要导入
创建AIDL方法后会在gen目录下自动生成了UserManager.java文件,太长就不贴出来

核心方法作用
Stub内部类,其内部决定了请求是走代理还是直接返回自身的Binder对象
asInterface(IBinder obj)用于将服务端的Binder对象转化为客户端的Binder对象,如果客户端服务端处于同一个进程,返回的是服务端对象本身,否则根据proxy代理返回封装后的Stub.proxy
asBinder返回当前Binder对象
onTransact根据请求的code得知请求的方法是什么,并根据传入参数进行操作并设定reply返回值,如果返回值为false则代表请求失败,可以以此做权限验证
Proxy此方法运行于客户端,创建需要的输入型对象_data,输出型对象_reply和返回值对象List,然后把参数信息写入_data,然后调用transact方法发送RPC请求,然后当前线程挂起,服务端调用onTransact方法,直到RPC返回数据,线程继续执行,并且从_relpy方法中取出数据

注:不要在UI线程中去进行请求

服务端
public class UserService extends Service{
    private List<User> mUsers = new ArrayList<User>();
    //实现UserManager.Stub()中的方法
    private Binder mBinder = new UserManager.Stub(){
        @Override
        public User getUser(int position) throws RemoteException{
            return mUsers.get(position);
        }

    }
    //将Binder返回给客户端
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
    ...
}
客户端
//监听是否连上service
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
      public void onServiceConnected(ComponentName name, IBinder service) {
          UserManager userManager = UserManager.Stub.asInterface(service);
          User user = userManager.getUser(2);
      }

      @Override
      public void onServiceDisconnected(ComponentName name) {

      }
  }
   //绑定服务并添加监听
   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this, UserManagerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
       ...
    }

注:onServiceConnected和onServiceDisconnected是运行在UI线程中,不要在这两个方法中运行耗时方法


AIDL中常用权限验证方法

  • 在onBind中验证
验证不通过直接返回null,可以通过设定permission,Uid,Pid或者其他参数传值等方式进行判断.
  • 在onTransact中验证
不通过直接返回false.方法同上

Android的跨进程的通信方式

  • Bundle
传递的数据必须能够被序列化,Bundle本身也实现了Parcelable接口
  • 文件共享
SharedPreferences是个特例,系统对于SharedPreferences的读写有一定的缓存策略,在多进程模式下,系统对其的读写会变得不可靠,很大几率会丢失数据,所以不推荐在多进程中使用SharedPreferences.
  • Messenger
底层还是AIDL,轻量级的IPC方案,用于传递Message对象
  • AIDL
前面已经研究了AIDL的基本使用,较为常用的跨进程通讯方式
  • ContentProvider
底层实现为Binder,提供不同应用实践的数据共享的方式
  • Socket
网络通信的方式,不仅可以实现进程间的通信,还可以进行设备之间的通信

本文摘自 Android开发艺术探索 一书,并进行整理

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值