Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html

在Android中进程间通信是比较难的一部分,同时又非常重要,针对进程间通信,博主会用四篇文章来介绍,本篇文章为IPC系列的开篇,主要介绍一些IPC中用到的一些概念、基础等,目的是让读者朋友们在学习IPC之前对一些必要的知识有一个大体的把握。在Android中进程间通讯的方式有很多种,在后续的三篇中会分别介绍每一种方式的实现过程已经各自的优缺点。

IPC是什么?

IPC(全称:Inter-Process Communication)为进程间通讯,指至少两个进程间传递数据或信号的一些技术活方法。

注:进程间通讯是至少两个进程之间发生的事情,我们通常习惯性的会把一方称为客户端,一方称为服务端,在后续的文章也会多次出现客户端和服务端,没接触过进程间通信的童鞋可能一开始会不太习惯,这里要注意一下。

为什么要使用IPC?

无论是在计算机系统还是Android系统中每个进程都有自己一部分独立的系统资源,彼此是隔离的,为了能是不同的进程互相访问资源并协同工作,就需要用到进程间通讯。

RPC是什么?

RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。(-来自百度百科)

在后面介绍AIDL时会用到RPC的概念,在这里简要说明一下RPC在Android的进程间通讯所扮演的角色,以博主本人的理解,简单来说RPC机制就是指在本地即可调用远程进程中的方法,而不需要关心其底层实现。

在Android中IPC有哪几种实现方式?

  • Bundle
  • 文件共享
  • ContentProvider
  • Messager
  • AIDL
  • Socket

如何开启一个进程

在四大组件的AndroidManifest配置中配置process属性

比如这个:

<service
    android:name=".messager.MessengerService"
    android:exported="true"
    android:process=":remote" />

“:”开头和不带“:”的有什么区别:

“:”开头的进程属于当前应用的私有进程,其他应用的组件不能和它跑在同一进程下。 不带“:”的进程属于全局进程,其他应用可以通过ShareUID和它跑在同一进程下。

Android系统会为每一个应用分配一个UID,具有相同的UID才能共享数据。 通过ShareUID跑在同一进程中需要两个应用有相同的ShareUID并且有相同的签名才可以。

Android系统为每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致不同的虚拟机访问同一个类的对象会产生多个副本。

使用多进程会导致如下问题:

  1. 静态变量和单例失效
  2. 线程同步机制失效
  3. SharePreference可靠性下降
  4. Application多次创建

IPC中涉及到的基础概念

  • Serializable
  • Parcelable
  • Binder

Serializable

使用Serializable进行序列化很简单,只需要实现Serializable接口,然后为类指定一个serialVersionUID即可。

Serializable中的serialVersionUID工作机制:

  1. 序列化时系统会把当前类的serialVersionUID写入序列化的文件中(或其他中介)
  2. 反序列化时系统去检测文件中的serialVersionUID,对比是否和当前类的seralVersionUID一致。
  3. 一致就说明序列化的类的版本和当前类的版本是相同的,可以成功反序列化,否则就说明当前类和序列化的类相比发生了某些转换,就会报错(java.io.InvalidClassException)
  • 静态变量属于类不属于对象,不参与序列化过程
  • 用transient关键字标记的成员变量不参与序列化过程

Parcelable

使用Parcelable进行序列化比Serializable要麻烦一些,需要实现Parcelable接口,并实现一些必要方法,其通常形式如下:

public class Contact implements Parcelable {
    public int phoneNumber;
    public String name;
    public String address;

    public Contact(int phoneNumber, String name, String address) {
        this.phoneNumber = phoneNumber;
        this.name = name;
        this.address = address;
    }

    public Contact() {
    }

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

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

    public void readFromParcel(Parcel parcel) {
        phoneNumber = parcel.readInt();
        name = parcel.readString();
        address = parcel.readString();
    }

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

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


    public Contact(Parcel parcel) {
        phoneNumber = parcel.readInt();
        name = parcel.readString();
        address = parcel.readString();
    }
}

一个类只要实现了Parcelable接口,其对象就可以实现序列化并可以通过Intent和Binder传递。

  • Parcelable中的Parcel内部包含了可序列化的数据,可以在Binder中自由传输。
  • 序列化功能:writeToParcel实现,最终是通过Parcel中的一系列write方法完成。
  • 反序列化:CREATOR完成,通过Parcel的一系列read方法来完成,内部表明了如何创建序列化对象和数组。
  • 内容描述:describeContents:仅当当前对象中存在文件描述符时返回1,其余所有情况返回0。
  • 反序列化过程需要传递当前线程的上下文类加载器,否则会报找不到类的错误。

Serializable和Parcelable的区别:

  • Serializable是java中的序列化接口,使用简单,但开销很大,序列化和反序列化过程需要大量IO操作。
  • Parcelable是Android中的接口,使用麻烦,但效率高,首选。
  • Parcelable主要适用于内存序列化上,但通过Parcelable将对象序列化到设备中或序列化后通过网络传输也可以,但稍微复杂,建议这种情况用Serializable。

