【IPC】Android中的进程间通信(IPC)详解

Dulcea ·
更新时间:2024-09-21
· 542 次阅读

1 需要的知识点 1.1 进程与线程

要理解跨进程通信,首先需要理解以下几个知识点1:

进程:按照操作系统的描述,进程是资源分配的最小单位,一个进程可以包含多个线程 线程:线程是CPU调度的最小单位,多线程需要考虑并发问题。 1.2 Android中的多进程

Android多进程指的是一个应用中存在多个进程的情况,在Android中,一般一个应用存在一个进程。多进程的情况2:

某个应用由于自身原因需要采用多进程模式实现 为了加大一个应用可使用的内存,通过多进程来获取多份内存空间

开启多进程模式是通过在AndroidManifest中指定Android:process属性

“:”开头的进程属于应用的私有进程,其它应用的组件不可以与它跑在同一个进程中,而不以冒号开头的数据全局进程,其它应用通过shareUID方式可以和它跑在同一个进程中。 shareUID:Android系统会为每个程序分配一个不同级别的UID,如果需要相互调用,只能是UID相同才行,这就使得共享数据具有了一定的安全性,每个软件之间是不能随意获得数据的,而同一个Application只有一个UID,所以Application之间不存在访问权限的问题。

一般来说,使用多进程会存在以下几个问题:

静态成员与单例模式完全失效 线程同步机制完全失效 SP(SharedPreference)可靠性会下降 Application创建多次 1.3 数据共享方法

如果你需要做一个Application将某些服务的ServiceProvider或者Activity等的数据共享出去,有如下三个办法:

完全暴露 :使用android:exported="true",一旦设置了true,则将会被完全暴露,可以被其它应用进程调用其暴露出去的数据。没有使用android:exportedServiceProvider或者Activity,其默认的exported值为false,但此时如果设置了intent filter,则其默认值为true. 权限提示暴露:如果应用A设置了android:permission="xxx.xxx.xxx",则你必须在Manifest中使用use-permission才能访问应用A的东西。 私有暴露:假如说一个公司做了两个产品,只想这两个产品之间可互相调用,那么这个时候就必须使用shareUserID将两个软件的Uid强制设置为一样的。这种情况下必须使用具有该公司签名的签名文档才能,如果使用一个系统自带软件的ShareUID,例如Contact,那么无须第三方签名。3 1.4 IPC基础概念介绍

SerialiazableParcelable

序列化:讲对象转化为字节的过程 Serialiazable:Java提供的序列化接口 Parcelable:Android提供的序列化接口

SerialiazableParcelable的区别Serialiazable使用简单但是需要大量I/O操作,Parcelable使用较繁琐,主要用于内存序列化,效率高。

Binder
详见【IPC】Binder跨进程通信机制原理

2 实现IPC方式

实现IPC方式可以分为以下几种方式4:

使用Bundle 使用文件共享 使用SharedPreferences 使用Messenger 使用AIDL 使用ContentProvider 使用Binder连接池 Broadcast Socket 管道 2.1 使用Messenger

Messenger被称为信使,常与Message一起使用实现跨进程通信。底层只是对Binder的简单包装

