它需要2年时间,字数超过10,000。最好的总结告诉你安卓多进程和微信微博的使用。

目录

前言为什么使用多进程?为什么你需要“跨过程沟通”?跨流程沟通的方法有哪些?使用AIDL的多进程消息推送实现的一个例子以其具体实现及其原因而闻名。跨进程回调接口死亡接受者权限验证根据不同的进程,做不同的初始化工作总结结论

为什么要使用多进程的概念

对于进程,来这里的人都是程序员和实践者,所以他们不会说太多。我相信每个人都能以各种姿势背出来,向前,向后,向前,向后和向前。相信很多学生在实际开发中不会分割应用程序的过程。此外,在安卓系统中使用多进程可能需要编写额外的进程通信代码,还可能带来额外的错误。这无疑增加了开发的工作量,并且在许多初创公司中是不允许的,导致整个应用处于一个过程中。

整个应用程序在一个过程中有什么问题?

在安卓系统中,虚拟机分配给每个进程的运行内存是有限的(该值可以是32M、48M、64M等)。视型号而定)。想象一下,如果一个非常普通的图片选择模块被添加到上传图片或头像的应用程序中,加载大量的位图会使应用程序的内存占用迅速增加。如果您也将检查过的图片缓存在内存中,那么OOM的风险将会大大增加。如果此时需要WebView加载一波网页,我会问你是否害怕!

微信、微博和其他主流应用如何解决这些问题?

微信移动开发团队在文章《安卓内存优化杂谈》中表示:“用于网络浏览、图库等。,由于内存系统泄漏或内存占用过多的问题,我们可以采用单独的流程微信还将把它们放在一个单独的工具流程中。"

