当前位置:首页 » 安卓系统 » android的通讯机制

android的通讯机制

发布时间: 2022-12-12 00:57:02

㈠ [Android源码分析] - 异步通信Handler机制

一、问题:在Android启动后会在新进程里创建一个主线程,也叫UI线程( 非线程安全 )这个线程主要负责监听屏幕点击事件与界面绘制。当Application需要进行耗时操作如网络请求等,如直接在主线程进行容易发生ANR错误。所以会创建子线程来执行耗时任务,当子线程执行完毕需要通知UI线程并修改界面时,不可以直接在子线程修改UI,怎么办?

解决方法:Message Queue机制可以实现子线程与UI线程的通信。

该机制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable对象 发给Looper,由它把消息放入所属线程的消息队列中,然后Looper又会自动把消息队列里的消息/Runnable对象 广播 到所属线程里的Handler,由Handler处理接收到的消息或Runnable对象。

1、Handler

每次创建Handler对象时,它会自动绑定到创建它的线程上。如果是主线程则默认包含一个Message Queue,否则需要自己创建一个消息队列来存储

Handler是多个线程通信的信使。比如在线程A中创建AHandler,给它绑定一个ALooper,同时创建属于A的消息队列AMessageQueue。然后在线程B中使用AHandler发送消息给ALooper,ALooper会把消息存入到AMessageQueue,然后再把AMessageQueue广播给A线程里的AHandler,它接收到消息会进行处理。从而实现通信。

2、Message Queue

在主线程里默认包含了一个消息队列不需要手动创建。在子线程里,使用Looper.prepare()方法后,会先检查子线程是否已有一个looper对象,如果有则无法创建,因为每个线程只能拥有一个消息队列。没有的话就为子线程创建一个消息队列。

Handler类包含Looper指针和MessageQueue指针,而Looper里包含实际MessageQueue与当前线程指针。

下面分别就UI线程和worker线程讲解handler创建过程:

首先,创建handler时,会自动检查当前线程是否包含looper对象,如果包含,则将handler内的消息队列指向looper内部的消息队列,否则,抛出异常请求执行looper.prepare()方法。

 - 在 UI线程 中,系统自动创建了Looper 对象,所以,直接new一个handler即可使用该机制;

- 在 worker线程 中,如果直接创建handler会抛出运行时异常-即通过查‘线程-value’映射表发现当前线程无looper对象。所以需要先调用Looper.prepare()方法。在prepare方法里,利用ThreadLocal<Looper>对象为当前线程创建一个Looper(利用了一个Values类,即一个Map映射表,专为thread存储value,此处为当前thread存储一个looper对象)。然后继续创建handler, 让handler内部的消息队列指向该looper的消息队列(这个很重要,让handler指向looper里的消息队列,即二者共享同一个消息队列,然后handler向这个消息队列发送消息,looper从这个消息队列获取消息) 。然后looper循环消息队列即可。当获取到message消息,会找出message对象里的target,即原始发送handler,从而回调handler的handleMessage() 方法进行处理。

 - handler与looper共享消息队列 ,所以handler发送消息只要入列,looper直接取消息即可。

 - 线程与looper映射表 :一个线程最多可以映射一个looper对象。通过查表可知当前线程是否包含looper,如果已经包含则不再创建新looper。

5、基于这样的机制是怎样实现线程隔离的,即在线程中通信呢。 

核心在于 每一个线程拥有自己的handler、message queue、looper体系 。而 每个线程的Handler是公开 的。B线程可以调用A线程的handler发送消息到A的共享消息队列去,然后A的looper会自动从共享消息队列取出消息进行处理。反之一样。

二、上面是基于子线程中利用主线程提供的Handler发送消息出去,然后主线程的Looper从消息队列中获取并处理。那么还有另外两种情况:

1、主线程发送消息到子线程中;

采用的方法和前面类似。要在子线程中实例化AHandler并设定处理消息的方法,同时由于子线程没有消息队列和Looper的轮询,所以要加上Looper.prepare(),Looper.loop()分别创建消息队列和开启轮询。然后在主线程中使用该AHandler去发送消息即可。

2、子线程A与子线程B之间的通信。

1、 Handler为什么能够实现不同线程的通信?核心点在哪?

不同线程之间,每个线程拥有自己的Handler、消息队列和Looper。Handler是公共的,线程可以通过使用目标线程的Handler对象来发送消息,这个消息会自动发送到所属线程的消息队列中去,线程自带的Looper对象会不断循环从里面取出消息并把消息发送给Handler,回调自身Handler的handlerMessage方法,从而实现了消息的线程间传递。

2、 Handler的核心是一种事件激活式(类似传递一个中断)的还是主要是用于传递大量数据的?重点在Message的内容,偏向于数据传输还是事件传输。

目前的理解,它所依赖的是消息队列,发送的自然是消息,即类似事件中断。

0、 Android消息处理机制(Handler、Looper、MessageQueue与Message)

1、 Handler、Looper源码阅读

2、 Android异步消息处理机制完全解析,带你从源码的角度彻底理解

谢谢!

wingjay

