Android IPC的几种方式

32 篇文章 0 订阅

IPC的概念

IPC即Inter-Process Communication(进程之间的通讯)。Android是基于Linux,而Linux出于安全的考虑,不同进程间不能之间操作对方的数据,这叫做“进程隔离”。

在Linux系统中,虚拟内存机制为每个进程分配了线性连续的内存空间,操作系统将这种虚拟内存空间映射到物理内存空间,每个进程有自己的虚拟内存空间,进而

不能操作其他进程的内存空间,只有操作系统才有权限操作物理内存空间。进程隔离保证了每个进程的内存安全,防止篡改其他进程的数据。

IPC方式

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

文件共享 简单易用 不适合高并发,并且不是实时的 当没有并发访问的前提下,交换简单的数据实时性不高的场景,可以使用,不推荐

AIDL功能方面非常强大,支持一对多,支持实时通讯,就是使用稍显复杂,需要处理好线程同步,一对多通信并且有RPC需求

Messager是底层是基于AIDL的封装,功能一般,AIDL有的它都有,但是不能进行高并发处理,不支持RPC数据是通过Message传输,因此也只能传输Bundle支持的数据类型。

少量并发的一对多即时通讯,无需RPC需求,或者无需返回结果的RPC需求。

ContentProvider 在数据访问方面功能比较优秀,支持一对多并发的数据共享,可通过Call方法扩展其他操作,可以理解为受约束的AIDL,主要提供数据的增删改查的操作,使用场景

一对多的数据共享

Socket 可以通过网络传输字节流,支持一对并发实时通讯,实现细节稍微有点繁琐,不支持直接的RPC,主要用于网络数据交换。

RPC概念

RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器上,想要通过B服务器上的应用提供的函数方法,由于不在一个内存空间,不能直接调用,需要通过网络

来表达调用的语义和传达调用的数据。

RPC会隐藏底层的通讯细节,不需要直接处理Socket通讯或http通讯。

RPC是一个请求响应的模型,客户端发起请求,服务端返回响应。

RPC在使用形式上像调用本地函数一样去调用远程的函数(或方法)

Binder是什么?

Binder是Android中的一个类,实现了IBinder接口。从IPC角度来说,Binder是Android中的一种扩进程通信方式。从Android应用层来说,Binder是客户端和服务端进行通信的媒介,

当bindService的时候,服务端会返回一个包含服务端业务调用的Binder对象。

Binder相较于传统的IPC来说更适合Android系统,具体原因包括以下几点:

Binder本身就是C/S架构的,这一点更符合Android的架构。

性能上更有优势:管道消息队列Socket的通讯都需要两次数据拷贝,而Binder只要一次。对于Android系统来说少一次数据拷贝,对整体的性能影响是非常之大的。

安全性更佳 传统的IPC形式,无法得到对方的身份标识(UID/GID),而在使用Binder IPC的时候,这些身份标识是跟随调用过程而自动传递的。Server端更容易就可以知道Client的身份,非常便于安全检查。

Linux传统跨进程IPC通信原理

刨除共享内存除外:

对于消息的发送端进程

通常传统的IPC通信(Socket管道消息队列)首先将消息发送方将要发送的数据存放在内存缓存区中

然后通过Linux系统调用进入内核态。然后操作系统为Linux发送方进程在内核空间分配内存,开辟一块内存缓存区,调用copy_from_user()函数将数据从用户空间的内存缓存区拷贝

到内核空间的内核缓存区中。

对于消息的接受端进程

首先在进行接受数据时,在自己的内存空间开辟一块内存缓存区

然后操作系统为Linux接受方进程在内核空间中调用copy_to_user()函数从内核缓存区拷贝到接收进程的内存缓存区。这样数据发送方进程和数据接收方进程就完成了一次数据传输。

缺点显而易见:

传统的IPC通信模型传输效率较低,拷贝次数过多,需要两次(这个仅是对Binder和匿名共享内存而言),第一次是从发送方用户空间拷贝到内核缓冲区,第二次是从内核缓冲区拷贝到

