【转】安卓多进程与进程间通信

1、安卓单进程的概念
        一般情况下,一个应用程序就是一个进程,这个进程名称就是应用程序包名。我们知道进程是系统分配资源和调度的基本单位,所以每个进程都有自己独立的资源和内存空间,别的进程是不能任意访问其他进程的内存和资源的。

2、单进程存在的问题
        每个进程所能使用的资源是有限,特别是内存,安卓系统对用户进程有严格的内存要求,超过此内存限制时,应用将OOM和崩溃。

        很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题。为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如大图片的处理、子进程记录运行轨迹等。

3、怎样使用多进程
        Android多进程创建很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可。命名之后,就成了一个单独的进程。

process分私有进程和全局进程:

3.1、私有进程的名称前面有冒号,例如:

<service
            android:name="con.tt.business.record.RecordService"
            android:process="con.tt.business.record.RecordService:record"/>


3.2、全局进程的名称前面没有冒号,例如:

<service
            android:name="con.tt.business.record.RecordService"
            android:process="con.tt.business.record.RecordService.record"/>


     为了节省系统内存,在退出该Activity的时候可以将其杀掉(如果没有人为的调用代码杀掉该进程,在程序完全退出时该进程会被系统杀掉)。 通常情况没有被杀掉的空进程也可以避免下次使用时再次创建进程, 这个有利有弊。

3.3、私有进程 与  全局进程 的 区别

        全局的进程,可以被其他应用共用。“共用”指的是不同的App的组件运行在同一个指定的进程中, 只不同的app的组件的 android:process 声明的进程名称是 相同的 且是 以小写字母开头。 那么不同的APP的组件都可以进行这个 名称的进程中。

需要注意的一点:
那么我们先来看看Android5.0以下的源码,ActivityManagerService是如何关闭在应用退出后清理内存的

Process.killProcessQuiet(pid);  


应用退出后,ActivityManagerService就把主进程给杀死了,但是,在Android5.0以后,ActivityManagerService却是这样处理的:

Process.killProcessQuiet(app.pid);  
Process.killProcessGroup(app.info.uid, app.pid); 

 
在应用退出后,ActivityManagerService不仅把主进程给杀死,另外把主进程所属的进程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情,也就停止了。所以在Android5.0以后的手机应用在进程被杀死后,要采用其他方案。

4、多进程的缺点:
4.1、Application的onCreate()多次执行

多进程被创建好了,应用运行时就会对进程进行初始化,如果一个application中有多个进程,在进行全局初始化时,多进程会被初始化多次。

        解决方法:通过判断当前进程是不是 应用包名(默认进程名就是应用包名),来决定需不需要进行初始化相关的操作。

private String getCurrentProcessName() {
     String currentProcName = "";
     int pid = android.os.Process.myPid();
     ActivityManager manager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
     for (ActivityManager.RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
        if (processInfo.pid == pid) {
            currentProcName = processInfo.processName;
            break;
        }
      }
      return currentProcName;
}


        如果子进程也需要一些初始化,则可根据进程名称来分别做对应的 初始化 即可,并且只初始化当前进程需要的业务,即按照进程按需初始化。

4.2、调试麻烦

多进程开发,调试起来很麻烦,比如希望在子进程创建后的首行代码里 添加断点,这是做不到。 因为Android Studio添加调试器的时候,会让你选一个调试的进程,而这时这个子进程还没有启动起来, 一旦你启动起来后,首行代码就执行完毕了,就无法再添加调试了

       解决方法:1、可以在子进程启动后, 添加几秒的延迟 再来启动业务逻辑, 比如延迟5秒,而这5秒就是留给你去Android Studio中添加 断点的

       2、调试时关闭子进程,例如某些操作是在一个  Service时执行的,这时先把 service放到主进程中,等业务代码写完了,再放到子进程 进行 功能测试。

4.3、不能内存共享

        同一个进程的多个线程是共享该进程的所有资源,但多个进程间内存是不可见的,也就是说多个进程间内存是不共享的。    会引起一些问题,如:静态成员的失效。 