![](https://avatars0.githubusercontent.com/u/9619875?v=3&s=460)

㈡ Android IPC机制

IPC是指两个进程之间进行数据交互的过程,即:跨进程通信。
进程是一个执行单,在移动设备上指一个程序或者一个应用。一个进程可以有多个线程,也可以只有一个线程,即主线程。在Android里边,主线程也叫作UI线程,要是在主线程执行大量耗时任务,就会造成界面无法响应,ANR问题,解决这类问题,把耗时操作放在子线程就好。
在Android中,最有特色的进程间通信就是Binder,Binder轻松的实现了进程间的通信。

给四大组件 Activity、Service、Receiver、ContentProvider 在AndroidMenifeist中指定 android:process 属性,可以指定其运行的进程。
: 开头的线程是当前应用的私有进程,其它应用不可以和它跑在同一个进程中,而不以 : 开头的属于全局进程,其他应用通过ShareUID方式可以和它跑在一个进程中。

Android为了每一个应用(进程)都分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间。
多进程会造成如下几个反面方面的问题:

为了解决这些问题,系统提供了跨进程通信方法,虽然不能直接共享内存,但是可以实现数据共享。Intent来传递数据,共享文件,基于Binder的Messenger,ContentProvider,AIDL和Socket。

当我们需要通过Intent和Binder传输数据,或者我们需要把对象持久化到存储设备上,再或者通过网络传输给其它客户端时,Serializable和Parcelable接口可以完成对象的序列化过程。

Serialzable是java提供的序列化接口,是一个空接口,为对象同序列化和反序列化操作。
想让一个类对象实现序列化,只需要这个类实现Serialzable接口,并声明一个serialVersionUID即可,serialVersionUID可以声明成1L或者IDE根据当前类接口自动生成它的hash值。
没有serialVersionUID不影响序列化,但是可能会影响反序列化。序列化时,系统当前类的serialVersionUID写入序列化文件中,当反序列化时,回去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果不一致,无法完成反序列化。

Seriallizable用起来简单但是开销大,序列化和反序列过程需要大量的I/O操作,而Parcelable是Android序列化方式,更适合Android平台,效率更高。Parcelable主要用于内存序列化上,而Seriallizable更适用于序列化到本地存储设备,或者将对象序列化后通过网络传输到别的客户端。

Activity、Service、Receiver都支持在 Intent中传递Bundle数据,Bundle实现了Pareclable接口,所以它可以方便地在不同进程间传输。

Android基于linux,使得其并发读写文件可以没有限制的进行,两个进程可以通过读写一个文件来交换数据。共享数据对文件格式没有要求,双反约定就行。使用文件共享很有可能出问题。
SharedPreferences是个特例,虽然也是属于文件的一种,但是由于系统对它的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,因此在多进程模式下,系统对他的读写变得不可靠,高并发的时候,很大可能会丢失数据。

Messenger可以在不同的进程中传递Message对象,在Message中存入我们需要传递的数据,就可以实现数据的跨进程传递。它是一种轻量级的IPC方案,底层实现是AIDL。
Messenger对AIDL做了封装,使得我们可以更便捷的实现跨进程通信,它一次只处理一个请求,在服务端不用考虑线程同步问题,在服务端不存在并发执行的情形。实现一个Messenger有如下几个步骤:

在服务端创建一个Service,同时创建一个Handler,并通过它来创建一个Messenger对象,然后再Service的onBind中返回这个Messenger对象底层Binder即可。

绑定服务端Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger。通过这个对象就可以向服务端发消息了。如果需要服务端回应客户端,就需要和服务端一样,创建一个Handler,并通过它来创建一个Messenger对象,然后把这个Messenger对象通过Message的replyTo参数传给服务端,服务端可以通过这个replyTo参数回应客户端。

首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现AIDL接口即可。

绑定服务端的Service,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可可以范文AIDL里边的方法了。

在AIDL文件中,并不是所有的额数据类型都是可以使用的。

以上6种数据就是AIDL所支持的所有类型,其中自定义的Parecelable对象和AIDL对象必须显示的import,不管是否和当前的AIDL文件位于同一个包。
AIDL文件中用到了自定义的Parcelable对象,必须新建一个同名的AIDL文件,在其中声明它为parcelable类型。
AIDL中除了基础数据类型,其它类型参数都需要标上方向:in、out、inout,in是输入型参数,out是输出型参数,inout是输入输出型参数。

上面是远程服务端示例,AIDL方法在服务端的Binder线程池中执行,因此各个客户端同时连接的时候,会存在多个线程同时访问的情形,所以要在AIDL中处理线程同步,这个CopyOnWriteArrayList支持并发的读写。
AIDL所支持的是一个抽象的List,只是一个接口,因此虽然服务端返回的是CopyOnWriteArrayList,当时Binder会按照List规范去范文数据并最终形成一个ArrayList传递给客户端。

ServiceConnection 的回调方法在UI线程中运行,服务端的方法有可能很久才能执行完毕,需要考虑ANR的问题。
服务的方法本省就运行再Binder线程池中,本身可以执行大量耗时操作,不要去服务端方法中开县城去进行异步任务。

客户端

服务端

RemoteCallbackList是系统提供专门用于删除跨进程listener的,它的内部有一个Map结构,用来保存所有的AIDL回调,这个Map的key就是Binder类型,value是CallBack类型。
客户端解注册的时候,我们只需要遍历服务端所有的listener,找出那个和接注册listener具有相同的Binder对象的服务端listener并把它删除即可。
RemoteCallbackList的beginBroadcast和finishBroadcast必须配对使用。

ContentProvider是Android专门提供不同应用间进行数据共享的方式。底层实现一样是Binder。
系统预置了许多ContentProvider,比如通讯录,日程信息表,只需要通过ContentResolver的query、update、insert、delete方法即可。

㈢ Binder机制概述

Android进程间通讯是通过Binder机制来实现的,Android是基于linux系统因此有必要了解Linux系统进程相关知识.

Linux系统中(其他系统也是这样)不同进程之间不允许直接操作或访问另一进程.也就是进程隔离.

为了保证用户进程不能直接访问内核,操作系统从逻辑上将虚拟空间划分为用户空间和内核空间.内核程序运行在内核空间(kernel space),应用程序运行在用户空间(user space).为了安全,他们之间是隔离的,即使用户程序奔溃了,也不会影响内核.内核空间数据是可以共享的,用户空间不可以.

用户空间访问内核空间只能通过系统调用,系统调用是用户空间访问内核空间的唯一方式,保证所有资源访问在内核控制下,避免了用户对系统资源的越权访问,提高了系统安全性和稳定性.
_from_user:将用户空间数据拷贝到内核空间
_to_user:将内核空间数据拷贝到用户空间

由于用户进程不能直接访问硬件地址,所以系统提供了一种机制:内存映射(Memory Map).在Linux中通过调用函数mmap实现内存映射,将用户空间一块内存地址映射到内核空间.映射关系建立后,对用户空间内存的修改可以反应到内核空间.内存映射可减少拷贝次数.
如果没有内存映射,用户进程需要访问硬盘文件时,需要在内核空间创建一片页缓存,将硬盘文件数据拷贝到页缓存然后用户进程在拷贝页缓存数据,需要两次拷贝.通过内存映射后硬盘文件可以和内核的虚拟内存直接映射,减少一次拷贝.
如图

inter-process-communication进程间通信,指进程间交换数据的过程.

Linux提供了很多进程间通讯的机制,主要有管道(pipe)、消息队列(Message)、信号(sinal)、信号量(semophore)、套接字(socket)等

内核程序在内核空间分配并开辟一块内核缓冲区,发送进程将数据通过_from_user拷贝到内核空间的数据缓冲区,内核空间通过_to_user将数据拷贝到接收进程,这样就实现了一次进程间通信.如图

Linux的IPC机制有两个问题:
1.数据通过用户空间->内核空间->用户空间,经过两次拷贝,效率不高
2.接收进程无法预先知道数据大小,只能尽可能大创建数据缓冲区,或通过api消息头获取消息体的大小,浪费了空间或时间.

Android进程间通信通过Binder实现,下面介绍Binder机制通信原理,为什么是Binder

内核程序创建一个数据接收缓存区,同时创建一个内核缓冲区.并建立内核缓冲区和数据接收缓冲区内存映射,以及数据内核缓冲区和接收进程用户空间的映射关系.发送进程通过_from_user将数据拷贝到内核数据接收缓冲区,因为内核数据接收缓冲区和内核缓冲区以及接收进程用户空间存在映射关系,相当于将数据发送到了接收进程.完成了一次进程间通信.如图

Binder机制是c/s架构的,由Client、server、ServiceManager和Binder组成.client、server和serviceManager都是独立的进程,由于Linux进程隔离的原因,所以需要借助Binder进行通信.
Binder通信主要有三个步骤:注册服务、获取服务、使用服务.如下图

从上一条Binder实现原理示例图中可以看到,Binder可分为Java Binder、Native Binder和Kernal Binder.应用开发需要了解Java Binder和Navive Binder.这里只介绍Binder基本原理.具体可查看文章结尾的链接.

感谢
https://blog.csdn.net/itachi85/article/details/102713845
https://www.jianshu.com/p/429a1ff3560c
https://blog.csdn.net/carson_ho/article/details/73560642?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159651217319195188353096%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=159651217319195188353096&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2 all first_rank_ecpm_v3~rank_business_v1-1-73560642.ecpm_v3_rank_business_v1&utm_term=Binder&spm=1018.2118.3001.4187

㈣ IM即时通讯开发如何实现Android版智能心跳机制

IM即时通讯开发如何实现Android版智能心跳机制。

大体思路

a)延迟心跳测试法:这是测试结果准确的前提保障,我们认为长连接建立后连续三次成功的短心跳就可以很大程度的保证下一次心跳环境是正常的。