接收方用户空间。

接收数据的缓存区由数据接收进程提供,但是接收进程并不知道需要多大的空间来存放将要传递过来的数据,因此只能开辟尽可能大的内存空间或者先调用API获取消息体的大小,这两种做法是浪费空间或浪费时间。

binder IPC过程(该图来自https://blog.csdn.net/tkwxty/article/details/112325376,这个Binder的知识点讲的无敌了。)

这里Binder就数据传入效率和资源包占用角度来说,相关传统的IPC通信有如下的优点:

1.减少了数据的拷贝次数,用内存读写取代 I/O 读写,提高了文件读取效率

2.作为Binder服务端开辟的数据接收区大小是固定的(对于普通的BInder服务端和servicemanager端有一定的区别,都是都没有大于1M的空间)

示例,AlDL的系统生成的各个含义

//新建AIDL文件
RemoteService.aidl

interface IRemoteService{
    
    int getUserId();
    
}
/*    系统自动生成的
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.mystudyapplication3;
// Declare any non-default types here with import statements
//import com.example.mystudyapplication3.IUserBean;

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.mystudyapplication3.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.example.mystudyapplication3.IRemoteService";
//DESCRIPTOR Binder的唯一标识,一般当前的Binder的类名表示

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.mystudyapplication3.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.example.mystudyapplication3.IRemoteService asInterface(android.os.IBinder obj) {//将服务端的Binder对象成客户端所需的AIDL接口类型对象
            //这种转换的过程是区分进程的,如果位于同一进程,返回的就是Stub对象,否则返回
            //的是系统封装后的Stub.proxy对象
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.mystudyapplication3.IRemoteService))) {//判断是统一进程还是不同的进程
                return ((com.example.mystudyapplication3.IRemoteService) iin);
            }
            return new com.example.mystudyapplication3.IRemoteService.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {//用于返回当前的binder对象
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            //运行在服务端中的Binder线程池中,远程请求会通过系统底层封装后交由此方法来进行处理。
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getUserId: {
                    data.enforceInterface(descriptor);
                    int _result = this.getUserId();
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
private static class Proxy implements com.example.mystudyapplication3.IRemoteService {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {//用于返回当前的binder.proxy对象
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public int getUserId() 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);
                    mRemote.transact(Stub.TRANSACTION_getUserId, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_getUserId = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public int getUserId() throws android.os.RemoteException;
}

AIDL中有定向的定义:(建议看一遍这篇文章https://blog.csdn.net/qq3965470/article/details/52671887

分为in out inout 

in 数据只能由客户端流向服务器,服务器将会收到客户端对象的完整数据,客户端对象不会因为服务端对传参的修改而发生变动。

out 数据只能由服务端流向客户端,服务端将会收到客户端对象,该对象不能为空,但是它里面的字段为空,但是在服务端对该对象做任何改变之后客户端的传参对象都会同步改动。

inout 服务端将会接受客户端传来对象的完整信息,并且客户端将会同步服务器对该对象的任何改动。

AIDL简单使用

//在如下的包下创建RemoteService.aidl文件 
package com.example.mystudyapplication3;

interface IRemoteService {

    int getUserId();

}
//创建远程服务
public class RemoteService extends Service {

    private int mId = -1;

    private Binder binder = new IRemoteService.Stub() {

        @Override
        public int getUserId() throws RemoteException {
            return mId;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mId = 1256;
        return binder;
    }
}
<service
    android:name=".RemoteService"
    android:process=":aidl" />
清单文件中注册
//绑定远程服务
public class MainActivity extends AppCompatActivity {

    public static final String TAG = "test";

    IRemoteService iRemoteService;
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iRemoteService = IRemoteService.Stub.asInterface(service);
            try {
                Log.d(TAG, String.valueOf(iRemoteService.getUserId()));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iRemoteService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindService(new Intent(MainActivity.this, RemoteService.class), mConnection, Context.BIND_AUTO_CREATE);
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值