首页 文章 精选 留言 我的

精选列表

搜索[面试],共5092篇文章
优秀的个人博客,低调大师

这些喜闻乐见的Java面试知识点,你都掌握了吗?

最近分享了一些有关学习方法和经验的文章,得到了很多读者的反馈,恰巧大家在昨天推文中的投票里一直选择了“Java基础的复习方法”这一项,那么今天我们就谈谈这方面的内容吧。 其实对于Java基础的学习,我觉得最好的方法就是理论结合实践,先通过书籍,博客等内容理解基本原理,再通过写一些demo进行实践,做到知其然又知其所以然。 前言以下内容出自我的CSDN技术博客专栏,由于内容较多,我在这里只贴一个大概的目录。 本系列文章是我在秋招复习过程中创作和整理的内容,当时的初衷也是希望能够通过写博客把这些知识点吃透,当然我也是通过阅读博客+代码实践的方式来学习的,所以也有部分文章是转载的,毕竟大牛的博客真的可以把知识点讲的很透彻。 大家可以根据需要进行阅读。即使看的不是我博客的文章,也可以按照这个顺序进行阅读学习,我个人觉得效果还是不错的。 专栏:深入浅出Java核心技术 本专栏主要介绍Java基础,并且会结合实现原理以及具体实例来讲解。同时还介绍了Java集合类,设计模式以及Java8的相关知识。 专栏地址:https://blog.csdn.net/a724888/column/info/21930 ONE Java基础知识 Java基础1:深入理解Java面向对象三大特性Java基础2:基本数据类型与常量池Java基础3:深入理解String及包装类java基础4:深入理解final关键字Java基础5:抽象类和接口Java基础6:代码块与代码加载顺序Java基础7:关于Java类和包的那些事Java基础8:深入理解内部类Java基础9:解读Java回调机制Java基础10:全面解读Java异常Java基础11:Java泛型详解Java基础12:深入理解Class类和Object类Java基础13:反射详解Java基础14:离开IDE,使用java和javac构建项目Java基础15:深入剖析Java枚举类Java基础16:Java多线程基础最全总结Java基础17:Java IO流总结Java基础18:Java序列化与反序列化Java基础19:Java集合框架梳理Java基础20:Java8新特性终极指南 TWO Java集合类 Java集合详解1:ArrayList,Vector与Stack Java集合详解2:LinkedList和Queue Java集合详解3:Iterator,fail-fast机制与比较器 Java集合详解4:HashMap和HashTable Java集合详解5:深入理解LinkedHashMap和LRU缓存 Java集合详解6:TreeMap和红黑树 Java集合详解7:HashSet,TreeSet与LinkedHashSet Java集合详解8:Java集合类细节精讲 THREE 设计模式 初探Java设计模式1:创建型模式(工厂,单例等) 初探Java设计模式2:结构型模式(代理模式,适配器模式等) 初探Java设计模式3:行为型模式(策略,观察者等)初探设计模式 初探Java设计模式4:JDK中的设计模式 初探Java设计模式5:Spring涉及到的9种设计模式

优秀的个人博客,低调大师

面试必问的4种单点登录的实现方式,你知道几个?

