東川印記

一本東川,笑看爭龍斗虎;寰茫兦者,度橫佰昧人生。

壬寅入伏小记

2022年6月15日星期三



值壬寅入伏之际,当疫情绵延之时

山中度假一月有余,此间返城继续

要务姑息国本,度日累年经营

人生在世不思议,只堪黄闷如雨

1,生命周期

https://senrsl.blogspot.com/2022/05/android-lifecycle.html

2,数据结构

https://senrsl.blogspot.com/2019/05/yhanjp2.html  #4

数组Array、栈Stack、队列Queue、链表Linked List、树Tree、散列表 HashTable、堆Heap、图Graph;

3,常用设计模式

https://senrsl.blogspot.com/2019/05/yhanjp2.html  #5

4,时间复杂度

常量阶 Constant
O(1) 无论数据增大多少,耗时/耗空间都不变
哈希算法
线性阶 Linear
O(n)
数据量增大几倍,耗时也增大几倍 遍历算法找最大数
n方阶 O(nⁿ) 数据量增大n倍时,耗时增大n的n次方倍
冒泡排序为平方阶O(n^2)
对数阶  Logarithmic
O(log n) 数据增大n倍时,耗时增加log n倍(log默认以2为底)
O(log n) 二分查找
线性对数阶 O(n log n) 就是n 乘以 log n
归并排序
指数阶 O(2ⁿ)

阶乘阶 O(n!)   

 

5,简单算法

冒泡排序 O(n^2) 从小到大,相邻排序。

 选择排序、插入排序、快速排序、希尔排序、归并排序、堆排序。


稳定排序: 冒泡、插入、归并;

非稳定排序: 选择、希尔、堆、快速。


6,Handler模式

https://senrsl.blogspot.com/2019/05/blog-post.html #5

1)handler调用sendMessage发送消息,方法中获取当前进程的MessageQueue,然后将当前消息的msg.target指向当前handler,入队进MessageQueue队列,入队方法使用加锁synchronized保护。

2)MessageQueue中,所有Message都是以链表形式保存,第一个元素保存在变量MessageQueue文件Message变量。

3)Looper中,使用static final ThreadLocal<Looper>变量来保存及获取当前线程Looper。

4)Looper.loop()方法从ThreadLocal中获取当前线程的消息队列MessageQueue,然后循环读取此消息队列,读取到Message后,调用msg.target(即Handler).dispatchMessage(msg)进行分发。


7,事件分发

简略app端 https://senrsl.blogspot.com/2019/05/blog-post.html #8

详细 https://senrsl.blogspot.com/2019/05/androiddispatchtouchevent.html

1)触摸屏幕时,ActivityThread中的Looper从MessageQueue中looper了一条Message,通过InputEventReceiver管道给他的子类WindowInputEventReciver;

2) ViewRootImpl入队输入事件,责任链子类ViewPostImeInputStage消耗此事件,传递给DecorView父类即View.dispatchTouchEvent(event),回调给activity.dispatchTouchEvent(ev)方法;

3)activity会判断是否有用户交互,然后分发给ViewGroup.dispatchTouchEvent,这里会判断是否做拦截和取消,如果没有,会遍历所有子View,找到点击的子View,如果找到了就子View.dispathcTouchEvent(),没找到就return 父View.dispatchTouchEvent;

4)如果找到点击的子View,会判断是否存在 拖动、用户实现onTouch()接口、或者View.onTouchEvent(ev)方法。

5)View.onTouchEvent(ev)方法会判断长按或点击,分发到了具体的事件。

6)如果没有分发到具体事件,就return false销毁。


8,View绘制

https://senrsl.blogspot.com/2022/06/androidview.html


9,Activity启动流程

https://senrsl.blogspot.com/2019/05/yhanjp2.html  #6

一种是Launcher2启动,一种是应用内startActivity()启动。

但最终都是 startActivity()启动。

Launcher.startActivitySafely()

-> Activity.startActivity() -> startActivityForResult()

                                      -> Instrumentation.execStartActivity()

                                      -> ActivityManager.getService.startActivity()

                                      -> AMS.startActivity()

                                       -> AMS.startActivityAsUser()

                                      -> ActivityStartController.obtainStarter()

                                      -> ActivityStarter.execute()

                                      -> ActivityStarter.startActivity()

                                      -> ActivityStarter.startActivityUnchecked()

                                      -> RootActivityContainer.resumeFocusedStatcksTopActivities()

                                      -> ActivityStack.resumeTopActivityUncheckedLocked()

                                      -> ActivityStatckSupervisor.realStartActivityLocked()

                                      -> AMS.startProcessLocked()

                                      -> LaunchActivityItem.exec()

                                      -> ActivityThread.handleLaunchActivity()

                                      -> Instrumentation.callActivityOnCreate()

                                      -> Activity.performCreate()

                                      -> Activity.onCreate()

