小程序如何实现多进程(小程序开发,移动开发)

时间:2024-04-28 18:36:17 作者 : 石家庄SEO 分类 : 移动开发
  • TAG :

    %E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E5%A4%9A%E8%BF%9B%E7%A8%8B

小程序这个名词相信大家已经不陌生了,继微信之后,阿里巴巴、百度、头条等大厂相继实现了自己的小程序。小程序是一种全新的开放能力,开发者能够快速开发出小程序并集成进宿主,实现推广等目的。
从使用角度看,小程序有轻量,易用等特点;
从技术角度,以Android端为例,小程序有部分组件原生化、UI和逻辑线程隔离、小程序之间进程隔离等等。

下面主要从小程序进程隔离角度出发,分析BAT的小程序多进程的实现方案,并自己实现一个小程序的多进程。

多进程,顾名思义,即每一个小程序都是一个单独的进程。这个效果只在Android端独有。那为什么我们希望小程序之间实现进程隔离呢?原因大致有三点:

由于是单独进程,无论小程序内部因为何种原因的崩溃,对主进程都没有影响,增强用户体验。

由于每个进程都有一片单独的内存区域,小程序不会占用主进程的内存,降低了内存溢出的风险。

由于不同进程间的内存是隔离的,当同时开启多个小程序时,内存变量、参数等数据互不影响,也可达到一个解耦的目的。

既然是分析多进程这种用户感知不强烈的技术点,我们需要通过一些工具或命令。

进程分析

首先,我们把微信完全杀死又重新开启,然后通过 adb shell ps | grep com.tencent.mm 命令,可以查看正在运行的进程名称和数量,其中 grep com.tencent.mm 是过滤微信相关的进程,因为微信的包名是com.tencent.mm。此时进程运行状况如下图:

小程序如何实现多进程

此时我们只能看出微信从完全关闭到启动,开启了6个进程,但是还看不出这些是否与小程序相关。于是,我们打开一个小程序,再次执行 adb shell ps | grep com.tencent.mm 命令,此时进程运行状况如下图:

小程序如何实现多进程

对比之后,就可以做一些分析了。其中,com.tencent.mm是主进程,com.tencent.mm:push应该是与推送相关的进程,com.tencent.mm.tools和com.tencent.mm:toolsmp应该都是一个类似于Helper的相关进程,因此我认为最有可能的是com.tencent.mm:appbrand2,因为这个命名比较特殊,前面分别有appbrand0和appbrand1两个进程出现过。那么为什么新开的第一个小程序,反而多出来的进程时appbrand2呢?我个人猜测这与微信小程序的预加载有关系,很有可能是,这个进程是空的,只是先fork出来,并没有做过多的事情,真正承载我们开启的那个小程序的进程,很有可能不是这个appbrand2。那么如何验证呢?进入第二步,Activity分析。

首先打开一个小程序,然后通过 adb shell dumpsys activity activities 命令,可以看到所有栈内的Activity信息,滑到顶部,查看正在与用户交互的Activity信息,如下图:

小程序如何实现多进程

关键的信息我已经用红色圈了出来,processName=com.tencent.mm:appbrand0,realActivity=com.tencent.mm/.plugin.appbrand.ui.AppBrandUI,这大致已经验证了我们刚才的猜想,即:

com.tencent.mm:appbrand系列,是与小程序相关的进程。

微信预加载2个空进程作为预加载,避免用时再fork进程,耗时过长影响用户体验。

为了进一步验证猜想是否正确,我下载了微信最新版本的apk,进行了逆向操作,也就是第三步,分析apk。

反编译的方法大家自行搜索,这里就不赘述了。我们打开反编译后的AndroidManifest.xml文件,搜索刚才的的Activity名称,结果如下:

小程序如何实现多进程

得到的信息与刚才一致。然而,我们又发现了另一个问题,那就是AppBrandUI还有另外4个兄弟,即AppBrandUI1,AppBrandUI2,AppBrandUI3,AppBrandUI4,而这四个Activity的名称与绑定的进程,又能够与一开始的appbrand对应起来,经过试验,我发现微信最多只可以启动5个小程序,而这些小程序的载体就是这5个Activity,不断轮询,超过5个时,将第一个结束掉。这样,我们就基本可以确定,微信是通过apk内置的5个Activity,来实现小程序的多开与进程隔离的。