4.4、文件共享问题。

       多进程情况下会出现两个进程在同一时刻访问同一个数据库文件的情况。这就可能造成资源的竞争访问,导致诸如数据库损坏、数据丢失等。在多线程的情况下我们有锁机制控制资源的共享,但是在多进程中比较难,虽然有文件锁、排队等机制,但是在Android里很难实现。解决办法就是多进程的时候不并发访问同一个文件,比如子进程涉及到操作数据库,就可以考虑调用主进程进行数据库的操作。

4.5、sharePreference不能在多进程中通用

      在主进程中往sharePreference存的值,在子进程不能立即可见。

     解决方法1:使用SharedPreferences时,SharedPreferences 在MODE_PRIVATE MODE_PUBLIC 之外其实还可以设置多进程的Flag,即 MODE_MULTI_PROCESS。

SharedPreferences myPrefs = context.getSharedPreferences(MY_FILE_NAME, Context.MODE_MULTI_PROCESS | Context.MODE_PRIVATE);

一旦我们设置了这个Flag,每次调用Context.getSharedPreferences 的时候系统会重新从SP文件中读入数据,因此我们在使用的时候每次读取和存入都要使用Context.getSharedPreferences 重新获取SP实例。即使是这样,由于SP本质上并不是多进程安全的,所以还是无法保证数据的同步,因此该方法我们并没有使用,我们也不推荐使用。

      解决方法2:

 github链接: tray安卓多进程安全的sharePreferences

   如果SP不是多进程安全的,那么是否有多进程安全的,又有SP功能的第三方项目呢。答案是有的,Tray——一个多进程安全的SharedPreferences,我们可以在Github上找到它,如果是AndroidStudio,可以直接使用Gradle引入,可谓是十分方便,如下是使用的代码,十分简单,没有apply commit,看起来比SP还要简单。

但是最终我们并没有选择使用它,主要的原因是它需要minSdk 为15,而我们是支持sdk14的,所以只能果断放弃了。

4.6、多进程的通信问题

同一个进程的多个线程是共享该进程的所有资源,但多个进程间内存是不可见的,也就是说多个进程间内存是不共享的。    但通常我们的业务代码都是有关联,很少有 能开启多进程、同时又不需要进程进程通信的 业务场景,因此这就需要多进程之间进行通信。

Android中提供了三种进程通信方法:

系统实现。
AIDL(Android Interface Definition Language,Android接口定义语言):大部分应用程序不应该使用AIDL去创建一个绑定服务,因为它需要多线程能力,并可能导致一个更复杂的实现。
Messenger:利用Handler实现。(适用于多进程、单线程,不需要考虑线程安全),其底层基于AIDL。
4.7、多进程暖启动的恢复问题 

  在主进程的MainActivity退出后,子进程还在运行,这时用户又从手机桌面上点击应用图标启动了主进程,若业务功能中 主进程 与 子进程 有通信,那么此时 应该让 上次断开的主进程与子进程连接  再 重新连接上。  这里就需要额外的判断。

5、Messenger实现多进程通信
1、第一步:在主进程中启动  标记为多进程的 service

2、第二步:利用connectin中回调的 IBinder 接口 创建一个 Messenger, 它用于 主进程 向 子进程 发送消息

3、第三步:子进程的Service的 onBind 创建一个 Messenger 并返回给主进程,这个Messager用于  子进程 向 主进程 发送消息

4、第四步:将子进程的service中创建好的 Messenger 通过 onBind 方法返回给 主进程 iBinder

5、第五步:建立连接

    主进程  必须 先发送一条 消息到 子进程中, 子进程才知道 回传消息 应该 发送给谁。  这时 主进程 就是 客户端,  子进程  就是 服务端, 服务端在被动的等待客户端 发起连接请求。  在服务端 收到 客户端的 连接后,服务端需要 记录下 客户端的链接,以便 服务端 知道后续的消息应该 发送给谁。
————————————————
原文链接:https://blog.csdn.net/u013394527/article/details/80775899

发布了27 篇原创文章 · 获赞 68 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/weixin_40611659/article/details/104984915