文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

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

2022-06-06 13:55

关注

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
将某些服务的
Service
Provider
或者
Activity
等的数据共享出去,有如下三个办法:

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

Serialiazable
Parcelable

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

Serialiazable
Parcelable
的区别
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.replyTo
5将客户端的
Messenger
发送到服务端,然后在服务端获取客户端创建的
Messenger
,然后通过Messenger将数据发送给客户端,这样就实现了服务端向客户端传递数据) 通过
mMessenger.send(message)
来将数据发送给服务端,从而实现跨进程通信。 代码实现
创建一个
Service
,在
Service
中的
onBind
里返回一个
IBinder
对象。

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

Bundle
final
类型,不可被继承,它实现了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_PROCESS
SharedPreferences
在跨进程频繁读写有可能导致数据全部丢失。根据线上统计,
SharedPreferences
大约会有万分之一的损坏率。 加载缓慢
SharedPreferences
文件的加载使用了异步线程,而且加载线程并没有设置优先级,如果这个时候读取数据就需要等待文件加载线程的结束。这就导致主线程等待低优先线程锁的问题,比如一个
100KB
SP
文件读取等待时间大约需要
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
的。如
Service
ContentProvider

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

待续 参考文章

带你了解android的IPC机制 ↩︎

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

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

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

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

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


作者:It一zhai男


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