前言 单点登录在现在的系统架构中广泛存在,他将多个子系统的认证体系打通,实现了一个入口多处使用,而在架构单点登录时,也会遇到一些小问题,在不同的应用环境中可以采用不同的单点登录实现方案来满足需求。 一、共享Session 共享Session可谓是实现单点登录最直接、最简单的方式。将用户认证信息保存于Session中,即以Session内存储的值为用户凭证,这在单个站点内使用是很正常也很容易实现的,而在用户验证、用户信息管理与业务应用分离的场景下即会遇到单点登录的问题,在应用体系简单,子系统很少的情况下,可以考虑采用Session共享的方法来处理这个问题。 这个架构我使用了基于Redis的Session共享方案。将Session存储于Redis上,然后将整个系统的全局Cookie Domain设置于顶级域名上,这样SessionID就能在各个子系统间共享。分布式Session共享解决方案,这篇推荐大家看下。这个方案存在着严重的扩展性问题,首先,ASP.NET的Session存储必须为SessionStateItemCollection对象,而存储的结构是经过序列化后经过加密存储的。并且当用户访问应用时,他首先做的就是将存储容器里的所有内容全部取出,并且反序列化为SessionStateItemCollection对象。这就决定了他具有以下约束:1.Session中所涉及的类型必须是子系统中共同拥有的(即程序集、类型都需要一致),这导致Session的使用受到诸多限制;2.跨顶级域名的情况完全无法处理; 二、基于OpenId的单点登录 这种单点登录将用户的身份标识信息简化为OpenId存放于客户端,当用户登录某个子系统时,将OpenId传送到服务端,服务端根据OpenId构造用户验证信息,多用于C/S与B/S相结合的系统,流程如下: 由上图可以看到,这套单点登录依赖于OpenId的传递,其验证的基础在于OpenId的存储以及发送。1.当用户第一次登录时,将用户名密码发送给验证服务;2.验证服务将用户标识OpenId返回到客户端;3.客户端进行存储;4.访问子系统时,将OpenId发送到子系统;5.子系统将OpenId转发到验证服务;6.验证服务将用户认证信息返回给子系统;7.子系统构建用户验证信息后将授权后的内容返回给客户端。这套单点登录验证机制的主要问题在于他基于C/S架构下将用户的OpenId存储于客户端,在子系统之间发送OpenId,而B/S模式下要做到这一点就显得较为困难。为了处理这个问题我们将引出下一种方式,这种方式将解决B/S模式下的OpenId的存储、传递问题。 三、基于Cookie的OpenId存储方案 我们知道,Cookie的作用在于充当一个信息载体在Server端和Browser端进行信息传递,而Cookie一般是以域名为分割的,例如a.xxx.com与b.xxx.com的Cookie是不能互相访问的,但是子域名是可以访问上级域名的Cookie的。即a.xxx.com和b.xxx.com是可以访问xxx.com下的Cookie的,于是就能将顶级域名的Cookie作为OpenId的载体。 验证步骤和上第二个方法非常相似:1、 在提供验证服务的站点里登录;2、 将OpenId写入顶级域名Cookie里;3、 访问子系统(Cookie里带有OpenId)4、 子系统取出OpenId通过并向验证服务发送OpenId5、 返回用户认证信息6、 返回授权后的内容在以上两种方法中我们都可以看到通过OpenId解耦了Session共享方案中的类型等问题,并且构造用户验证信息将更灵活,子系统间的验证是相互独立的,但是在第三种方案里,我们基于所有子系统都是同一个顶级域名的假设,而在实际生产环境里有多个域名是很正常的事情,那么就不得不考虑跨域问题究竟如何解决。 四、B/S多域名环境下的单点登录处理 在多个顶级域名的情况下,我们将无法让各个子系统的OpenId共享。处理B/S环境下的跨域问题,我们首先就应该想到JSONP的方案。 验证步骤如下:1、 用户通过登录子系统进行用户登录;2、 用户登录子系统记录了用户的登录状态、OpenId等信息;3、 用户使用业务子系统;4、 若用户未登录业务子系统则将用户跳转至用户登录子系统;5、 用户子系统通过JSONP接口将用户OpenId传给业务子系统;6、 业务子系统通过OpenId调用验证服务;7、 验证服务返回认证信息、业务子系统构造用户登录凭证;(此时用户客户端已经与子业务系统的验证信息已经一一对应)8、 将用户登录结果返回用户登录子系统,若成功登录则将用户跳转回业务子系统;9、 将授权后的内容返回客户端; 五、安全问题 经过以上步骤,跨域情况下的单点登录问题已经可以得到解决。而在整个开发过程初期,我们采用用户表中纪录一个OpenId字段来保存用户OpenId,而这个机制下很明显存在一些安全性、扩展性问题。这个扩展性问题主要体现在一个方面:OpenId的安全性和用户体验的矛盾。 整个单点登录的机制决定了OpenId是会出现在客户端的,所以OpenId需要有过期机制,假如用户在一个终端登录的话可以选择在用户每次登录或者每次退出时刷新OpenId,而在多终端登录的情况下就会出现矛盾:当一个终端刷新了OpenId之后其他终端将无法正常授权。欢迎大家关注我的公种浩【程序员追风】,文章都会在里面更新,整理的资料也会放在里面。 而最终,我采用了单用户多OpenId的解决方案。每次用户通过用户名/密码登录时,产生一个OpenId保存在Redis里,并且设定过期时间,这样多个终端登录就会有多个OpenId与之对应,不再会存在一个OpenId失效所有终端验证都失效的情况。 最后 欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