b)成功一次认定,失败连续累积认定:成功是绝对的,连续失败多次才可能是失败。

c)临界值避免:我们使用比计算出的心跳稍微小一点的值做为稳定心跳避免临界值。

d)动态调整:即使在一次完整的智能心跳计算过程中,我们没有找到最好的值,我们还有机会来进行校正。

方案需考虑到影响连接寿命的思素

在Android下,不管是GCM,还是微信,都是通过TCP长连接来进行消息收发的,TCP长连接存活,消息收发就及时,所以要对影响TCP连接寿命的因素进行研究。

1、NAT超时

大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断(NAT超时的更多描述见附录9.1)。NAT超时是影响TCP连接寿命的一个重要因素(尤其是国内),所以客户端自动测算NAT超时时间,来动态调整心跳间隔,是一个重要的优化点。

2、DHCP的租期 (lease time)

目前测试发现安卓系统对DHCP的处理有Bug,DHCP租期到了不会主动续约并且会继续使用过期IP,这个问题会造成TCP长连接偶然的断连。(租期问题的具体描述见附录9.2)。

3、网络状态变化

手机网络和WIFI网络切换、网络断开和连上等情况有网络状态的变化,也会使长连接变为无效连接,需要监听响应的网络状态变化事件,重新建立Push长连接。