步骤 创建一个Service,在Service中的onBind里返回一个IBinder对象。 在AndroidManifest中声明服务,并将该服务放在另外的一个进程中 通过bindService绑定服务 在客户端创建ServiceConnection对象,使用服务端返回的IBinder对象创建一个Messenger,该Messenger是服务端的Messenger,可以通过该Messenger将客户端数据发送到服务端(该方法是通过客户端向服务端传递数据,如果想从服务端向客户端传递数据,可以通过在客户端创建一个Handler对象,然后通过该对象创建一个客户端Messenger,然后通过message.replyTo5将客户端的Messenger发送到服务端,然后在服务端获取客户端创建的Messenger,然后通过Messenger将数据发送给客户端,这样就实现了服务端向客户端传递数据) 通过mMessenger.send(message)来将数据发送给服务端,从而实现跨进程通信。 代码实现
创建一个Service,在Service中的onBind里返回一个IBinder对象。 /** * Created by yds * on 2020/4/13. */ public class MessengerService extends Service { //服务端自己的信使,为了获取客户端数据 private Messenger mMessenger; //客户端信使对象,为了将数据发送到客户端 private Messenger cMessenger; @Override public IBinder onBind(Intent intent) { mMessenger = new Messenger(new MyHandler()); System.out.println("MessengerService onBind"); return mMessenger.getBinder(); } class MyHandler extends Handler{ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case 0: Message message = Message.obtain(null,1); message.arg1 = 1000; //获取客户端传递来的信使 cMessenger = msg.replyTo; try { //通过客户端信使发送服务端数据到客户端 cMessenger.send(message); } catch (RemoteException e) { e.printStackTrace(); } System.out.println("MessengerService handleMessage"); break; } } } }

AndroidManifest中声明服务,并将该服务放在另外的一个进程中

使用Messenger实现客户端和服务端双向通信

public class MainActivity extends AppCompatActivity { //服务端信使,为了发送数据到客户端 private Messenger mMessenger; //客户端自己的信使,为了获取服务端数据 private Messenger cMessenger; private static class MyHandler extends Handler { @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: System.out.println("Service Data: " + msg.arg1); break; } } } private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mMessenger = new Messenger(service); cMessenger = new Messenger(new MyHandler()); if (mMessenger != null) { Message msg = Message.obtain(null, 0); //将客户端自己的信使放在Message里传递到服务端 msg.replyTo = cMessenger; try { mMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected(ComponentName name) { mMessenger = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, MessengerService.class); bindService(intent, mServiceConnection, BIND_AUTO_CREATE); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mServiceConnection); } }

运行截图:
在这里插入图片描述

2.2 使用Bundle

Bundlefinal类型,不可被继承,它实现了Parcelable接口,是一个特殊的Map类型,支持进程间通信。
使用Bundle进行进程间通信,其实还是通过Messenger+Message来实现的,核心代码如下:

mMessenger = new Messenger(service); Message msg = Message.obtain(null, 0); Bundle bundle = new Bundle(); bundle.putString("IPC", "Bundle"); bundle.putInt("Number", 20); msg.setData(bundle); try { mMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } 2.3 使用SharedPreferences

SharedPreferences并不适合存储大量数据和频繁改变数据,只能用于轻量的存储,高并发读/写时有可能会丢失数据。SharedPreferences存在以下性能问题6:

跨进程不安全。由于没有使用跨进程的锁,就算使用 MODE_MULTI_PROCESSSharedPreferences 在跨进程频繁读写有可能导致数据全部丢失。根据线上统计,SharedPreferences 大约会有万分之一的损坏率。 加载缓慢SharedPreferences 文件的加载使用了异步线程,而且加载线程并没有设置优先级,如果这个时候读取数据就需要等待文件加载线程的结束。这就导致主线程等待低优先线程锁的问题,比如一个 100KBSP 文件读取等待时间大约需要 50 ~ 100ms,并且建议大家提前用预加载启动过程用到的 SP 文件。 全量写入。无论是commit() 还是 apply(),即使我们只改动其中一个条目,都会把整个内容全部写到文件。而且即使我们多次写同一个文件,SP 也没有将多次修改合并为一次,这也是性能差的重要原因之一。 卡顿。由于提供了异步落盘(拷贝到磁盘)的apply机制,在崩溃或者其它一些异常情况可能会导致数据丢失。所以当应用收到系统广播,或者被调用 onPause 等一些时机,系统会强制把所有的 SharedPreferences对象的数据落地到磁盘。如果没有落地完成,这时候主线程会被一直阻塞。这样非常容易造成卡顿,甚至是ANR,从线上数据来看 SP卡顿占比一般会超过 5%

由于以上原因,不建议使用SharedPreferences进行跨进程通信,特别是SharedPreferences进行跨进程通信不安全。

2.4 使用文件共享

利用多进程同时读写同个外部文件达到是数据交互的目的,存储形式没有限制:xml,文本,对象序列化等等。但其有着明显的缺点,由于Linux系统对文件并发读写没有限制,会导致数据不同步问题,所以该方式只适合于对数据同步要求不高的进程间通信

2.5 使用AIDL

为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。很多IPC通信方式都是基于AIDL的。如ServiceContentProvider

AIDL的简单使用可见:AIDL简单使用

待续 参考文章

带你了解android的IPC机制 ↩︎

Android开发之android中的多进程模式 ↩︎

Android中UID机制和共享进程 ↩︎

Android夸进程通信机制四:使用 Bundle进行进程间通信 ↩︎

Messenger:使用消息的跨进程通信 (Message.replyTo()的使用) ↩︎

Android之SharedPreferences简介及使用说明 ↩︎


作者:It一zhai男



进程 ipc 进程间通信 通信 Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号