《Android开发艺术探索》读书笔记----第二章:Android IPC 简介、多进程模式

Android IPC 简介

  • 定义
    IPC 是 Inter-Process Communication 的缩写,含义为进程间通信或者跨进程通信,是指两个进程之间交换数据的过程。
  • 进程和线程
    根据操作系统的描述,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此,进程和线程是包含与被包含的关系。在Android里面,主线程也叫UI线程,在UI线程里面才能操作界面元素。
  • ANR异常(Application Not Responding)
    如果一个进程要执行大量的耗时任务,如果把这些任务放在主线程中去执行就很容易造成界面无法响应,即应用程序无响应,ANR异常。

IPC 不是 Android 所特有的,任何一个操作系统都需要有响应的 IPC 机制,比如 Windows 上可以通过剪贴板、管道、邮槽等来进行进程间通信。Linux 上可以通过命名管道、共享内存、信号量等来进行进程间通信。不同的操作系统有不同的进程间通信的方式,对于 Android 来说,它是一种基于 Linux 内核的移动操作系统,也有自己的进程间通信的方式。在 Android 中最有特色的进程间通信方式就是 Binder 了,通过 Binder 可以轻松的实现进程间通信,还有 Socket ,通过 Socket 也可以实现任意两个终端之间的通信,当然,同一个设备上的两个进程也可以通过 Socket 通信。

  • 多进程的情况分为两种:
    1.一个应用因为某些原因需要采用多进程模式来实现,如某些模块由于特殊原因需要运行在单独的进程中、为了加大一个应用的内存所以通过多进程来获取多份内存空间。Android 对单个应用可使用的最大内存空间做了限制,早期可能是16MB,不同设备情况不同。
    2.当前应用要向其他应用获取数据,由于是两个应用所以必须采用多进程间通信,使用系统的 ContentProvider 去查询数据时其实也是一中进程间通信。

Android 中的多进程模式

通过给 Android 的四大组件设置 android:process 属性,我们可以轻易地开启多进程模式,但是使用过程中往往会出现许多未知的错误。

  • 开启多进程模式
    Android 中的多进程是指一个应用中多个进程的情况,因此这里不讨论两个应用之间的多进程情况。
    在Android 中使用多进程只有一种方法,那就是给四大组件在 Menifest文件中指定 process 属性,我们无法给一个线程或者实体类指定其运行时所在的进程。另一种非常规的方法:通过JNI在 native 层去 fork 一个新的进程,属于特殊情况。
<activity  android:name=".SecondActivity" android:process="com.gechao.test.one" />
 <activity  android:name=".ThirdActivity" android:process=":one" />

当着两个 Activity 启动时,系统会分别为它们创建一个进程,加上一个默认的进程,就有三个进程,默认的进程名是包名,可以在DDMS中查看进程信息。

上边指定进程时出现了两种写法:

  • 第一种是完整的写法,com.gechao.test.one
  • 第二种是省略的写法,结果为:com.gechao.test:one 即 在:前边加上完整包名
  • 区别
    以 : 开头的进程属于当前应用的私有进程,其他应用和组件不可以和它跑在同一个进程中
    而第一种写法的进程属于全局进程,其他应用可以通过ShareUID 方式和它跑在同一个进程中。

多进程模式的运行机制

当应用开启多进程之后会出现很多奇怪的现象。
假如我们添加一个 Activity 为 SecondActivity,指定一个新的进程,同时新建一个类,里面存放有一个 public 的静态变量,

public class Manager {
    public static int Uid = 1;
}
  • 然后在MainActivity 的 onCreate方法中修改 Uid的值为2,然后启动 SecondActivity,打印 Uid的值,会发现 Uid的值还是1,按照正常逻辑,静态变量是在所有地方共享的,并且每一处的修改都会同步,正常的理解,Uid的值应该已经被改为2。
  • 此问题出现的原因是 SecondActivity 运行在一个单独的进程中,我们知道,Android 为每一个应用分配一个独立的虚拟机,或者说为每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址,这导致在不同的虚拟机中访问同一个类的对象会产生多份副本,拿本例来说,有一个默认的进程,即进程名为包名,SecondActivity 有一个单独的进程,两个进程都存在一个 Manager 类,并且这两个类互不干扰,在同一个进程中修改 Uid的值只会影响当前进程,不会对其他进程造成影响。

所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会失败,这也是多进程带来的主要影响。正常情况下,四大组件不可能不通过一些中间层来共享数据,那么通过简单的指定进程名都会无法正常运行。当然,某些特殊情况下,某些组件之间不需要共享数据,可以直接通过在 Menifest 中指定进程名来实现多进程,但是这种场景不常见。

一般来说,使用多进程会带来以下的问题:

  • 静态成员和单例模式完全失效。
  • 线程同步机制完全失效。
  • SharePreferences 的可靠性下降。
  • Application会多次创建。

第一个问题已经分析完毕,第二个问题其实和第一个是类似的,既然不是一块内存了,那么不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象。第三个是因为 SharePreferences 不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失,因为 SharePreferences 底层是通过读写XML文件实现的,并发写显然是容易出现问题的,甚至并发读/写都会出现问题。第四个问题:当一个组件跑在一个新的进程中的时候,由于系统要在创建新的进程的同时分配独立的虚拟机,所以这个过程 其实就是启动一个应用的过程,因此系统又把这个应用重新启动一遍,当然会自动创建新的 Application 。运行在同一个进程中的组件属于同一个虚拟机和同一个 Application 的,在不同进程中的组件是属于不同的 虚拟机和 Application 的,可以在 Application 的onCreate方法中做测试。

这里我们分析了多进程带来的问题,但是我们不能因为多进程有很多问题就不去使用,为了解决这个问题,系统提供了很多跨进程通信的方法,虽然不能直接共享内存,但是跨进程通信我们还是可以实现数据交互,实现跨进程通信的方式有很多,接下来会一一介绍。

你可能感兴趣的:(android,线程,ipc,多进程,运行机制)