心跳范围选择

1、前后台区分处理:

为了保证微信收消息及时性的体验,当微信处于前台活跃状态时,使用固定心跳。微信进入后台(或者前台关屏)时,先用几次最小心跳维持长链接。然后进入后台自适应心跳计算。这样做的目的是尽量选择用户不活跃的时间段,来减少心跳计算可能产生的消息不及时收取影响。

2、后台自适应心跳选择区间:

可根据自身产品的特点选择合适的心跳范围。

自适应心跳算法量化描述

因为每个网络的NAT时间可能不一致。所以需要区分计算,数据网络按subType做关键字,WIFI按WIFI名做关键字。对稳定的网络,因为NAT老化时间的存在,在自适应计算态的时候,暂设计以下步骤在当前心跳区间逼近出最大可用的心跳。 即时通讯聊天软件app开发可以加蔚可云的v:weikeyun24咨询

a)变量说明:

[MinHeart,MaxHeart]——心跳可选区间。

successHeart——当前成功心跳,初始为MinHeart

curHeart——当前心跳初始值为successHeart

heartStep——心跳增加步长

successStep——稳定期后的探测步长

经过该流程,会找到必然使心跳失败的curHeart(或者MaxHeart),为了保险起见,我们选择比前一个成功值稍微小一点的值作为后台稳定期的心跳间隔。

影响手机网络测试的因素太多,为了尽量保证测试结果的可靠性,我们使用延迟心跳测试法。在我们重新建立TCP连接后,先使用  短心跳连续成功三次,我们才认为网络相对稳定,可以使用curHeart进行一次心跳测试。图4-2显示了一次有效心跳测试过程。图4-3显示了在没有达到稳定网络环境时,我们会一直使用固定短心跳直到满足三次连续短心跳成功。

使用延迟心跳测试的好处是,可以剔除偶然失败,和网络变化较大的情况(如地铁),使测试结果相对可靠(五次延迟测试确定结论)。同时在网络波动较大的情况,使用短心跳,保证收取消息相对及时。

c)运行时的动态调整策略(已经按测算心跳稳定值后)

NAT超时值算出来后,在维持心跳的过程中的策略。

-  无网络、网络时好时坏、偶然失败、NAT超时变小:

在后台稳定期发生心跳发生失败后,我们使用延迟心跳测试法测试五次。如果有一次成功,则保持当前心跳值不变;如果五次测试全失败,重新计算合理心跳值。该过程如图4-4所示,有一点需要注意,每个新建的长连接需要先用短心跳成功维持3次后才用successHeart进行心跳。

NAT超时变大:

以周为周期,每周三将后台稳定态调至自适应计算态,使用心跳延迟法往后探测心跳间隔。

-  successHeart是NAT超时临界值:

因为我们现在选择的是一个比successHeart稍小的值作为稳定值,所以在计算过程中可以避开临界值。当运营商在我们后台稳定期将NAT超时调整为我们当前计算值,那么由于我们每周会去向下探索,所以下一周探测时也可以及时调整正确。

d)冗余Sync和心跳

在用户的一些主动操作以及联网状态改变时,增加冗余Sync和心跳,确保及时收到消息。

1、当用户点亮屏幕的时候,做一次心跳。

2、当微信切换到前台时,做一次Sync。

3、联网时重建信令TCP,做一次Sync。

可能存在的风险及预防措施

DHCP租期因素:

1、问题:根据目前的测试结果显示,安卓不续约到期的IP Bug,会导致TCP连接在不确定的时间点失效,从而会导致一次心跳失败。

2、预防:统计后台稳定期的心跳成功率,上报给后台。后台可以按地区分网络监控这个指标的波动,并且后台可以根据不同的波动,动态调整某区域特定网络下可选的心跳区间。

NAT超时介绍

因为 IP v4 的 IP 量有限,运营商分配给手机终端的 IP 是运营商内网的 IP,手机要连接 Internet,就需要通过运营商的网关做一个网络地址转换(Network Address Translation,NAT)。简单的说运营商的网关需要维护一个外网 IP、端口到内网 IP、端口的对应关系,以确保内网的手机可以跟 Internet 的服务器通讯。

㈤ Android消息机制和原理

Android消息机制及其原理

Handle的原理

andriod提供了Handler和Looper来满足线程间的通信。Handler先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(MessageExchange)。

MessageQueue