Binder的使用及上层原理

  • Binder是Android中的一个类,实现了IBinder接口
  • 从IPC角度来说,Binder是一种跨进程通讯方式
  • 从android framework角度来说,Binder是ServiceManager链接各种Manager(ActvitiyManager、WindowManager等等)和相应ManagerService的桥梁;
  • 从android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService时,服务端会返回一个包含了服务端业务调用的Binder对象

AIDL中自动生成的Binder接口类的一些方法:

  1. DESCRIPTOR:Binder的唯一标识,一般用类名
  2. asInterface(IBinder obj):用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象。(如果客户端和服务端位于同一进程,此方法返回的就是服务端的Stub对象本身,否则返回Stub.proxy)
  3. asBinder:返回当前的Binder对象
  4. onTransact:运行在服务端的Binder线程池中

Binder运行在服务端进程,如果服务端进程被异常终止,Binder链接就会断裂,导致我们远程调用失败。但是此时我们并不知道Binder链接已经中断,为了解决这个问题,Binder中提供了两个配对的方法:

  • linkToDeath:通过它可以给Binder设置一个死亡代理,当Binder死亡后我们就会收到通知。

如何设置Binder死亡代理:

首先声明一个DeathRecipeint对象,然后通过binder.linkToDeath()方法将其绑定到binder上。在DeathRecipeint内部有一个方法binderDied,当Binder死亡后,系统就会回调binderDied方法。

示例代码如下:

private ServiceConnection serviceConnection = new ServiceConnection(){
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mIContactsManager = IContactsManager.Stub.asInterface(service);
            Log.i(TAG, "onServiceConnected: mIContactsManager=" + mIContactsManager);
            try {
                // 给service设置死亡代理
                service.linkToDeath(mDeathRecipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        ...
};

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            // 当binder挂掉后就会执行此方法
            if (mIContactsManager == null) {
                return;
            }
            // 首先移除之前绑定的死亡代理
            mIContactsManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
            mIContactsManager = null;

            // 然后重新绑定远程服务
            bindService(intent, serviceConnection, BIND_AUTO_CREATE);
        }
};

进程间通讯的基础知识就先介绍到这里,接下来将开始针对每种进程间通讯方式作出详细的介绍。


最后想说的是,本系列文章为博主对Android知识进行再次梳理,查缺补漏的学习过程,一方面是对自己遗忘的东西加以复习重新掌握,另一方面相信在重新学习的过程中定会有巨大的新收获,如果你也有跟我同样的想法,不妨关注我一起学习,互相探讨,共同进步!

参考文献:

  • 《Android开发艺术探索》

源码地址:本系列文章所对应的全部源码已同步至github,感兴趣的同学可以下载查看,结合代码看文章会更好。源码传送门

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云原生架构实践

Jhipster技术栈定制 - 基于UAA的微服务之间安全调用

3个微服务都是通过Jhipster生成。 工程代码生成完之后,根据上一节启动的组件的实际情况,修改微服务配置文件中Eureka和database相关的配置。

9703
来自专栏向治洪

Android NDk环境配置

概论 NDK全称是Native Development Kit,NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应...

19310
来自专栏CodingBlock

Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html 在Android...

2896
来自专栏CodingBlock

Android查缺补漏(IPC篇)-- 进程间通讯基础知识热身

本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8479282.html

1004
来自专栏一个会写诗的程序员的博客

《Kotin 极简教程》第11章 使用Kotlin 集成 SpringBoot开发Web服务端第11章 使用Kotlin集成SpringBoot开发Web服务端《Kotlin极简教程》正式上架:

我们在前面第2章 “ 2.3 Web RESTFul HelloWorld ” 一节中,已经介绍了使用 Kotlin 结合 SpringBoot 开发一个RES...

811
来自专栏后端云

部署实时OpenStack实例

最近的OpenStack nova版本增加了对实时实例的支持,即提供实时应用所需的确定性和性能保证的实例。这项工作在OpenStack Ocata发行版中最终标...

3163
来自专栏Java3y

Druid数据库连接池就是这么简单

前言 本章节主要讲解Druid数据库连接池,为什么要学Druid数据库连接池呢?? 我的知识储备数据库连接池有两种->C3P0,DBCP,可是现在看起来并不够用...

48111
来自专栏battcn

一起来学SpringBoot | 第二十五篇:打造属于你的聊天室(WebSocket)

WebSocket 是 HTML5 新增的一种在单个 TCP 连接上进行全双工通讯的协议,与 HTTP 协议没有太大关系....

1982
来自专栏闻道于事

Spring boot集成MybatisPlus

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。(摘自myba...

872
来自专栏编程

Spring in Action 要点总结

一、Spring基础 1. Environment 环境抽象:profiles 和 properties. 2. SpEL Spring EL 表达式 3. 设...

1895

扫码关注云+社区

领取腾讯云代金券