从Invalidate到Draw的辗转历程

以android Q的源码为例子,一步步分析invalidate的调用流程。

在写自定义控件的时候时常会用到 invalide 方法,都知道调用 invalide 后会回调Draw方法,但其中的辗转历程鲜有人知,为了搞清这个流程,我从android Q 源码入手分析了整个调用流程。在此记录一下,省得以后忘了

1.App Call SufaceFlinger

1.任何的View的invalidate方法最终都会调用到ViewRootImpl.java的invalidate()或者invalidateChildInParent,区别只是mDirty的大小,这个mDirty表示这个区域需要重绘

frameworks/base/core/java/android/view/ViewRootImpl.java

2.scheduleTraversals的实现如下

frameworks/base/core/java/android/view/ViewRootImpl.java

3.这里需要注意的是一个插入同步屏障的过程,插入同步屏障后,只有异步的消息才能在MessageQeue循环,app这边 Handler之类的全部失去效果,所以在Traversals执行或者取消的时候会马上移除,如下所示 查看更多

从最上层到驱动层看Android Conetext.getSystemService究竟做了什么

曾经好奇为什么同样是binder接口,Conetext.getService为什么可以直接获取,而不需要像bindeService一样异步回调,今天终于从能够从上层源码到binder驱动源码,彻底了解这一过程,同时分析binder接口在调用的时候,底层究竟是如何实现的。

1.从getSystemService 到native的getContextObject

ContextImpl.java

SystemServiceRegistry.java

SYSTEM_SERVICE_FETCHERS是一个hasmap,键值是一个抽象类,ServiceFetcher,它在初始化的时候实现,见如下代码

registerService 的作用就是把CachedServiceFetcher 放到 SYSTEM_SERVICE_FETCHERS 中,这样每次取服务时都可以从这个缓存获取,因此我们第二次getSystemService时性能是非常高的,时间复杂度位O(1),然后所有系统服务端最终都会调用到这一步 查看更多

覆盖补给-树形dp

已知有一片呈二叉树的道路,我们要在道路上的一些节点设置补给站支援。

补给站可以设置在任意节点上,每个补给站可以使距离自身小于等于 1 个单位的节点获得补给。

若要使道路的所有节点均能获得补给,请返回所需设置的补给站最少数量。

示例 1:

示例 2:


提示:

  • 节点的数量范围为 [1, 1000]
  • 每个节点的值均为 0 。

Android如何优雅的弹出键盘

在很多app中,为了避免ui样式被键盘顶起,从而会把主内容的编辑功能单独摘一个发布器,作为用户的编辑界面,如快手抖音的评论。如下图所示

因此作为发布器必须第一时间弹出输入法,但是过早的调用InputMethodManager.showSoftInput 是无法弹出键盘的,任何的showflag都是一样的,

网上大部分人告诉我们答案,只要将showSoftInput 进行延迟展示即可,更有离谱的告诉你监听draw,监听layout后再调用等

然而这些方法都是不可靠的,监听draw监听layout或者进行delay的话,你会发现换个手机就不生效了,要么就要继续把delay 时间延长保证线上的可靠性,不同手机的延长时间是不一样的。。。。 查看更多

Android 特定情况requestlayout 一直无效分析

问题的发现

在做一次项目重构后,发现偶现ViewPager页面出现滑动到一半停住,继续触摸继续滑动,连续滑动后,ViewPager不会有新的页面创建,所有页面都滑到了屏幕之外。

问题分析

既然问题出现在ViewPager,那么当然是从ViewPager的代码开始分析了,既然Viewpager可以滑动,但是松手的时候一切没有触发新页面的加载,于是我怀疑是Viewpager的populate(int item)方法有问题。

经过断点分析,发现这段逻辑完全正常,松手后,确实完美触发了populate 方法,index同样也是对的,那么为什么没有触发滚动动画和页面加载呢。

重新增加断点到Viewpager adapter里的View创建里,发现正常情况下,创建页面的调用栈是从View的onlayout触发的,而异常情况下,没有触发onLyaout。 查看更多

备忘

查找当前目录下的所有字符串:

grep -n “xxxxxx” -r ./

多个字符串或匹配

grep -n -E “xxxx1|xxxx2” -r ./

一个dexfile解析库