MessageQueue是持有Message(在Looper中派发)的一个链表,Message并不是直接添加到MessageQueue中的,而是通过与Looper相关联的Handler来进行的。

用来存放线程放入的消息,读取会自动删除消息,单链表维护,在插入和删除上有优势。在其next()中会无限循环,不断判断是否有消息,有就返回这条消息并移除。

Looper

一个线程可以产生一个Looper对象,由它来管理此线程里的MessageQueue

Looper创建的时候会创建一个MessageQueue,调用loop()方法的时候消息循环开始,loop()也是一个死循环,会不断调用messageQueue的next(),当有消息就处理,否则阻塞在messageQueue的next()中。当Looper的quit()被调用的时候会调用messageQueue的quit(),此时next()会返回null,然后loop()方法也跟着退出。

MessageQueue和Looper是一对一关系,Handler和Looper是多对一

Handler

在主线程构造一个Handler,与Looper沟通,以便push新消息到MessageQueue里;

接收Looper从MessageQueue取出Handler所送来的消息。然后在其他线程调用sendMessage(),此时主线程的MessageQueue中会插入一条message,然后被Looper使用.

Thread

UIthread 通常就是main thread,而Android启动程序时会替它建立一个MessageQueue,系统的主线程在ActivityThread的main()为入口开启主线程,其中定义了一系列消息类型,包含四大组件的启动停止。

消息队列分发算法源码

每个message之间拉手,知道自己前面和后面的message

message通过时间戳来排序,小的在前

配合handle取出message,message时间到,就去除队列首个message,取出之后置为null,第二个message就排在第一,类推

//消息的存放

boolean enqueueMessage(Message msg, long when) {

    synchronized (this) {

        msg.when = when;

        Message p = mMessages;  //注解1

        if (p == null || when == 0 || when < p.when){

            msg.next = p;

            mMessages = msg;    //注解2

        } else {

            Message prev;

            for (;;) {          //注解3

                prev = p;

                p = p.next;

                if (p == null || when < p.when) {

                    break;

                }

            }

            msg.next = p;

            prev.next = msg;

        }

    }

    return true;

}

㈥ Android通信方式篇(七)-Binder机制(Native层(下))

本篇文章针对向ServiceManager注册服务 和 获取服务两个流程来做总结。在这两个过程中,ServiceManager都扮演的是服务端,与客户端之间的通信也是通过Binder IPC。

在此之前先了解下Binder的进程与线程的关系:

用户空间 :ProcessState描述一个进程,IPCThreadState对应一个进程中的一个线程。
内核空间 :binder_proc描述一个进程,统一由binder_procs全局链表保存,binder_thread对应进程的一个线程。
ProcessState与binder_proc是一一对应的。

