关于Android进程的那些事儿

作者: b923228cc7b5 | 来源:发表于2017-02-28 15:41 被阅读224次

    最近项目中需要接入一个SDK,这个SDK会开启一个新进程,但是它自身的初始化要在app的主进程中进行。由于对进程的理解不够,导致我在此坑中挣扎了整整一个礼拜,痛定思痛,终于下定决心研究一下进程。


    1. 什么是进程

    进程占有一定的资源空间,内部资源共享,一个进程至少包含一个线程。但是线程没有独立的内存空间,而是在它所在的进程中资源共享。也正是这个原因,有时候我们需要考虑到线程安全的问题,避免在不同线程中对同一个资源文件进行操作导致的冲突。

    在安卓中,一个应用启动时会新建一个进程,该进程由zgoate进程fork出来,具体过程参照Android的系统启动流程和应用启动流程这篇文章。


    2.进程与线程的区别

    线程(Thread)、进程(Process)这俩货中文翻译都有一个“程”字,但是不代表二者有很多的共同点。我举一个例子,大家就明白了。

    工厂=CPU

    如果把CPU比作一座工厂,内存比作工厂的占地面积,那么,进程就是工厂的车间,线程就是车间里的工人。那么,他们之间的对应关系如下:

    • 多个车间可以同时运行,但是受限于工厂的占地面积:代表CPU可以实现多进程,但是受限于内存容量的大小。
    • 单个车间面积有限制:Android下单个进程所占内存大小的限制。
    • 车间与车间之间需要通过卡车来运输货物:多进程通信的载体Binder.
    • 一个车间至少有一个工人:一个进程中至少包含一个线程。
    • 车间里的空间是共享的,每个工人都可以使用这些空间:进程的内存空间是共享的,每个线程都可以使用这些内存。
    • 车间的卫生间每次只能一个人进去,其他人也想挤进去,就会吵架:当线程使用某些内存时,其他线程必须等它结束才能使用这块内存,否则会线程不安全。
    • 上卫生间的人把门锁上可以避免其他工人进入:加互斥锁可以避免多个线程同时读写某块内存区域

    详细内容参照这篇文章http://blog.csdn.net/yapian8/article/details/41015631


    3. 进程分类

    按优先级由高到低分为5类:

    进程优先级金字塔

    3.1 前台进程:

    用户当前操作所必需的进程。

    前台进程一般是visble(可见的)但要注意,visible不一定是active的。比如activity弹出一个dialog,那么activity可见但是inactive,dialog变成active。

    3.2 可见进程:

    没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。
    比如弹出dialog后面的activity。

    3.3 服务进程:

    正在运行的Service。

    由于运行服务的进程其级别高于托管后台 Activity 的进程,因此启动长时间运行操作的 Activity 最好为该操作启动服务,而不是简单地创建工作线程,当操作有可能比 Activity 更加持久时尤要如此。否则,后台的Activity被销毁时,工作线程也会被销毁,导致后台操作的失败。

    例如,正在将图片上传到网站的 Activity 应该启动服务来执行上传,这样一来,即使用户退出 Activity,仍可在后台继续执行上传操作。使用服务可以保证,无论 Activity 发生什么情况,该操作至少具备“服务进程”优先级。 同理,广播接收器也应使用服务,而不是简单地将耗时冗长的操作放入线程中。

    3.4 后台进程:

    已经调用onStop方法的activity。正如3.3中所提到的,避免因为后台进程的销毁导致后台任务的中断。

    3.5 空进程:

    不包含任何应用组件的进程

    总之,时刻注意四大组件的生命周期,避免因为GC(Garbage Collection)问题导致关键性的操作失败。


    4. 为什么需要多进程

    一个应用默认只有一个进程,进程名称就是包名,每个进程都有自己独立的资源和内存空间,系统给每个进程分配的内存大小是有限制的,当进程占用内存超过限制时,就会报OOM的错误。

    OOM的问题的解决方法:

    • 代码优化
      比如图片处理视频播放等采用高效的内存占用少的框架
    • 向系统申请更多内存
      在AndroidManifest.xml中的application标签下添加android:largeHeap=”true”。
    • 多进程
      开启新进程的方法很简单,在四大组件的标签下添加android:process属性即可。为了便于调试,可以调试时将process属性去掉,调试完再恢复即可。

    5. 进程间通信(IPC: InterProcess Communication)

    因为每个进程都有自己独立的资源和内存空间,所以堆栈信息,文件操作都是独立的,这就需要用到IPC。IPC实现方式通常有如下几种:

    5.1 Activity.

    通过指定Activity的action,访问其他进程的Activity,通过Intent或者Uri来传递数据。

    5.2 ContentProvider

    多个应用程序之间使用文件或者Sqlite共享数据,利用ContentProvider实现对数据的增删改查。

    5.3 BroadCast

    类似Activity,通过Intent传递数据,

    5.4 Service

    Service有两个作用:后台运行和跨进程通信。其中跨进程通信是通过AIDL(Android Interface Definition Language)服务实现的。

    上述四种方式分别对应Android四大组件,一张图表示


    Activity,Service,BroadCast Receiver,ContentProvider实现IPC

    5.5 Messenger

    当您需要执行 IPC 时,为您的接口使用 Messenger要比使用 AIDL 实现它更加简单,因为 Messenger会将所有服务调用排入队列,而纯粹的 AIDL 接口会同时向服务发送多个请求,服务随后必须应对多线程处理。

    对于大多数应用,服务不需要执行多线程处理,因此使用Messenger 可让服务一次处理一个调用。如果您的服务必须执行多线程处理,则应使用 AIDL 来定义接口。

    Messenger实现IPC

    限于篇幅,IPC实现方式的具体步骤和原理不再展开,有空再写一篇关于进程间通信的文章。当然如果嫌麻烦,可以使用别人封装好的框架,比如EventBus之类。


    另外,最后两张图的来源自下面这个帅哥,有兴趣可以关注他,https://github.com/yinshijian-kkb/stay4android/blob/master/Binder%E6%A1%86%E6%9E%B6%E8%A7%A3%E6%9E%90.md

    Aleksandar Gargenta

    相关文章

      网友评论

        本文标题:关于Android进程的那些事儿

        本文链接:https://www.haomeiwen.com/subject/odozbttx.html