最近同事在研究自定义解释器加壳的的能力,简单的说就是,自己实现解释器来执行加密的字节码,从而让破解的人无法从内存中dump到完整的dex文件或者永远是错误的dex,加大了破解难度。

由于在Github上没找到合适的c/c++实现的 dex解析库,只好徒手从art虚拟机中扒出来。

这个dexfile只是从art 从抽出的代码,可以使用jni调用,没有封装好的jni接口。需要自己参考dumpdex里的代码或者了解dexfile里的实现才能用。

通过这个库可以获得dex file里的method field 以及各个method的opcode和oprand,如果想自己实现dex虚拟机玩玩可以试试

使用的ndk版本的是r21 cmak3.14 查看更多

Android 硬件和软件绘制的一些差异分析

1.从简单的现象出发

当布局关系如下图所示时,其中Child1和Child2有重叠

Applictaion关闭硬件绘制

使用软件绘制时,调用invalidate时候可以发现兄弟 Child2也进行了绘制,ParentLayout1的兄弟节点ParentLayout2 虽然执行了dispatchDraw,但是子控件并没有重绘

Applictation打开硬件绘制时

使用硬件绘制时,调用invalidate可以发现仅仅是Child1进行绘制但是Child2没有进行绘制

从上面的现象可以发现,硬件绘制时,即使view有重叠,在刷新ui的时候也可以保持只绘制一个,而且不管是软件还是硬件,父亲的兄弟节点之后的view也不需要重绘,这表明一个问题,使用硬件绘制和软件绘制的时候,方法的调用流程似乎是不一样的。 查看更多

VM之从字节码看vm的实现差异

1.基于栈实现,以JVM为例

java源文件

执行命令:得到字节码信息

从上面可以看到main方法由8条指令组成,一般称他为opcode,注意这个opcode并不是汇编语言,而是jvm可以识别的语言,可以说汇编之于Native等于opcode之于jvm

执行分析:

iconst_1 :将1放入操作数栈顶部(此时操作数栈深度为1)

istore_1: 从栈顶取出值,赋值给变量1 (此时操作数栈深度为0)

iconst_2 :将2放入操作数栈顶部(此时操作数栈深度为1)

istore_2:从栈顶取出值,赋值给变量2 (此时操作数栈深度为0)

iload_1,iload_2: 从变量1和变量2加载值,放到操作数栈( 此时操作数栈深度为2)

iadd:把栈顶的两个int值取出并且相加,结果放在栈顶 (此时操作数栈深度为1) 查看更多

ActivityThead.mNewActivities导致的内存泄漏

昨天有测试报告说这个版本的应用在重复跑测试case的时候内存不断变大

由于测试case会重复启动activity首先就怀疑是不是activitty导致的内存泄漏

Adb shell dumpsys meminfo [packagename]

通过dump 信息,发现activity的实例确实在不断变多,但查阅代码并未发现任何持有引用的地方

android studio profile来 dump内存信息发现最短的引用路径来自android os. ActivityThead

开始有点不信,不觉得系统的引用的会导致内存不断增加,然后再次使用MAT分析

发现确实来自ActivityThead.mNewActivities

源码分析

通关简单分析发现mNewActivities 是一个链表

搜索关键字发现仅仅只在ActivityThead.Idler这个私有类里进行了引用释放,这个

MessageQueue.IdleHandler 这是一个接口,查阅注释发现这个接口仅仅只有当前线程 MessageQueue 空闲之后才会回调 查看更多

悬浮窗开发的总结

1、动画和点击区域问题

可以使用hide方法viewTreeObserver下的方法进行窗口裁剪实现局部透过点击事件,这样在动画的时候就不需要考虑进行window大小变化了

2、 启动慢的问题

1)由于悬浮窗启动一般使用的是service启动,通过分析ams可以发现,在进行activity启动的是有进行powehint操作的,而一般的service是没有的,如果系统是自家的可以考虑在启动自己的服务的时候加上powerhint操作

2)使用startforgroundService的方式提高进程的优先级

3) 考虑使用异步layoutinfler来实现在applictaion创建时候就着手准备view

3、偶现UI不刷新,但没有anr

这个问题,赶紧查查进程的OOM_ADJ,大概率是优先级过低,得不到系统的资源调度问题 查看更多