点击Launcher图标,Launcher进程会向AMS发送启动信息(启动信息定义在AndroidManifest.xml中<intent-filter/>标签,由PMS解析);

AMS收到信息后会经ActivityTaskManangerService->ActivityStartController->ActivityStarter,存储信息到Request,并通知Launcher进程让Activity休眠,Launcher进程的ActivityThread收到消息后调用handlePauseActiviy()暂停,并通知AMS已经暂停。

AMS收到已暂停后,会检查要启动的Activity所在进程是否已经启动,如果已经启动了就打开,如果没有启动就Process.start(ActivityThread)来启动新的进程。

进程创建后,调用ActivityThread.main(),初始化MainLooper,创建Application对象,调用Instrumentation.callApplicationOnCreate()来执行Application.onCreate(),然后通知AMS进程已启动。

AMS收到进程启动的消息后,从ActivityTaskManangerService中取出Activity启动信息,传到ActivityThread.sendMessage(),通过H发送Handler消息,最终调用performLauncherActivity()关联activity和context,然后通过Instrumentation.callActivityOnCreate()->Activity.performCreate()->Activity.onCreate()回调到Activity生命周期。


整个过程涉及四个进程和三次IPC调用,四个进程为

1)Launcher进程

    点击图标生成intent,使用Binder通知AMS启动Activity。

2)SystemServer进程

    生成ActivityStarter准备启动Activity;

    解析intent,解析出ActivityInfo对象,计算flags,处理LaunchMode,判断目标栈,将Activity入栈,并将任务栈移到前台,使Activity可见;

    创建事务并通过Binder调用发送到目标App进程等,如果APP进程不存在,通过Socket通知Zygote进程启动App进程。

3)Zygote进程

    接收SystemServer消息通过Fork自身来创建App进程。

4)应用进程

    接收SystemServer传递的事务并执行,反射创建Activity对象,为Activity创建Context、Window等;

    回调onCreate等生命周期。


三次IPC为两次Binder和一次Socket。


涉及类

ActivityThread:整个 App main 方法所在的类,所谓主线程就是指它所执行的线程,所以通常会称其为主线程。它负责管理应用程序进程的主线程的执行,处理 ATMS 的调度和执行Activity,Broadcast及相关操作。
ApplicationThread:它是 ActivityThread 的一个内部类,继承自 IApplicationThread.Stub,是一个 IBinder。ActivityThread 通过它来进行 IPC 调用,与 SystemServer 通信。
Intrumentation:用于监控应用程序与系统交互,启动 Activity 或者调用 Activity、Application 的生命周期都需要经过它的处理。
ActivityTaskManager:Activity,Service 等与 ATMS 跨进程交互的接口,ATMS的辅助类。后文简称为 ATM。
ActivityTaskManagerService:管理 Activity 和它的回退栈、任务栈的系统服务,Android 10以前是 AMS 的工作,Android 10中将部分工作从 AMS 抽离为 ATMS。后文将简称为 ATMS。
ActivityRecord:system_server进程中用来描述Activity的数据类型。ActivityRecord中存储了Activity的信息,如所在的进程名称,应用的包名,所在的任务栈的taskAffinity等,与ActivityClientRecord、Activity的是一一对应关系。
ActivityClientRecord:App进程中用来描述Activity的数据类型。
TaskRecord:表示一个任务栈,记录了Activity启动的先后顺序,栈中严格按照先进后出的特性维护一系列ActivityRecord。
ActivityStack:负责维护TaskRecord,内部保存了当前Stack中所有的Task列表。
ActivityDisplay:管理所有ActivityStack,表示当前屏幕的抽象,内部维护了mHomeStack、mStacks、mPreferredToFocusableStack和mLastFocusedStack等ActivityStack。
RootActivityContainer:Android10中新增的类,由ActivityStackSupervisor中抽离而来,其作为Activity容器的根节点,负责管理所有的ActivityDisplay。
ActivityStackSupervisor:Android系统中Activity的最大管家,通过持有RootActivityContainer来间接管理所有的Activity。
ClientLifecycleManager:组合多个客户端生命周期转换和请求,作为单个事务来执行。


10,打包流程

之前总结过,用的时候就不见了。。。。

