最近在项目中再次用到了多进程相关的内容,这里结合最近看到的一些资料和个人积累对android的多进程这块进行一个总结。由于android多进程的内容比较多,这里分成几篇文章来讲解:
Android中的多进程
关于进程与线程
在android中,一般情况下,一个应用对应一个进程。这个应用内部可以创建自己的子线程用来执行网络加载、数据处理等耗时操作,可以用UI线程来控制界面的交互、动画的执行等。
线程是进程的子集,一个进程可以包含多个线程,进程用来管理各个线程。从CPU角度来说,进程和线程不是同一个层面上的概念,线程主抓CPU执行代码的过程,其余的资源的保护和管理由整个进程去完成。
什么时候使用多进程
在Android中,系统为每个应用分配的内存空间是有限的,比如在一些早期的机型中是16M,现在的手机分配的内存更大甚至可以达到512M,但是分配的内存总是有限的。
当应用使用的内存空间不足时,会发生OOM。所以对于一些要占用大内存的应用(比如图片处理,视屏处理)就需要多进程来进行处理。
在新起的进程中,我们可以处理一些看不见的服务、比较独立而又相当占用内存的功能,用来分担一些内存消耗。
多进程还可以应用在应用保活方面,当一个进程被杀后,另一个进程来拉起。比如现在的各类流氓软件等。虽然不推荐这样做,但在产品有这个强制需求时候,还是可以考虑用这个方法的。
如何开启多进程
在一个应用中开启多进程的方式十分简单,只需要在xml文件中配置对应的属性即可
<activity android:name=".UIActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".OtherActivity" android:process=":remote"/>
其中process的命名规则如下
如果和上面代码一样,用”:”开头,那么这个进程是个私有进程,在这个应用创建时候创建,进程名对应的是:应用包名+:remote。这个进程只能被当前应用访问到。
如果是以小写字母开头,那么当前进程是一个就是一个全局的进程,可以和其他应用共享资源,前提是这个应用和当前应用的ShareUID相同,并且具有相同的应用签名
使用多进程的一些注意事项
虽然开启多进程的方式十分简便,但是在使用过程中还是有很多坑的。常见的问题包括:静态成员变量和单例模式完全失效、线程同步机制失效、SharedPreferences会有偶现bug、Application会被多次创建。
前三个问题可以通过避免使用该用IPC对应的方法来避免。而对于Application重复创建,可以通过条件判断来避免重复初始化。比如在代码中获取当前进程名,只有在UI线程中才进行必要的初始化。
产生以上问题的主要原因是:当启动一个新进程时候,系统会为当前进程重新生成一套对应的类和内存地址,所以访问同一个类时,得到的内容是不一样的。每个类也就会生成两次。
android中的IPC方式
首先说说什么是IPC,IPC是Inter-Process Communication的缩写,翻译为进程间通讯。指的是两个进程间进行数据交换。android中常见的IPC方式有:
Intent+Bundle方式
Activity、Service、Receiver都是支持Intent传值的,即使他们处于不同的进程中也可以进行值传递。所以可以通过给Bundle附件我们需要传输给进程的值,来通过Intent来发送。
平时我们使用最多的就是startActivity这个方法了。由于这个方法太过常用,所以人们经常忽略这个进程间通讯的方法。不过也是由于这个结构过于简单,所以对于较复杂的交互,使用起来不方便。
使用文件共享的方式
当数据交互的频率不是很高时候,可以考虑使用这种方式,在一个进程中把对应的数据序列化到本地文件中,在另一应用中对这个文件进行读取并使用。这个对简单的线性任务的支持性还是不错的。
不过这种通讯方式在高并发的操作环境下很不有好,因为我们读取的文件可能正在被修改中,数据获取可能不及时,甚至出现异常。所以文件共享方式只适合对数据同步要求不高的进程之间的通讯。
使用剪切板
这个使用场景大家都见过,比如淘宝通过识别淘口令来寻找商品、复制网址后打开UC会有是否跳转的提示等。具体的实现方式无非是在应用启动时候,获取剪切板中最新内容,根据自己的规则来进行一些验证。
这种使用方法局限性更多,因为剪切板的数据是不可靠的,任何应用都可能对其中的数据进行更改。所以这个通讯方式一般只用来增加用户体验或者其他进程间通讯都不可用(如淘宝)的情况。
使用Messenger
Messenger,中文翻译是信使。通过他可以再不同的进程间传递Message对象。在message中放入对应的数据就可以实现进程间的数据传递了。它的底层实现是AIDL,是一种轻量级的IPC方案。
Messanger是通过串行的方式来处理消息的,如果有大量的消息同事传递。那么只能一个个的处理。此时使用Messanger会存在处理时间可能过长的问题。而且Messanger主要用来在进程中传递消息,如果要进行跨进程的方法调用,就不太合适了。要处理这些,就需要使用AILDL了。
使用AIDL
AIDL,全称是Android Interface Definition Language,也就是Android接口定义语言。所以AIDL也有自己对应的语法和对应的文件。对应文件的后缀是.aidl。具体AIDL的详细介绍可以查看我的这篇博客。这里我们只是简答介绍一下。
AIDL的功能是很强大的,可以处理各类型的进程间通讯,支持一对多的并发通讯,支持实时通讯。一般在多进程的开发中出现的频率也比较高,是有并发需求并且功能较复杂的应用开发的最佳选择。
不过AIDL使用起来稍微显复杂。需要经过一定的熟悉以后才能上手使用,还要处理好线程同步等疑难问题。
使用Socket
socket虽然在大多数情况中,用于网络通讯相关的内容,但是在本地进程间通讯也可以使用,关于socket的具体使用可以参考这篇博客,这里只是把它列出来。