Android中常见的IPC通信方式

如何在一个Android项目中使用多个进程

在AndroidManifest中配置使用android:process属性,因为这个属性只能在AndroidManifest中使用,所以跨进程只能作用于四大组件
这个属性的使用,有以下几种特点

  1. 如果没有配置 android:process这个属性,默认为项目的包名
  2. 这个属性可以配置 :remote 或者 包名.remote, 使用冒号这种方式,创建的是这个应用的私有进程,此进程不能与其他 应用 通过ShareId的方式跑在同一个进程中
  3. 因为使用android:process虽然新开了一个进程,但是还是之前的app,所以UID是一样的

可用 adb shell ps | grep 项目名 看PID和UID
USER PID PPID VSIZE RSS WCHAN PC NAME
u0_a145 10608 134 292960 26816 ffffffff 4019ca70 S com.ex.thre
u0_a146 10755 134 302420 32152 ffffffff 4019ca70 S com.ex.two

IPC(跨进程通信方式)

1. Bundle

通过Intent传递一个带有数据的Bundle来来实现跨进程通信,bundle的本质是一个parcelable对象

Intent intent = new Intent()
intent.putExtra()

2. 使用共享文件

比如A进程将数据存放在一个共有的目录下的文件中,然后让B进程去读,这样就实现了跨进程通信。但是两个进程共同去读写文件,会产生多进程安全问题,所以这种方式最好不要再并发量大的场景下使用。

还需要强调的一点是这个共享的文件不能是SharePrefernces文件,因为SP文件在内存中有一份缓存,所以多进程共同读写非常不可靠,容易造成数据丢失。

3.Messenger

Messenger封装了AIDL,可以让我们很容易的进行进程通信,但是使用Messenger一次只能处理一个请求即串行的处理,所以不用考虑多线程同步问题。

  1. 当不要求服务端响应时
    在这里插入图片描述
  2. 如果需要服务端向客户端的回复一些数据
    在这种场景下,需要让客户端也变为一个服务端,即在客户端用Handle新初始化一个Messenger用于处理服务端回复的数据。在发送请求时通过Message的replyTo字段将客户端这边的Messenger传递给服务端。
    在这里插入图片描述

4. AIDL

AIDL服务端
第一步 创建AIDL文件

要想使用AIDL,首先需要创建AIDL文件,AIDL分为两种,一种是声明AIDAIDL服务端提供给AIDL客户端的接口,我们将它成为接口AIDL文件,另一种是声明接口AIDL文件中使用到的Parcelable对象,即Parcelable对象声明文件

具体的AIDL文件规范如下:

  1. 并不是所有的类型AIDL文件都支持,AIDL文件只支持如下几种类型
类型 是否需要显示import
八大基本类型,比如int,float,long等 不需要
String或者CharSequence 不需要
List,只支持ArrayList,要求List中的每一个元素都是AIDL文件支持的类型 不需要
Map,只支持HashMap,要求Map中的每一个元素都是AIDL文件支持的类型 不需要
实现了Parcelable接口的对象 不但需要显示声明,还需要单独的AIDL文件使用parcelable关键字去声明这个对象,要求这个声明文件的全限定名和实际的Java类型保持一致
AIDL接口 需要显示import
  1. AIDL中除了基本数据类型和AIDL类型,一律需要在类型前声明 in, out, 或inout,不能一律的使用inout,因为这个在底层是由开销的
类型前声明 意义
in 表示数据只能由客户端流向服务端,服务端会获取到客户端完整的数据,但客户端不会同步服务端你对该对象的修改,不写的话,默认的 tag 就是 in
out 表示数据只能由服务端流向客户端,从服务端端接受该对象不为空,但字段内容为空,服务端修改对象后,binder 远程调用返回后,客户端会收到修改后的对象
inout 则表示数据可在服务端与客户端之间双向流通
  1. AIDL接口中不像传统接口,可以定义静态场景,它只能定义方法
第二步 写AIDL服务

写一个服务将AIDL文件生成的binder对象, 返回给绑定这个服务的客户端
上一步写的AIDL文件会在编译阶段,自动帮助我们生成一个含有binder的类
在这里插入图片描述
自己创建一个Service类将该binder对象返回给客户端即可
在这里插入图片描述

AIDL客户端

通过ServiceConnection绑定AIDL服务获得binder对象即可调用AIDL服务端的接口
在这里插入图片描述

AIDL安全问题

如果创建了一个AIDL服务,任何进程都可以bind并并且调用AIDL接口是很不安全的,所以要对连接AIDL接口的客户端做限制
常用的显示方式有两种

  1. 自定义权限
    在这里插入图片描述
    可以在onbind方法进行这种权限判断

  2. 判断binder的身份
    可以在AIDL文件生成的onTransact方法中通过getCallUid方法得知调用者的UID,再通过packageManager.getPackagesForUid()得到调用者的包名再加以判断

onway关键字

我们知道通过binder去跨进程通信,是会阻塞客户端当前线程的,如果在AIDL定义的接口中加了oneway关键字,那么该接口的调用将会变成异步的,不会阻塞当前线程。oneway关键字只适用于无返回值的接口
加了oneway关键字后AIDL文件与之前有什么不同
在这里插入图片描述
只是transact方法的flag从0 变为了 1

客户端给服务端注册回调无法注销

一般我们设置回调都会使用一个list去保存回调,注销时再将回调从list中删除,但是AIDL服务端收到的参数是反序列化过来的,所以注册的对象会想要注销的绝对不是一个对象,所以无法注销, 可用RemoteCallBackList解决这个问题
在这里插入图片描述
虽然不能以回调对象本身作为注销的依据,但是binder对象是同一个
还有就是虽然它看起来像个List但其实本质并不是,所以遍历时一定要注意要用以下的这种方式
在这里插入图片描述

监控AIDL服务端的消失

如果AIDL服务端被杀了,客户端有两种方式可以感应到

  1. 注册死亡通知
    通过IBinder对象注册
    在这里插入图片描述
    当服务端死亡时, binderDied会在binder线程池中被回调
  2. ServerConnetion的回调
    在这里插入图片描述
    当服务端死亡时, serviceConnection会在onServiceDisconnected方法会在主线程中回调
binder连接池

binder连接池是一种思想,并不像线程池一样有封装好的类,需要自己实现
binder连接池可以解决一个AIDL服务端进程写了十几个Service的问题
大致思想如下图所示
在这里插入图片描述

什么是binder

是Android中的一个类,实现了IBinder接口,是Android中的一种跨进程通信的一种方式
下面我们通过AIDL来通知binder是工作过程
首先我们要创建三个文件

  1. Book.java ------- aidl请求接口中用到的parcelable对象
  2. IBookInterface.aidl ------- aidl请求接口
  3. Book.aidl ------- 声明Book.java对象
    在这里插入图片描述

Book.java

package com.example.studyapplication;

import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
   
    private String name;
    private int price;

    protected Book(Parcel in) {
   
        name = in.readString();
        price = in.readInt();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
   
        @Override
        public Book createFromParcel(Parcel in) {
   
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
   
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
   
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
   
        dest.writeString(name);
        dest.writeInt(price);
    }
}

IBookInterface.aidl

// IBooklInterface.aidl
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值