优秀的个人博客,低调大师

这10道springboot常见面试题你需要了解下

1、什么是Spring Boot? 多年来,随着新功能的增加,spring变得越来越复杂。只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能。 如果必须启动一个新的Spring项目,我们必须添加构建路径或添加Maven依赖关系,配置应用程序服务器,添加spring配置。 因此,开始一个新的spring项目需要很多努力,因为我们现在必须从头开始做所有事情。 Spring Boot是解决这个问题的方法。Spring Boot已经建立在现有spring框架之上。使用spring启动,我们避免了之前我们必须做的所有样板代码和配置。 因此,Spring Boot可以帮助我们以最少的工作量,更加健壮地使用现有的Spring功能。 2、Spring Boot有哪些优点? 减少开发,测试时间和努力。 使用JavaConfig有助于避免使用XML。 避免大量的Maven导入和各种版本冲突。 提供意见发展方法。 通过提供默认值快速开始开发。 没有单独的Web服务器需要。这意味着你不再需要启动Tomcat,Glassfish或其他任何东西。 需要更少的配置 因为没有web.xml文件。只需添加用@ Configuration注释的类,然后添加用@Bean注释的方法,Spring将自动加载对象并像以前一样对其进行管理。您甚至可以将@Autowired添加到bean方法中,以使Spring自动装入需要的依赖关系中。 基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应用程序属性文件后,Spring将在(application{environment} .properties)中加载后续的应用程序属性文件。 3、什么是JavaConfig? Spring JavaConfig是Spring社区的产品,它提供了配置Spring IoC容器的纯Java方法。因此它有助于避免使用XML配置。使用JavaConfig的优点在于: 面向对象的配置。由于配置被定义为JavaConfig中的类,因此用户可以充分利用Java中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean方法等。 减少或消除XML配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在XML和Java之间来回切换。 JavaConfig为开发人员提供了一种纯Java方法来配置与XML配置概念相似的Spring容器。 从技术角度来讲,只使用JavaConfig配置类来配置容器是可行的,但实际上很多人认为将JavaConfig与XML混合匹配是理想的。 类型安全和重构友好。JavaConfig提供了一种类型安全的方法来配置Spring容器。由于Java 5.0对泛型的支持,现在可以按类型而不是按名称检索bean,不需要任何强制转换或基于字符串的查找。 4、如何重新加载Spring Boot上的更改,而无需重新启动服务器? 这可以使用DEV工具来实现。通过这种依赖关系,您可以节省任何更改,嵌入式tomcat将重新启动。 Spring Boot有一个开发工具(DevTools)模块,它有助于提高开发人员的生产力。Java开发人员面临的一个主要挑战是将文件更改自动部署到服务器并自动重启服务器。 开发人员可以重新加载Spring Boot上的更改,而无需重新启动服务器。这将消除每次手动部署更改的需要。Spring Boot在发布它的第一个版本时没有这个功能。 这是开发人员最需要的功能。DevTools模块完全满足开发人员的需求。该模块将在生产环境中被禁用。它还提供H2数据库控制台以更好地测试应用程序。 5、Spring Boot中的监视器是什么? Spring boot actuator是spring启动框架中的重要功能之一。Spring boot监视器可帮助您访问生产环境中正在运行的应用程序的当前状态。 有几个指标必须在生产环境中进行检查和监控。即使一些外部应用程序可能正在使用这些服务来向相关人员触发警报消息。监视器模块公开了一组可直接作为HTTP URL访问的REST端点来检查状态。 6、如何在Spring Boot中禁用Actuator端点安全性? 默认情况下,所有敏感的HTTP端点都是安全的,只有具有ACTUATOR角色的用户才能访问它们。 安全性是使用标准的HttpServletRequest.isUserInRole方法实施的。 我们可以使用management.security.enabled = false 来禁用安全性。只有在执行机构端点在防火墙后访问时,才建议禁用安全性。 如何在自定义端口上运行Spring Boot应用程序? 为了在自定义端口上运行Spring Boot应用程序,您可以在application.properties中指定端口。 server.port = 8090 7、什么是YAML? YAML是一种人类可读的数据序列化语言。它通常用于配置文件。 与属性文件相比,如果我们想要在配置文件中添加复杂的属性,YAML文件就更加结构化,而且更少混淆。可以看出YAML具有分层配置数据。 8、如何实现Spring Boot应用程序的安全性? 为了实现Spring Boot的安全性,我们使用 spring-boot-starter-security依赖项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展WebSecurityConfigurerAdapter并覆盖其方法。 9、如何集成Spring Boot和ActiveMQ? 对于集成Spring Boot和ActiveMQ,我们使用spring-boot-starter-activemq 依赖关系。 它只需要很少的配置,并且不需要样板代码。 10、如何使用Spring Boot实现分页和排序? 使用Spring Boot实现分页非常简单。使用Spring Data-JPA可以实现将可分页的org.springframework.data.domain.Pageable传递给存储库方法。