旧版的官方介绍图,现在竟然官方都找不到了  androidBuidOld.png

新版的图 Android Build Process.png

1)使用AAPT(Android  Asset Package Tool)打包资源文件,生成R.java

2)  aidl生成.java

3) .java生成.class

4) .class混淆,然后dx工具生成 classes.dex

5)  apkbuilder工具打包生成apk

6)  jarsigner工具私钥签名

7)使用zipalign进行文件对齐

    对齐主要是将APK中所有资源文件起始偏移4字节的整倍数,这样通过内存映射访问时会更快,减少内存使用。


11,AMS,PMS,WMS


12,IBinder

Linux进程隔离,进程隔离使用了虚拟内存技术,即 用户空间及内核空间,虚拟内存映射物理内存。

用户空间调用内核空间,进入内核态,进行读写磁盘、分配回收内存、网络读写数据等。

内存映射mmap()函数,本质是进程虚拟内存映射方法。可以将一个文件、一段物理内存或对象映射到进程的虚拟内存地址空间,然后采用指针方式操作,正因如此,就不用调用read/write等系统函数了。映射成功后,两个空间各自的修改能直接反映到映射的内存区域,从而被对方空间即时感知,从而提供进程间通信的支持,从而实现了一次拷贝。

Binder驱动是一种虚拟字符设备,注册在 /dev/binder中,其定义了Binder通信协议,负责建立进程间Binder通信。

这个讲的全 my.oschina.net/u/3897543/blog/4901658


13,JNI通信

Java Native Interface   jni.png

14,UML建模基础

用例图、类图、时序图、对象图、协作图、活动图、状态图、组件图、配置图

https://senrsl.blogspot.com/2019/03/uml.html


15,音视频编解码

Android mediaCodec流程 https://senrsl.blogspot.com/2021/01/android-rtmp04-androidmediacodecmp4.html

Exoplayer 基本流程 https://senrsl.blogspot.com/2021/05/exoplayer01.html

Android音频架构 https://senrsl.blogspot.com/2021/05/exoplayer08-android-hal.html


16,性能优化

1)apk包瘦身

    资源优化:删除冗余资源、重复资源复用、图片压缩、资源混淆、语言包等;

    so库优化;

    代码资源优化等;

2)启动速度 

    冷启动、热启动

    耗时操作异步、延迟初始化、线程优化等;

3)运行稳定性

    CRASH:Java层拦截、Native层注册信号处理函数;

    ANR(Application Not Respose):避免在主线程做耗时操作

        a) 5秒内无法响应屏幕触摸或键盘输入;

        b)前台广播10秒,后台广播60秒内未处理完成;

        c)前台服务20秒,后台服务200秒内未执行完成;

        d)ContentProvider的publish在10秒内未执行完成;

4) 内存

    内存泄漏;

    内存溢出。

5) 操作流畅度,卡顿问题

    在界面绘制过程中,CPU主要计算屏幕View对应图形和向量等信息,GPU主要把CPU计算出的图形栅格化并转化为位图。

    减少CPU计算任务,如逻辑运算、文件读写、解析大量图片、频繁请求网络、复杂布局、频繁创建对象等。

    View绘制,需要达到 1000ms/60fps=16.667ms每帧。

    导致卡顿的情况

            a) SurfaceFlinger主线程耗时;

            b) 后台活动进程太多导致系统繁忙;

            c) 主线程调度不到,调度器迟迟不能对齐调度;

            d) system_server的AMS锁和WMS锁;

            e) Layer过多导致SufaceFlinger Layer Compute耗时;

            f)主线程执行操作超时导致卡顿;

            g) Activity resume时与AMS通信要持有AMS锁,如果后台繁忙,就会卡顿:

            h) webview性能不足;

            i) 帧率与刷新率不匹配,如120HZ的屏幕搭配60fps的动画;

    卡顿检测,可以用dumpsys或systrace工具。


17,hashmap解决hash碰撞

hashmap 存储为 数组存key,链表存value;

key的hash相同时,会把新value追加在链表后面,get()时遍历链表取出key对应的value.

链表长度大于8时,用红黑树。

18,最大堆最小堆

二叉树中的两种形式。

堆:堆可以被看成是一棵树,如堆排序;

栈:一种先入后出的数据结构。

最大堆:根节点的键值是所有堆结点键值中的最大者,且每个结点的值都比其孩子大。

最小堆:反之。

2022年06月15日17:18:21

--
senRsl
2022年05月28日11:32:11

没有评论 :

发表评论