Binder线程池 :每个Server进程在启动时会创建一个binder线程池,并向其中注册一个Binder线程;之后Server进程也可以向binder线程池注册新的线程,或者Binder驱动在探测到没有空闲binder线程时会主动向Server进程注册新的的binder线程。对于一个Server进程有一个最大Binder线程数限制15,(#define DEFAULT_MAX_BINDER_THREADS 15)。对于所有Client端进程的binder请求都是交由Server端进程的binder线程来处理的。我的理解是:binder线程是进程进行binder ipc时的一条数据处理路径。

MediaPlayerService向ServiceManager注册过程如下:

相关类:

整个过程总结如下:
1 获取BpServiceManager 与 BpBinder
由defaultServiceManager()返回的是BpServiceManager,同时会创建ProcessState对象和BpBinder对象。然后通过BpBinder执行transact,把真正工作交给IPCThreadState来处理。

2 BpBinder transact
Binder代理类调用transact()方法,真正工作还是交给IPCThreadState来进行transact工作。

3 通过IPCThreadState 包装并转换数据并进行transact事务处理
每个线程都有一个IPCThreadState,每个IPCThreadState中都有一对Parcel变量:mIn、mOut。相当于两根数据管道:

最后执行talkWithDriver。

writeTransactionData:将BC Protocol + binder_transaction_data结构体 写入mOut, 然后执行waitForResponse:

由talkWithDriver将数据进一步封装到binder_write_read结构体,通过ioctl(BINDER_WRITE_READ)与驱动通信。同时等待驱动返回的接收BR命令,从mIn取出返回的数据。

mIn包装的数据结构(注册服务handle = 0 ,code 为ADD_SERVICE_TRANSACTION):

4 Binder Driver
把binder_write_read结构体write_buffer里数据取出来,分别得到BC命令和封装好数据的事务binder_transaction_data, 然后根据handler,在当前binder_proc中,找到相应的binder_ref,由binder_ref再找到目标binder_node实体,由目标binder_node再找到目标进程binder_proc。然后就是插入数据:当binder驱动可以找到合适的线程,就会把binder_transaction节点插入到servciemanager的线程的todo队列中,如果找不到合适的线程,就把节点之间插入servciemanager的binder_proc的todo队列。

5 ServiceManager
经过Binder Driver的处理,数据已经到了ServiceManager进程,在BR_TRANSACTION的引导下,在binder_loop()中执行binder_parser()取出数据,执行do_add_service()操作,最终向 svcinfo 列表中添加已经注册的服务(没有数据的返回)。最后发送 BR_REPLY 命令唤醒等待的线程,通知注册成功。结束MediaPlayerService进程 waitForResponse()的状态,整个注册过程结束。

获取服务的过程与注册类似,首先 ServiceManager 向 Binder 驱动发送 BC_TRANSACTION 命令携带 CHECK_SERVICE_TRANSACTION 命令,同时获取服务的线程进入等待状态 waitForResponse()。Binder 驱动收到请求命令向 ServiceManager 的发送 BC_TRANSACTION 查询已注册的服务,会区分请求服务所属进程情况。

查询到直接响应 BR_REPLY 唤醒等待的线程。若查询不到将与 binder_procs 链表中的服务进行一次通讯再响应。

以startService为例来简单总结下执行流程:

3.1 从方法执行流程来看:

Client :

1 AMP.startService 标记方法以及通过Parcel包装数据;

2 BinderProxy.transact 实际调用native的 android_os_BinderProxy_transact 传递数据;

3 获取BpServiceManager 与 BpBinder 同时会创建ProcessState。然后通过BpBinder执行transact,把真正工作交给IPCThreadState来处理;

4 IPC.transact 主要执行writeTransactionData,将上层传来的数据重新包装成binder_transaction_data,并将BC Protocol + binder_transaction_data结构体 写入mOut;

5 IPC waitForResponse talkWithDriver + 等待返回数据;

6 talkWithDriver 将数据进一步封装成binder_write_read,通过ioctl(BINDER_WRITE_READ)与驱动通信;

Kernel :

7 binder ioctl 接收BINDER_WRITE_READ ioctl命令;

8 binder_ioctl_write_read 把用户空间数据ubuf拷贝到内核空间bwr;

9 binder_thread_write 当bwr写缓存有数据,则执行binder_thread_write;当写失败则将bwr数据写回用户空间并退出;

10 binder_transaction 找到目标进程binder_proc并插入数据到目标进程的线程todo队列,最终执行到它
时,将发起端数据拷贝到接收端进程的buffer结构体;

11 binder_thread_read 根据binder_transaction结构体和binder_buffer结构体数据生成新的binder_transaction_data结构体,写入bwr的read_buffer,当bwr读缓存有数据,则执行binder_thread_read;当读失败则再将bwr数据写回用户空间并退出;最后,把内核数据bwr拷贝到用户空间ubuf。

12 binder_thread_write + binder_ioctl BR命令和数据传递

Server:

13 IPC.executeCommand 解析kernel传过来的binder_transaction_data数据,找到目标BBinder并调用其transact()方法;

14 IPC.joinThreadPool 采用循环不断地执行getAndExecuteCommand()方法, 处理事务。当bwr的读写buffer都没有数据时,则阻塞在binder_thread_read的wait_event过程. 另外,正常情况下binder线程一旦创建则不会退出.

15 BBinder.transact 到Binder.exeTransact 调用 AMN.onTransact

16 AMN.onTransact 把数据传递到AMS.starService去执行

17 AMS.starService Server处理了Client的请求了

然后原路replay回去,talkWithDriver 到Kernel ,然后找到Client进程,把数据拷贝到read_buffer里,最终唤醒IPC,把反馈传递回AMP.startService。完成启动服务。

3.2 从通信协议流程来看:

非oneWay:

oneway:

oneway与非oneway区别: 都是需要等待Binder Driver的回应消息BR_TRANSACTION_COMPLETE. 主要区别在于oneway的通信收到BR_TRANSACTION_COMPLETE则返回,而不会再等待BR_REPLY消息的到来. 另外,oneway的binder IPC则接收端无法获取对方的pid.

3.3 从数据流来看

从用户空间开始:

进入驱动后:

回到用户空间:

参考:
http://gityuan.com/2016/09/04/binder-start-service/
http://gityuan.com/2015/11/28/binder-summary/
http://gityuan.com/2015/11/14/binder-add-service/
http://gityuan.com/2015/11/15/binder-get-service/

㈦ 在android中进程间通信机制是怎样的

一般都是基于ARM处理器的吧 安卓的内核也是基于Linux的吧。 网络实现依靠TCP/IP协议栈实现实行封包和解包以及连接的建立和控制,还涉及到你手机的硬件网卡等。 进程间通信方式一般采用的消息队列,共享内存,套接字,还有管道了。 多线程是由操作系统来管理每个线程的CPU时间和资源的分配。也是比较复杂的,涉及到线程间通信,线程同步等。 内存管理是由操作系统进行分段,分页。分配机制比较复杂的,涉及到碎片的减少,内存的回收等。 要想了解详细内容,可以看看Linux操作系统原理。或者google提供的相关文档。

㈧ android中的进程通信和Binder机制

如果统观Binder中的各个组成元素,就会惊奇的发现它和TCP/IP网络有很多相似之处:

首先Binder是android进程间通信的一种方式,
基本原理:binder定义了4个角色:client,server,serviceManager ,binder驱动
server会创建一个binder实体并起一个名字,然后将名字一块以数据包的形式通过binder驱动发送给serviceManager ,通知servicemanager注册一个名字为xx的Binder,然后client通过名字查询到该Binder 的引用。

注意

㈨ Android面试必问handler机制浅析

Handler是Android中的异步消息处理机制。当发送一个消息之后,这个消息是进入一个消息队列(MessageQueue),在消息队列中通过Looper去循环的获取队列中的消息,然后将消息分派给对应的处理者进行处理。

Message:存储需要处理操作的信息

MessageQueue:先进先出,存储handler发送过来的消息

Looper:循环器,它是消息队列和handler的通信媒介,1:循环的取出消息队列中的消息;2:将取出的消息发送给对应的处理者

Handler:主线程和子线程的通信媒介,1:添加消息到消息队列; 2:处理循环器分派过来的消息

在handler机制中,Looper.loop方法会不断循环获取Message, 其中的消息的获取是通过调用MessageQueue的next()方法获取的,而该方法会调用nativePollOnce()方法 ,这是一个native方法。底层的实现涉及到Linux pipe/epoll机制,nativePollOnce()被阻塞时,主线程会释放CPU资源,进入休眠状态. 直到下个消息到达或者有事务发生,会通过pipe管道写端写入数据来唤醒looper工作。

Android6.0及以前的版本使用管道与epoll来完成Looper的休眠与唤醒的

Android6.0及以后的版本使用eventfd与epoll来完成Looper的休眠与唤醒的

如果不处理的话,会阻塞线程,处理方案是调用Looper的quit()(清空所有的延迟和非延迟的消息)和quitSafely()(只清空延迟消息); 这个方法会调用MessageQueue的quit()方法,清空所有的Message,并调用nativeWake()方法唤醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循环继续执行,接下来发现Message为null后就会结束循环,Looper结束。如此便可以释放内存和线程

同进程线程间内存共享,通过handler通信,消息的内容是不需要从一个线程拷贝到另一个线程,因为两个线程间可使用的内存是同一个区域。(注意:线程私有区域ThreadLocal)

管道的作用就是当一个线程准备好Message,并放入消息池,这时需要通知了一个线程B去处理这个消息。线程A向管道的写端写入数据,管道有数据便会唤醒线程B去处理消息。管道的作用是用于通知另一个线程的,这便是最核心的作用。

从内存角度,通信过程中binder涉及到一次内存拷贝,handler机制中的Message根本不需要拷贝,本身就是在同一片内存。

从CPU角度,为了Binder通信底层驱动还需要创建一个binder线程池,每次通信涉及binder线程的创建和内存的分配等比较浪费CPU资源

原因:handler发送的消息在当前handler的消息队列中,如果此时activity被finish掉了,那么消息队列的消息依旧由handler进行处理,若此时handler申明为内存类(非静态内部类),内部类持有外部类的实例引用,这样在GC垃圾回收时发现Activity还有其他引用存在,因而就不会去回首这个Activity,进而导致Activity泄漏。

方法:使用静态内部类,并且使用WeakReference包裹外部类的对象。首先静态内部类不持有外部类的引用,使用静态的handler不会导致activity的泄漏,handler定义static的同时,还要用WeakReference包裹外部类的对象。

㈩ Android跨进程通信

本文整理和引用他人的笔记,旨在个人复习使用。

参考链接:

https://blog.csdn.net/fanleiym/article/details/83894399

https://github.com/274942954/AndroidCollection/blob/master/Docs/Android%E7%9F%A5%E8%AF%86%E7%82%B9%E6%B1%87%E6%80%BB.md#%E8%BF%9B%E7%A8%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F

https://www.kaelli.com/4.html

https://carsonho.blog.csdn.net/article/details/73560642?utm_medium=distribute.pc_relevant.none-task-blog--1.e_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog--1.e_weight

默认情况下,一个app只会运行在一个进程中,进程名为app的包名。

1. 分散内存的占用

Android系统对每个应用进程的内存占用是有限制的,占用内存越大的进程,被系统杀死的可能性就越大。使用多进程可以减少主进程占用的内存,避免OOM问题,降低被系统杀死的概率。

2. 实现多模块

一个成熟的应用一定是多模块化的。项目解耦,模块化,意味着开辟新的进程,有独立的JVM,带来数据解耦。模块之间互不干预,团队并行开发,同时责任分工也很明确。

3. 降低程序奔溃率

子进程崩溃不会影响主进程的运行,能降低程序的崩溃率。

4. 实现一些特殊功能

比如可以实现推送进程,使得主进程退出后,能离线完成消息推送服务。还可以实现守护进程,来唤醒主进程达到保活目的。还可以实现监控进程专门负责上报bug,进而提升用户体验。

android:process 属性的值以冒号开头的就是 私有进程 ,否则就是 公有进程 。当然命名还需要符合规范,不能以数字开头等等。

1. 前台进程

2. 可见进程

3. 服务进程

4. 后台进程

5. 空进程

Android 会将进程评定为它可能达到的最高级别。另外服务于另一进程的进程其级别永远不会低于其所服务的进程。

创建新的进程时会创建新的Application对象,而我们通常在Application的onCreate方法中只是完成一些全局的初始化操作,不需要多次执行。

解决思路:获取当前进程名,判断是否为主进程,只有主进程的时候才执行初始化操作

获取当前进程名的两种方法:

Application中判断是否是主进程(方法1例子):

Serializable 和 Parcelable是数据序列化的两种方式,Android中只有进行序列化过后的对象才能通过intent和Binder传递。

通常序列化后的对象完成传输后,通过反序列化获得的是一个新对象,而不是原来的对象。

Serializable是java接口,位于java.io的路径下。Serializable的原理就是把Java对象序列化为二进制文件后进行传递。Serializable使用起来非常简单,只需直接实现该接口就可以了。

Parcelable是Google为了解决Serializable效率低下的问题,为Android特意设计的一个接口。Parcelable的原理是将一个对象完全分解,分解成可以传输的数据类型(如基本数据类型)再进行传递。

通常需要存到本地磁盘的数据就使用Serializable,其他情况就使用效率更高的Parcelable。

IPC 即 Inter-Process Communication (进程间通信)。Android 基于 Linux,而 Linux 出于安全考虑,不同进程间不能之间操作对方的数据,这叫做“进程隔离”。

每个进程的虚拟内存空间(进程空间)又被分为了 用户空间和内核空间 进程只能访问自身用户空间,只有操作系统能访问内核空间。

由于进程只能访问自身用户空间,因此在传统的IPC中,发送进程需要通过_from_user(系统调用)将数据从自身用户空间拷贝到内核空间,再由接受进程通过_to_user从内核空间复拷贝到自身用户空间,共需要拷贝2次,效率十分低下。Android采用的是Binder作为IPC的机制,只需复制一次。

Binder翻译过来是粘合剂,是进程之间的粘合剂。

Binder IPC通信的底层原理是 通过内存映射(mmap),将接收进程的用户空间映射到内核空间 ,有了这个映射关系,接收进程就能通过用户空间的地址获得内核空间的数据,这样只需发送进程将数据拷贝到内核空间就可完成通讯。

一次完整的Binder IPC通信:

从IPC的角度看,Binder是一种跨进程通信机制(一种模型),Binder 是基于 C/S 架构的,这个通信机制中主要涉及四个角色:Client、Server、ServiceManager和Binder驱动。

Client、Server、ServiceManager都是运行在用户空间的进程,他们通过系统调用(open、mmap 和 ioctl)来访问设备文件/dev/binder,从而实现与Binder驱动的交互。Binder驱动提供进程间通信的能力(负责完成一些底层操作,比如开辟数据接受缓存区等),是Client、Server和ServiceManager之间的桥梁。

Client、Server就是需要进行通信两个的进程,通信流程:

细心的你一定发现了,注册服务和获得服务本身就是和ServiceManager进行跨进程通信。其实和ServiceManager的通信的过程也是获取Binder对象(早已创建在Binder驱动中,携带了注册和查询服务等接口方法)来使用,所有需要和ServiceManager通信的进程,只需通过0号引用,就可以获得这个Binder对象了。

AIDL内部原理就是基于Binder的,可以借此来分析Binder的使用。

AIDL是接口定义语言,简短的几句话就能定义好一个复杂的、内部有一定功能的java接口。

先看看ICallBack.aidl文件,这里定义了一个接口,表示了服务端提供的功能。

被定义出来的java接口继承了IInterface接口,并且内部提供了一个Stub抽象类给服务端(相当于封装了一下,服务端只需继承这个类,然后完成功能的里面具体的实现)。

参考: https://www.cnblogs.com/sqchen/p/10660939.html

(以下是添加了回调的最终实现,可以看参考链接一步一步来)

为需要使用的类,创建aidl文件。

系统会自动在main文件下生成aidl文件夹,并在该文件夹下创建相应目录。

在java相同路径下创建Student类,这里不能使用@Parcelize注解,否则会报错

创建IStudentService.aidl,定义了一个接口,该接口定义了服务端提供的功能。创建完后rebuild一下项目 (每次创建和修改定义接口文件都要rebuild一下)

创建在服务端的StudentService

可以看见有回调,说明客户端也提供了接口给服务端来回调(双向通信,此时客户端的变成了服务端),即ICallBack.aidl

客户端是通过Binder驱动返回的Binder调用StudentService里的具体实现方法

AIDL使用注意:

Messenger可以在不同进程中传递 Message 对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递了。Messenger 是一种轻量级的 IPC 方案,是对AIDL的封装,底层实现是 AIDL。

使用详见: https://blog.csdn.net/qq951127336/article/details/90678698

热点内容
宽带拨号连接中账户密码是什么 发布:2025-03-17 23:49:06 浏览:356
android贪吃蛇 发布:2025-03-17 23:45:57 浏览:67
zbar源码 发布:2025-03-17 23:42:18 浏览:769
水星wifi改密码怎么改 发布:2025-03-17 23:41:39 浏览:789
编程班表 发布:2025-03-17 23:41:34 浏览:880
网上邻居访问权限 发布:2025-03-17 23:41:31 浏览:389
国行安卓11如何使用谷歌 发布:2025-03-17 23:40:52 浏览:145
ftp数据传输和控制端口 发布:2025-03-17 23:40:52 浏览:874
首算法 发布:2025-03-17 23:40:02 浏览:450
php应用程序池 发布:2025-03-17 23:36:13 浏览:409