下面我们用adb查看微信和微博的流程信息(安卓5.0以下版本可以直接设置->;应用程序"相关条目视图):

微博微信

进入adb shell后,使用" ps | grep条目名称"过滤出您想要查看的进程

我们可以看到微信的确有工具流程,新浪微博也有图片相关的流程,还有很多其他的流程,比如微信的推送流程和微博的远程流程。在这里我们可以看到,他们不仅将上面的WebView和gallery放入单独的流程中,而且还在单独的流程中运行推送服务。消息推送服务可能需要从用户界面流程中分离出来,以确保稳定性。分离后,即使用户界面进程退出、崩溃或内存消耗过高,消息推送服务也不会受到影响。

表明,多过程的合理使用不仅是一个多好的问题,我个人认为这是必要的

因此,我们最好根据自己的情况考虑是否需要拆分流程。这也是本文的初衷:为你提供一个多过程参考思路,当遇到上述问题和场景时,你可以考虑使用多过程方法来解决问题,或者,在面试过程中,当你与面试官谈论这些知识时,你不会感到尴尬。为什么

需要“跨进程通信”

安卓进程互相通信?有些不需要我们写额外的通信代码。例如,如果我们将选定的图片模块放入一个单独的过程中,我们仍然可以使用startActivityForResult方法将选定的图片放入一个包中,并使用“意图”来传输它(看这里,你不打算把你项目的图片选择交给一个独立的过程吗?然而,为了将“消息推送服务”放入一个单独的过程中,这个服务稍微复杂一些。此时,一系列复杂的操作,如在活动和服务之间传递对象,调用服务方法等。可能会发生。由于每个进程都在相对独立的内存空间中运行,它们不能直接通信,因为程序中的变量和对象在初始化后都有内存地址。例如,读取变量的值本质上就是找到变量的内存地址并取出存储的值。不同的进程在独立的存储器中运行(事实上,它们可以被理解为两个不同的应用程序)。显然,他们不能直接知道对方的变量和对象的内存地址。这样,它们就不能自然地访问彼此的变量和对象等。此时,两个流程之间的交互需要通过跨流程通信来实现。简而言之,跨进程通信是一种允许进程相互交互的技术。要在

的跨流程通信方法中传递Bundle的

4个主要组件是什么;使用文件共享方法,多个进程读写同一个文件,并获取文件内容进行交互;使用了Messenger,一种轻量级的跨进程通信方案,并在底部使用了AIDL(实现相对简单。在开始这篇文章之前,这位博客作者还考虑了是否要对此说些什么。最后,他觉得没有这个必要。谷歌可以一次性解决这个问题,所以他不会过多谈论这个问题。使用AIDL(安卓界面定义语言),安卓界面定义语言用于定义跨流程通信的界面。使用ContentProvider,它通常用于多进程数据共享,如系统相册、音乐等。我们也可以通过内容提供商访问它。使用套接字传输数据接下来,本文将重点讨论AIDL在多进程通信中的应用,因为AIDL是安卓系统提供给我们的标准跨进程通信API,它非常灵活和强大(看起来采访经常被问到,但实际上并没有太多使用……)上面提到的信使也是AIDL实施的一种跨流程方法。信使,顾名思义,就像一个串行消息机制。这是一个轻量级的IPC方案,可以在不同的进程中传递消息对象。通过将要传递的数据放入消息中,我们可以很容易地实现进程间通信。但是当我们需要调用服务器端方法或者有并发请求时,信使就不合适了然而,这四个主要组件传递Bundle,这无需解释。要传输的数据可以打包并有目的地传输。其他方法超出了本文的范围。

以下是AIDL的解释。你准备好去渡劫了吗?

使用AIDL推进

多进程要求,例如通过使用跨进程消息进行图片选择。它可能不需要我们为过程通信编写额外的代码。只需要使用四个主要组件来传输包。然而,对于诸如推送服务这样的需求,流程和流程需要高度的交互,此时不能绕过流程通信的这一步。让我们以即时消息软件为例,手动实现一个多进程推送示例。具体要求如下:

用户界面和消息推送服务分为两个流程;用户界面进程用于显示特定的消息数据,将用户发送的消息传送到消息服务,然后将其发送到远程服务器。服务负责接收和发送消息,并与远程服务器保持长期连接。用户界面进程可以通过服务向远程服务器发送消息。服务从远程服务器接收消息并通知用户界面进程。即使用户界面进程退出,服务仍然需要继续运行并接收服务器消息

实施思路

让我们先理清实施思路:

创建用户界面流程(以下统称为客户);创建消息服务(以下统称为服务器);将服务器配置为单独的进程(在AndroidManifest.xml中指定进程标签);客户端和服务器绑定(BindServiCe);让客户端和服务器能够交互(AIDL使用);为了便于阅读,下面的代码将省略非关键部分。您可以在本地查看这篇文章,并获得完整的代码克隆:

STEP 0。AIDL调用过程概述在开始

之前,让我们总结一下使用AIDL的多进程调用的整个过程:

客户端使用bindService方法绑定服务器;服务器在onBind方法中返回一个Binder对象;客户端接收服务器返回的Binder对象,并调用跨进程方法。

微博微信

的整个AIDL呼叫过程可以概括为上述三个步骤。在下文中,我们将使用上述示例逐步分解这些步骤,并描述其中的细节。

步骤1。客户端使用bindService方法绑定服务器

1.1来创建客户端和服务器,并将服务器配置到另一个进程

来创建客户端->;主要活动;创建服务器->。MessageService。将服务器配置到另一个进程->。安卓:process = ":REMOTE"

上面描述的客户端和服务器,以及将服务器配置到另一个进程,都包含在安卓清单. xml中,如下所示:

微博微信

是一个打开多个进程的简单方法,只有安卓:process标签需要分配给四个组件。

1.2将消息服务绑定到主活动

创建消息服务:

此时的消息服务正是它所创建的。onBind中返回null。我们将在下一步向客户端返回一个可操作的对象。

微博微信

客户端MainActivity调用bindService方法来绑定MessageService

。这一步实际上属于与服务组件相关的知识。简而言之,启动服务可以通过以下两种方式完成:

使用bindService方法->;绑定服务(意图服务、服务连接连接、内部标志);使用开始服务方法->。开始服务(意向服务);

bindService & amp开始服务区别:使用绑定服务方法,多个客户端同时解除绑定一个服务,但是当所有客户端都解除绑定时,服务将退出。在正常情况下,如果您想要与服务交互,通常使用bindService方法。您可以使用onServiceConnected中的IBinder对象与服务进行交互。您可以在不与服务交互的情况下使用startService方法。

如上所述,我们希望与服务交互,所以我们需要使用bindService方法,但是我们希望服务在解除绑定后仍然运行。在这种情况下,我们可以调用bindService和startService(例如,本例中的消息服务,退出用户界面进程,服务仍然需要接收消息)。代码如下:

微博微信

Stpe2。服务器在onBind方法中返回Binder对象

2.1。首先,什么是活页夹?

说Binder,首先,IBinder是远程对象的基本接口,是轻量级远程过程调用机制的核心部分。这个接口描述了与远程对象交互的抽象协议,而Binder实现了IBinder接口。简单地说,Binder是Android SDK中内置的多进程通信实现类。在使用时,我们不需要也不实现IBinder,而是继承Binder类来实现多进程通信。

2.2其次,需要在onBind方法中返回的Binder对象来自哪里?

将引出本文的主题——AIDL多进程中使用的Binder对象通常是通过自动生成的。我们定义的adil接口文件。当然,您可以直接或手动编写跨流程通信所需的Binder类。它的本质只不过是一个继承Binder的类。假设野生路径行走起来很麻烦,而且都是重复的步骤。谷歌提供了AIDL界面来帮助我们自动生成活页夹的直线路径。在下文中,我们将继续讨论AIDL的直路(我们不能偏袒任何人),

微博微信

2.3定义AIDL接口

显然,接下来我们需要进行上述的绑定,以便客户端可以调用服务器的方法。活页夹是通过AIDL界面自动生成的,所以让我们从AIDL开始,在继续之前先看看需要注意的事项。为避免意外:

AIDL支持数据类型:

Java编程语言中的所有基本数据类型(如int、long、char、boolean等)。)字符串和CharSequenceParcelable:实现Parcelable接口的对象列表:其中的元素需要得到AIDL的支持。在另一端实际接收的具体类总是ArrayList,但是生成的方法使用列表接口映射:其中的元素需要得到AIDL的支持,包括键和值。在另一端实际接收的具体类总是HashMap,但是生成的方法使用Map接口

其他注意事项:在AIDL传递的

对象必须实现Parcelable序列化接口;对于在aidl中传递的对象,是一个名称相同但后缀为。应该在类文件的同一路径下创建AIDL,并且应该使用parcelable关键字在文件中声明该类。不同于普通接口:只能声明方法,不能声明变量;所有非基础数据类型参数都需要标注数据趋势的方向标志可以是进、出或出。默认情况下,基本数据类型只能在中,不能在其他方向

继续下面的例子,我们开始解释AIDL

2.4创建一个AIDL界面,它提供了一种发送消息的方法(安卓工作室创建AIDL:右击项目->;新->。AIDL ->。AIDL文件),代码如下:

微博微信

是一个相当尴尬的事情。在阅读了许多文章之后,从来没有一篇文章能够清楚地解释输入、输出和输入输出这三个参数方向的含义。后来,我找到了一个可以理解的答案。我翻译了一般的意思:标有“in”的

参数是接收实际数据的参数,与我们常用的参数传输具有相同的含义在AIDL,“输出”指定一个仅用于输出的参数。换句话说,这个参数不关心调用者传递了什么数据,但是这个参数的值可以在方法被调用后被填充(不管调用者传递了什么值,当方法被执行时,这个参数的初始值总是空的)。这是“输出”的意思,仅用于输出“输入输出”显然是输入和输出参数“输入”和“输出”的组合区分“进”和“出”有什么用?这非常重要,因为每个参数的内容都必须分组(序列化、传输、接收和反序列化)入/出标签允许活页夹跳过分组步骤以获得更好的性能

上述消息模型是消息的一个实体类。这个类在AIDL传递,并实现了Parcelable序列化接口。代码如下:

微博微信

手动实现Parcelable接口很麻烦。安利的自动生成插件安卓智能插件创建了实体类消息模型。不要忘记还有一件事要做:“对于在AIDL传递的对象,一个同名但后缀为的文件。必须在类文件的同一路径下创建aidl,并且必须使用文件中的parcelable关键字声明该类。代码如下:

对于没有接触过aidl的学生来说,仅仅说它就能让人目瞪口呆。现在让我们来看看项目结构:

微博微信

我们刚刚添加了3个文件:

MessageSender.aidl ->;定义发送消息的方法,自动生成名为MessageSender的Binder类。存根,在服务器上实现它,并将其返回给客户端以调用MessageModel . Java-->消息实体类,该类从客户端传递到服务器,实现MessageModel . aidl-->的Parcelable序列化。据说,消息模型可以在AIDL交付,并放置在与MessageModel.java

OK相同的包裹路径下。据信,此时孟的原力已释放~

微博微信

2.5。由MessageSender.aidl的AIDL接口自动生成的Binder对象在服务器上创建,并返回给客户端进行调用。服务器端消息服务的代码如下:

微博微信

消息发送者。存根是一个由安卓工作室根据我们的MessageSender.aidl文件自动生成的Binder对象(至于它是如何生成的,下面会有一个答案)。我们需要将这个Binder对象返回到客户端。

2.6在客户端获得Binder对象后,它调用远程方法

。调用步骤如下:

在客户端的onServiceConnected方法中,获取服务器返回的Binder对象;使用消息发送者。stub . as接口方法,获取消息发送者. aidl对应的操作界面;获得MessageSender对象后,像调用普通接口一样调用该方法

此时,客户端代码如下:

微博微信

在客户端,我们调用了消息发送方的sendMessage方法,向服务器发送消息,并将生成的MessageModel对象作为参数传递给服务器。服务器的最终打印结果如下:

微博微信

这里有两点要说:

服务器已经收到客户端发来的消息,打印正确;服务器和客户端区分两个进程,具有不同的进程标识和不同的进程名称。

在这里,我们已经使用AIDL完成了最基本的step方法调用,这也是步骤0的整个精化过程。我们可以再次回顾步骤0。既然我们已经学会了如何使用它,那么...整出戏结束了。如果

微博微信

写在整部戏的结尾,它和咸鱼有什么区别...

知道它是什么,为什么是

。通过上述调用过程,我们将看到从客户端到服务器发生了什么,以及Binder的上层是如何工作的。至于活页夹的下层,这是一个非常复杂的话题,本文将不再深入探讨。(如果你想问活页夹是什么,请手动倒带并查找…)

让我们回顾一下从客户端发起的调用过程:

消息发送者消息发送者=消息发送者。存根。界面(服务);消息发送者.发送消息(消息模型);

除了其他不相关的代码之外,客户端调试方法由这两个步骤组成,它们被封装在由MessageSender.aidl最终生成的MessageSender.aidl源代码中(具体路径是:构建目录的子目录,如果不喜欢,自己找到它并点击我)。

请参见下面的代码和注释。前方的高能警报...如果只看代码,

微博微信

16

微博微信

可能会有点混乱。我相信如果我们结合代码看下面的流程图会更好理解:

微博微信

从客户的发送消息开始,整个AIDL呼叫过程如上图所示。作为接口方法,将判断onBind方法返回的绑定器是否存储在同一进程中。如果在同一个过程中,将进行传统的方法调用。如果是在不同的进程中,整个数据传输过程需要通过Binder底层进行分组(序列化、传输、接收和反序列化),最终数据将在进行常规方法调用之前获得。

敲黑板:对象跨进程传输的本质是一个序列化、传输、接收和反序列化的过程。这就是为什么跨进程传输的对象必须实现Parcelable接口

跨进程回调接口

。上面,我们已经实现了从客户端向跨进程服务器发送消息的功能。接下来,我们需要将服务器接收到的远程服务器消息传输到客户端一些学生可能会说,“这不是回调接口的事情”。设置一个回调接口是正确的,但是这里使用的回调接口有点不同。在AIDL传递的接口不能是公共接口,而只能是AIDL接口。因此,我们需要创建一个新的AIDL接口,并将其作为回调接口发送给服务器。

aidl接口消息接收器。AIDL:

微博微信

对于新的消息收集,我们将向服务器注册回调接口。修改我们的信息发送者。AIDL:

微博微信

或更多是我们最终修改的AIDL界面。接下来,我们需要做相应的改变:

将消息发送者的注册和反注册接口添加到服务器的方法;消息接收器接口在客户端实现,并通过消息发送器向服务器注册。

客户端更改并修改了主要活动:

微博微信

22-


微博微信

客户端主要有3项更改:

添加了一个messageReceiver对象来监控服务器的消息通知;在onServiceConnected方法中,消息接收器在服务中注册;onDestroy何时注销消息接收器对

:

微博微信

25-

下的服务器消息服务进行了以下更改。服务器的主要更改是:

消息发送者。存根实现注册和注销回调接口的方法;添加远程呼叫反向列表来管理AIDL远程接口。FakeTCPTask模拟一个长连接,通知客户端新消息已经到达。(这里的长连接可以是XMPP、Mina、Mars、Netty等。这是假的意思。如果你有时间,让我们打开一个帖子来谈论XMPP。)还有必要讨论远程呼叫反向列表。为什么使用远程调用链表,普通的数组链表做不到?当然不是,否则,为什么要让另一个RemoteCallbackList、registerReceiveListener和unregistreceivelstener从客户端传输对象,当服务器通过Binder处理接收到该对象时,它实际上是一个新对象?这导致当使用unregisterReceiveListener时,公共数组列表找不到使用registereerceivelistener时添加到列表中的对象,但是它们底部使用的Binder对象是相同的。RemoteCallbackList可以通过使用这个特性找到相同的对象,这样我们就可以顺利地反向注册客户端传递的接口对象。远程调用反向列表可以在客户端进程终止后自动删除客户端注册的侦听器。它还在内部实现线程同步,因此我们不需要在注册和注销中考虑线程同步。这确实是一个666班(至于使用数组列表的飞蛾现象,你可以自己试试。这里不会演示空间问题)

这里,服务器通知客户端相关代码已经编写完毕,运行结果只不过是没有映射的正确打印。您可以自己运行它,并注意选择不同的过程时,打印,否则将没有日志,如果屏幕被损坏。你认为这已经结束了吗?太年轻,太单纯...

我不知道您是否觉得两个过程之间的交互总是有点不安全...例如,服务器进程崩溃,而客户端进程想要调用服务器方法,因此无法调用此时,我们可以为活页夹设置一个死亡接收者对象。当活页夹意外挂起时,我们可以在死亡接收者接口的回调方法中接收通知,并进行相应的操作,如重新连接服务等。

死亡收件人的用法如下:

声明死亡收件人对象并实现其绑定的方法。当绑定器死亡时,它将回调绑定的方法;将死亡收件人对象设置为活页夹对象。

客户端主要活动声明中的两个重要方法死亡接收者:

微博微信

绑定者:

链接到死亡->;将死亡收件人对象设置为死亡代理;不链接到目录->。当粘合剂死亡时,试剂被释放

此外,《活页夹》中的“活页夹”还可以判断活页夹是否死了

许可验证

即使是公共汽车,上车并上车卡。对吧。如果我们希望我们的服务流程不像总线,那么我们可以添加许可验证。

引入了两种常用的验证方法:

在服务器的onBind中验证自定义权限。如果它通过了我们的验证,它通常会返回Binder对象。如果它没有通过验证并返回null,客户端就不能绑定到我们的服务。服务器端的onTransact方法检查客户端包名,并且不检查就直接返回false。检查通过正常过程

自定义权限,在Androidmanifest中添加自定义权限。xml:

微博微信

服务器权限检查方法:

微博微信

29-

根据不同的进程做不同的初始化工作

相信很多朋友在过去的一两年里仍然在使用安卓-通用-图像加载器加载图片,需要在应用程序类中进行初始化例如,我们用它来加载图片,还有一个图片选择过程,所以我们想分配更多的缓存给图片选择过程,或者一些其他的初始化工作,我们应该做什么而不初始化图片选择过程?

在这里提供了一个简单而粗糙的方法,博客作者也做了同样的事情...只要得到进程名判断并进行相应的操作:

微博微信

一旦创建了每个进程,就会调用应用程序的onCreate方法。这是一个需要注意的地方,我们也可以得到当前流程的名称,根据当前流程的pid进行判断。然后做一些我们需要的逻辑。在我们的例子中,我们得到了两个进程名:

客户端进程:com.example.aidl服务器进程:com . example . aidl:remote

Summary

多进程应用程序可以在系统中应用多个内存副本,但应该合理使用。建议将一些高消耗但不常使用的模块放入独立的进程中,不使用的进程可以及时手动关闭。实现多进程有很多方法:四个主要组件通过Bundle、Messenger、AIDL等。并选择自己的使用场景;安卓实现多进程通信。建议使用系统提供的活页夹类。这个类已经实现了多进程通信,不需要我们做底层工作。对于多进程应用程序,该应用程序将被创建多次。

结论

这篇文章断断续续写了很长时间,我相信真正使用它的学生可能不多。选择这样一个话题对我来说是吃力不讨好的…但是我仍然希望在这里提供一个完整的解决方案。简单的多流程使用,效果显著,比如图片选择和WebView配置要分开流程,这一点我希望你能采取行动这篇文章中有许多知识点,可能不容易理解。如果你感兴趣,我建议你手工写,然后打断你不理解的地方,看看是什么样的操作步骤。

对于被采访的学生,如果你在采访中谈论多个过程,如果你和采访者交谈,你可能会增加一些要点。或者在实际工作中,如果你使用多种方法来更好地解决问题,你可以在会议上拍一张桌子,对主管说,“我有一个大胆的想法……”,这对于假装被强迫也有好处(当然,如果你被解雇了,这不关我的事...)。

不容易。如果你喜欢这篇文章,或者它对你有帮助,我希望你会更加注意它文章将不断更新。绝对是干货!!!

大家都在看

相关专题