因此,理论上这5个Activity应该是除了进程不同,内部逻辑应该都是相同的,于是我们继续验证,反编译代码后找到AppBrandUI1这个Activity,结果如下图:

小程序如何实现多进程

AppBrandUI2,AppBrandUI3,AppBrandUI4与此完全一样,都是继承了AppBrandUI,做了极少的事,由于微信代码混淆过,我们无法看出那几行代码具体做了什么,但是基本可以理解为完全复用。至于为什么分开写,而不是复用同一个,我猜测原因可能有二:

由于语法限制,为Activity开辟进程需要在AndroidManifest.xml中预先配置

2. 微信不仅将小程序进程隔离,并且还进行了栈隔离,当我们同时开启多个小程序时,长按Home键,可以发现存在多个小程序任务卡片,这种效果同样需要在AndroidManifext.xml中配置taskAffinity属性,这在上图中也有体现。

另外,我还注意到,微信在AndroidManifest.xml中配置了这样的Receiver:

小程序如何实现多进程

小程序如何实现多进程

这种Receiver共有5个,每个小程序进程有一个,其它4个只是继承了这个AppBrandTaskPreloadReceiver,由于混淆的原因,无法看出具体做了什么事,但是通过名字判断,是实现小程序进程预加载的,空闲时开启这个广播,至少可以提前开启进程,避免用时再加载耗时过长影响用户体验。

同样的,我分析了百度和支付宝的apk,通过命令和反编译等方法,发现他们的方案几乎一样,只是预加载的数量等一些小细节不同,感兴趣的同学可以自己逆向之后做对比。

分析总结

1. 微信对每个小程序都做了进程隔离和栈隔离,互不影响。

2. 实现这一功能的载体Activity是预先配置在AndroidManifest.xml中的。

3. 通过某种方法,微信将小程序的最大运行数量控制在5个。

4. 微信对多进程做了一些优化,已知的是预加载2个空进程。

5. BAT等大厂的小程序多进程方案大同小异。

一. Application初始化

Android的app在开启多进程时,每开启一个进程,Application都会重新创建,也就是onCreate函数会被调用,如果没有做进程判断,所有东西会初始化多次,造成卡顿或意料之外的bug。

二. 分配与管控

由于进程之间的内存无法共享,小程序的生命周期需要在某一个进程中维护,不然无法做到动态分配进程和栈,而这个进程选择主进程最为合适。因此需要创建一个管理器,这个管理器负责以下几件事:

1. 接收外界开启、关闭等对小程序的操作

2. 合理的为接收到的请求分配空闲的进程

3. 接收远程小程序的生命周期回调并通过某个uuid进行维护

4. 在空闲时预加载进程

5. 根据设置的可开启的最大小程序数量,对进程进行新建、销毁等操作

6. 所有这些管理和维护的操作,对小程序接入者都应是透明的,无需关心具体实现流程,仅在需要时开启小程序即可。

三. 进程生命周期问题

Android系统对于内存有一套自己的管控机制,当内存较为紧张时会在不做任何通知的情况下kill掉活跃度较低的进程,至于进程活跃程度,就与Android的进程保活有关了,可通过设置前台进程、唤醒等方式去尽量保活。但是无论应用端再怎么做,都无法逾越操作系统的权限,系统在某些情况下依然会把进程杀死来保证整个系统的正常运行。因此,开发时需要做容错处理,不能仅以Activity的onDestroy回调为准,因为一旦出现系统级的回收,很可能导致整个分配管理器的错乱。

四. 通讯

通信又分为两个方面,第一,小程序进程与app主进程是隔离的,需要进程间的IPC通信;第二,小程序的本质是一个web容器,这就少不了js与原生的通信,需要jsbridge。下面分别说一下这两个方面。进程通信:

IPC的实现已经不是什么问题,这里仅说几个需要注意的点:

1. 通信一定是双向的,无论哪个进程,最好绑定同一个服务,方便数据的维护。

2. 由于通信较为频繁,建议使用基于Binder的通信机制,可以提高运行效率。

js与原生通信:

js与原生通信一定是通过jsbridge,最好做法是将原生方法的实现写在主进程,分布在不同进程的小程序向主进程请求某个bridge的实现结果,主进程根据相应的参数去执行并返回结果,类似于一套CS的架构。即小程序客户端无需关心具体操作,只关心结果并响应给web端。

本文:小程序如何实现多进程的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:Context是什么下一篇:

16 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18