优秀的个人博客,低调大师

看完这篇 Android ANR 分析,就可以和面试官装逼了!

ANR概述 首先,ANR(Application Not responding)是指应用程序未响应,Android系统对于一些事件需要在一定的时间范围内完成,如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成ANR。ANR由消息处理机制保证,Android在系统层实现了一套精密的机制来发现ANR,核心原理是消息调度和超时处理。 其次,ANR机制主体实现在系统层。所有与ANR相关的消息,都会经过系统进程(system_server)调度,然后派发到应用进程完成对消息的实际处理,同时,系统进程设计了不同的超时限制来跟踪消息的处理。 一旦应用程序处理消息不当,超时限制就起作用了,它收集一些系统状态,譬如CPU/IO使用情况、进程函数调用栈,并且报告用户有进程无响应了(ANR对话框)。 然后,ANR问题本质是一个性能问题。ANR机制实际上对应用程序主线程的限制,要求主线程在限定的时间内处理完一些最常见的操作(启动服务、处理广播、处理输入), 如果处理超时,则认为主线程已经失去了响应其他操作的能力。主线程中的耗时操作,譬如密集CPU运算、大量IO、复杂界面布局等,都会降低应用程序的响应能力。 哪些场景会造成ANR? 1. 发生ANR时会调用AppNotRespondingDialog.show()方法弹出对话框提示用户,该对话框的依次调用关系如下图所示: 2.AppErrors.appNotResponding(),该方法是最终弹出ANR对话框的唯一入口,调用该方法的场景才会有ANR提示,也可以认为在主线程中执行无论再耗时的任务,只要最终不调用该方法,都不会有ANR提示,也不会有ANR相关日志及报告;通过调用关系可以看出哪些场景会导致ANR,有以下四种场景: (1)Service Timeout:Service在特定的时间内无法处理完成 (2)BroadcastQueue Timeout:BroadcastReceiver在特定时间内无法处理完成 (3)ContentProvider Timeout:内容提供者执行超时 (4)inputDispatching Timeout: 按键或触摸事件在特定时间内无响应。 ANR机制 ANR机制可以分为两部分:ANR监测机制:Android对于不同的ANR类型(Broadcast, Service, InputEvent)都有一套监测机制。ANR报告机制:在监测到ANR以后,需要显示ANR对话框、输出日志(发生ANR时的进程函数调用栈、CPU使用情况等)。 整个ANR机制的代码也是横跨了Android的几个层:App层:应用主线程的处理逻辑;Framework层:ANR机制的核心,主要有AMS、BroadcastQueue、ActiveServices、InputmanagerService、InputMonitor、InputChannel、ProcessCpuTracker等;Native层:InputDispatcher.cpp; Provider超时机制遇到的比较少,暂不做分析;Broadcast目前主要想说两个知识点: 第一:无论是普通广播还是有序广播,最终广播接受者的onreceive都是串行执行的,可以通过Demo进行验证; 第二:通过Demo以及框架添加相关日志,都验证了普通广播也会有ANR监测机制,ANR机制以及问题分析文章认为只有串行广播才有ANR监测机制,后续再会专门讲解Broadcast发送及接收流程,同时也会补充Broadcast ANR监测机制;本文主要以Servie处理超时、输入事件分发超时为例探讨ANR监测机制。 Service超时监测机制 Service运行在应用程序的主线程,如果Service的执行时间超过20秒,则会引发ANR。 当发生Service ANR时,一般可以先排查一下在Service的生命周期函数中(onCreate(), onStartCommand()等)有没有做耗时的操作,譬如复杂的运算、IO操作等。 如果应用程序的代码逻辑查不出问题,就需要深入检查当前系统的状态:CPU的使用情况、系统服务的状态等,判断当时发生ANR进程是否受到系统运行异常的影响。 如何检测Service超时呢?Android是通过设置定时消息实现的。定时消息是由AMS的消息队列处理的(system_server的ActivityManager线程)。 AMS有Service运行的上下文信息,所以在AMS中设置一套超时检测机制也是合情合理的。 我们先抛出两个问题 问题一:Service启动流程? 问题一:如何监测Service超时? 主要通过以上两个问题来说明Service监测机制,在知道Service启动流程之后,通过Service启动流程可以更容易分析Service超时监测机制。 1. Service启动流程如下图所示: (1)ActiveServices.realStartServiceLocked()在通过app.thread的scheduleCreateService()来创建Service对象并调用Service.onCreate()后,接着又调用sendServiceArgsLocked()方法来调用Service的其他方法,如onStartCommand。以上两步均是进程间通信,应用与AMS之间跨进程通信可以参考应用进程与系统进程通信 (2)以上只是列出Service启动流程的关键步骤,具体每个方法主要做哪些工作还需要查看具体的代码,暂时先忽略这些,感兴趣的可以参考Android开发艺术探索等其他相关资料 2. Service超时监测机制 Service超时监测机制可以从Service启动流程中找到。 (1)ActiveServices.realStartServiceLocked()主要工作有 privatefinalvoidrealStartServiceLocked(ServiceRecordr, ProcessRecordapp,booleanexecInFg)throwsRemoteException{ ... //主要是为了设置ANR超时,可以看出在正式启动Service之前开始ANR监测; bumpServiceExecutingLocked(r,execInFg,"create"); //启动过程调用scheduleCreateService方法,最终会调用Service.onCreate方法; app.thread.scheduleCreateService(r,r.serviceInfo, //绑定过程中,这个方法中会调用app.thread.scheduleBindService方法 requestServiceBindingsLocked(r,execInFg); //调动Service的其他方法,如onStartCommand,也是IPC通讯 sendServiceArgsLocked(r,execInFg,true); } (2)bumpServiceExecutingLocked()会调用scheduleServiceTimeoutLocked()方法 voidscheduleServiceTimeoutLocked(ProcessRecordproc){ if(proc.executingServices.size()==0||proc.thread==null){ return; } Messagemsg=mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_TIMEOUT_MSG); msg.obj=proc; //在serviceDoneExecutingLocked中会remove该SERVICE_TIMEOUT_MSG消息, //当超时后仍没有removeSERVICE_TIMEOUT_MSG消息,则执行ActiveServices.serviceTimeout()方法; mAm.mHandler.sendMessageDelayed(msg, proc.execServicesFg?SERVICE_TIMEOUT:SERVICE_BACKGROUND_TIMEOUT); //前台进程中执行Service,SERVICE_TIMEOUT=20s;后台进程中执行Service,SERVICE_BACKGROUND_TIMEOUT=200s } (3)如果在指定的时间内还没有serviceDoneExecutingLocked()方法将消息remove掉,就会调用ActiveServices. serviceTimeout()方法 voidserviceTimeout(ProcessRecordproc){ ... finallongmaxTime=now- (proc.execServicesFg?SERVICE_TIMEOUT:SERVICE_BACKGROUND_TIMEOUT); ... //寻找运行超时的Service for(inti=proc.executingServices.size()-1;i>=0;i--){ ServiceRecordsr=proc.executingServices.valueAt(i); if(sr.executingStart<maxTime){ timeout=sr; break; } ... } ... //判断执行Service超时的进程是否在最近运行进程列表,如果不在,则忽略这个ANR if(timeout!=null&&mAm.mLruProcesses.contains(proc)){ anrMessage="executingservice"+timeout.shortName; } ... if(anrMessage!=null){ //当存在timeout的service,则执行appNotResponding,报告ANR mAm.appNotResponding(proc,null,null,false,anrMessage); } } (4)Service onCreate超时监测整体流程如下图 在onCreate生命周期开始执行前,启动超时监测,如果在指定的时间onCreate没有执行完毕(该该方法中执行耗时任务),就会调用ActiveServices.serviceTimeout()方法报告ANR;如果在指定的时间内onCreate执行完毕,那么就会调用ActivityManagerService.serviceDoneExecutingLocked()方法移除SERVICE_TIMEOUT_MSG消息,说明Service.onCreate方法没有发生ANR,Service是由AMS调度,利用Handler和Looper,设计了一个TIMEOUT消息交由AMS线程来处理,整个超时机制的实现都是在Java层;以上就是Service超时监测的整体流程。 输入事件超时监测 应用程序可以接收输入事件(按键、触屏、轨迹球等),当5秒内没有处理完毕时,则会引发ANR。 这里先把问题抛出来了: 输入事件经历了一些什么工序才能被派发到应用的界面? 如何检测到输入时间处理超时? 1.Android输入系统简介 Android输入系统总体流程与参与者如下图所示。 简单来说,内核将原始事件写入到设备节点中,InputReader在其线程循环中不断地从EventHub中抽取原始输入事件,进行加工处理后将加工所得的事件放入InputDispatcher的派发发队列中。InputDispatcher则在其线程循环中将派发队列中的事件取出,查找合适的窗口,将事件写入到窗口的事件接收管道中。窗口事件接收线程的Looper从管道中将事件取出,交由窗口事件处理函数进行事件响应。关键流程有:原始输入事件的读取与加工;输入事件的派发;输入事件的发送、接收与反馈。其中输入事件派发是指InputDispatcher不断的从派发队列取出事件、寻找合适的窗口进行发送的过程,输入事件的发送是InputDispatcher通过Connection对象将事件发送给窗口的过程。 InputDispatcher与窗口之间的跨进程通信主要通过InputChannel来完成。在InputDispatcher与窗口通过InputChannel建立连接之后,就可以进行事件的发送、接收与反馈;输入事件的发送和接收主要流程如图所示: 其中,将输入事件注入派发队列后,会唤醒派发线程,派发线程循环由InputDispatcher.dispatchOnce函数完成;InputDispatcher将事件以InputMessage写入InputChannel之后,窗口端的looper被唤醒,进而执行NativeInputReceiver::handleEvent()开始输入事件的接收,从InputEventReceiver开始输入事件被派发到用户界面;以上只是输入事件的大致流程,更详细的流程可以参考相关资料;在了解输入系统的大致流程之后,我们来分析输入事件的超时监测机制。 2.输入事件超时监测 按键事件超时监测整体流程如下图所示 (1)InputDispatcher::dispatchOnceInnerLocked(): 根据事件类型选择不同事件的处理方法:InputDispatcher::dispatchKeyLocked()或者InputDispatcher::dispatchMotionLocked(),我们以按键事件超时监测为例进行说明; (2)findFocusedWindowTargetsLocked()方法会调用checkWindowReadyForMoreInputLocked();该方法检查窗口是否有能力再接收新的输入事件;可能会有一系列的场景阻碍事件的继续派发,相关场景有: 场景1: 窗口处于paused状态,不能处理输入事件 “Waiting because the [targetType] window is paused.” 场景2: 窗口还未向InputDispatcher注册,无法将事件派发到窗口 “Waiting because the [targetType] window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.” 场景3: 窗口和InputDispatcher的连接已经中断,即InputChannel不能正常工作 “Waiting because the [targetType] window’s input connection is [status]. The window may be in the process of being removed.” 场景4: InputChannel已经饱和,不能再处理新的事件 “Waiting because the [targetType] window’s input channel is full. Outbound queue length: %d. Wait queue length: %d.” 场景5: 对于按键类型(KeyEvent)的输入事件,需要等待上一个事件处理完毕 “Waiting to send key event because the [targetType] window has not finished processing all of the input events that were previously delivered to it. Outbound queue length: %d. Wait queue length: %d.” 场景6: 对于触摸类型(TouchEvent)的输入事件,可以立即派发到当前的窗口,因为TouchEvent都是发生在用户当前可见的窗口。但有一种情况, 如果当前应用由于队列有太多的输入事件等待派发,导致发生了ANR,那TouchEvent事件就需要排队等待派发。 “Waiting to send non-key event because the %s window has not finished processing certain input events that were delivered to it over %0.1fms ago. Wait queue length: %d. Wait queue head age: %0.1fms.” 以上这些场景就是我们常在日志中看到的ANR原因的打印。 (3)其中事件分发5s限制定义在InputDispatcher.cpp;InputDispatcher::handleTargetsNotReadyLocked()方法中如果事件5s之内还没有分发完毕,则调用InputDispatcher::onANRLocked()提示用户应用发生ANR; //默认分发超时间为5s constnsecs_tDEFAULT_INPUT_DISPATCHING_TIMEOUT=5000*1000000LL; int32_tInputDispatcher::handleTargetsNotReadyLocked(nsecs_tcurrentTime, constEventEntry*entry, constsp<InputApplicationHandle>&applicationHandle, constsp<InputWindowHandle>&windowHandle, nsecs_t*nextWakeupTime,constchar*reason){ //1.如果当前没有聚焦窗口,也没有聚焦的应用 if(applicationHandle==NULL&&windowHandle==NULL){ ... }else{ //2.有聚焦窗口或者有聚焦的应用 if(mInputTargetWaitCause!=INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY){ //获取等待的时间值 if(windowHandle!=NULL){ //存在聚焦窗口,DEFAULT_INPUT_DISPATCHING_TIMEOUT事件为5s timeout=windowHandle->getDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT); }elseif(applicationHandle!=NULL){ //存在聚焦应用,则获取聚焦应用的分发超时时间 timeout=applicationHandle->getDispatchingTimeout( DEFAULT_INPUT_DISPATCHING_TIMEOUT); }else{ //默认的分发超时时间为5s timeout=DEFAULT_INPUT_DISPATCHING_TIMEOUT; } } } //如果当前时间大于输入目标等待超时时间,即当超时5s时进入ANR处理流程 //currentTime就是系统的当前时间,mInputTargetWaitTimeoutTime是一个全局变量, if(currentTime>=mInputTargetWaitTimeoutTime){ //调用ANR处理流程 onANRLocked(currentTime,applicationHandle,windowHandle, entry->eventTime,mInputTargetWaitStartTime,reason); //返回需要等待处理 returnINPUT_EVENT_INJECTION_PENDING; } } (4)当应用主线程被卡住的事件,再点击该应用其它组件也是无响应,因为事件派发是串行的,上一个事件不处理完毕,不会处理下一个事件。 (5)Activity.onCreate执行耗时操作,不管用户如何操作都不会发生ANR,因为输入事件相关监听机制还没有建立起来;InputChannel通道还没有建立 这时是不会响应输入事件,InputDispatcher还不能事件发送到应用窗口,ANR监听机制也还没有建立,所以此时是不会报告ANR的。 (6)输入事件由InputDispatcher调度,待处理的输入事件都会进入队列中等待,设计了一个等待超时的判断,超时机制的实现在Native层。 以上就是输入事件ANR监测机制;具体逻辑请参考相关源码; ANR报告机制 无论哪种类型的ANR发生以后,最终都会调用 AppErrors.appNotResponding() 方法,所谓“殊途同归”。这个方法的职能就是向用户或开发者报告ANR发生了。 最终的表现形式是:弹出一个对话框,告诉用户当前某个程序无响应;输入一大堆与ANR相关的日志,便于开发者解决问题。 finalvoidappNotResponding(ProcessRecordapp,ActivityRecordactivity, ActivityRecordparent,booleanaboveSystem,finalStringannotation){ ... if(ActivityManagerService.MONITOR_CPU_USAGE){ //1.更新CPU使用信息。ANR的第一次CPU信息采样,采样数据会保存在mProcessStats这个变量中 mService.updateCpuStatsNow(); } //记录ANR到EventLog中 EventLog.writeEvent(EventLogTags.AM_ANR,app.userId,app.pid, app.processName,app.info.flags,annotation); //输出ANR到mainlog. StringBuilderinfo=newStringBuilder(); info.setLength(0); info.append("ANRin").append(app.processName); if(activity!=null&&activity.shortComponentName!=null){ info.append("(").append(activity.shortComponentName).append(")"); } info.append("\n"); info.append("PID:").append(app.pid).append("\n"); if(annotation!=null){ info.append("Reason:").append(annotation).append("\n"); } if(parent!=null&&parent!=activity){ info.append("Parent:").append(parent.shortComponentName).append("\n"); } //3.打印调用栈。具体实现由dumpStackTraces()函数完成 FiletracesFile=ActivityManagerService.dumpStackTraces( true,firstPids, (isSilentANR)?null:processCpuTracker, (isSilentANR)?null:lastPids, nativePids); StringcpuInfo=null; //MONITOR_CPU_USAGE默认为true if(ActivityManagerService.MONITOR_CPU_USAGE){ //4.更新CPU使用信息。ANR的第二次CPU使用信息采样。两次采样的数据分别对应ANR发生前后的CPU使用情况 mService.updateCpuStatsNow(); synchronized(mService.mProcessCpuTracker){ //输出ANR发生前一段时间内各个进程的CPU使用情况 cpuInfo=mService.mProcessCpuTracker.printCurrentState(anrTime); } //输出CPU负载 info.append(processCpuTracker.printCurrentLoad()); info.append(cpuInfo); } //输出ANR发生后一段时间内各个进程的CPU使用率 info.append(processCpuTracker.printCurrentState(anrTime)); //会打印发生ANR的原因,如输入事件导致ANR的不同场景 Slog.e(TAG,info.toString()); if(tracesFile==null){ //Thereisnotracefile,sodump(only)theallegedculprit'sthreadstothelog //发送signal3(SIGNAL_QUIT)来dump栈信息 Process.sendSignal(app.pid,Process.SIGNAL_QUIT); } //将anr信息同时输出到DropBox mService.addErrorToDropBox("anr",app,app.processName,activity,parent,annotation, cpuInfo,tracesFile,null); //BringuptheinfamousAppNotRespondingdialog //5.显示ANR对话框。抛出SHOW_NOT_RESPONDING_MSG消息, //AMS.MainHandler会处理这条消息,显示AppNotRespondingDialog对话框提示用户发生ANR Messagemsg=Message.obtain(); HashMap<String,Object>map=newHashMap<String,Object>(); msg.what=ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj=map; msg.arg1=aboveSystem?1:0; map.put("app",app); if(activity!=null){ map.put("activity",activity); } mService.mUiHandler.sendMessage(msg); } } 除了主体逻辑,发生ANR时还会输出各种类别的日志:event log:通过检索”am_anr”关键字,可以找到发生ANR的应用main log:通过检索”ANR in “关键字,可以找到ANR的信息,日志的上下文会包含CPU的使用情况dropbox:通过检索”anr”类型,可以找到ANR的信息traces:发生ANR时,各进程的函数调用栈信息 至此ANR相关报告已经完成,后续需要分析ANR问题,分析ANR往往是从main log中的CPU使用情况和traces中的函数调用栈开始。所以,更新CPU的使用信息updateCpuStatsNow()方法和打印函数栈dumpStackTraces()方法,是系统报告ANR问题关键所在,具体分析ANR问题请参考相关资料。 总结 1.ANR的监测机制:首先分析Service和输入事件大致工作流程,然后从Service,InputEvent两种不同的ANR监测机制的源码实现开始,分析了Android如何发现各类ANR。在启动服务、输入事件分发时,植入超时检测,用于发现ANR。 2.ANR的报告机制:分析Android如何输出ANR日志。当ANR被发现后,两个很重要的日志输出是:CPU使用情况和进程的函数调用栈,这两类日志是我们解决ANR问题的利器。 3.监测ANR的核心原理是消息调度和超时处理。 4. 只有被ANR监测的场景才会有ANR报告以及ANR提示框。 附录 Android高级技术大纲,以及系统进阶视频; Android高级技术大纲 Android高级进阶视频资料 获取方式; 加Android进阶群;701740775。即可前往免费领取。免费备注一下csdn

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册