Android 内核基本知识
Android基本知识
Android基本知识.... 1
1. 各版本系统特性.... 1
2. View绘制流程.... 2
3. 动画体系.... 2
4. 事件分发机制.... 3
输入消息获取.... 3
1. 按键消息总体派发流程.... 3
根视图内部派发流程.... 4
触摸消息派发流程.... 4
根视图内部消息派发流程.... 4
ViewGroup内部消息派发过程.... 4
View内消息检测机制.... 4
5. 常见UI 布局优化方式.... 4
6. 常见性能优化方式.... 5
7. 网络通信机制及访问策略,TCP/IP、 HTTP协议.... 6
8. Android消息机制, 多线程.... 7
9. 熟悉SQL,常见本地化存储的优化.... 8
10. JNI 和 NDK.. 8
11. HTML5 和 JS. 8
13. Java Server端.... 8
14. Linux & Kenel 8
15. Framework概述.... 9
1. 服务端.... 9
2. 客户端.... 9
3. Linux驱动.... 9
Apk的整个运行过程.... 9
客户端中的线程.... 10
1. 各版本系统特性
Android 5.0十大新特性 1、全新 Material Design 设计风格 2、支持多种设备(智能手机、平板电脑、笔记本电脑、智能电视、汽车、智能手表) 3、全新的通知中心设计 SystemUI Notification 4、支持 64 位 ART 虚拟机(放弃Dalvik虚拟机,改用了ART模式) 5、Project Volta 电池续航改进计划 6、全新的“最近应用程序” 7、改进安全性,防止绕过锁屏, 开启系统数据加密 8、不同数据独立保存 Knox多用户 9、改进搜索(Google Search 将会更好的意识到用户正在做什么) 10、新的 API 支持,蓝牙 4.1、USB Audio、多人分享等其它特性
Android 4.4系统新特性 1. 新的拨号和智能来电显示 2. 针对RAM的优化 3. 新图标、锁屏、启动动画和配色方案 4. 加强主动式语音功能 5. 集成Hangouts IM软件 6. 全屏模式 7. 支持Emoji键盘 8. 轻松访问在线存储 9. 无线打印 10. 屏幕录像功能 11. 内置字幕管理功能 12. 计步器应用 13. 低功耗音频和定位模式 14. 新的接触式支付系统(通过NFC和智能卡) 15. 新的蓝牙配置文件和红外兼容性 |
2. View绘制流程
1. View的状态
2. 导致View重绘的因素 视图或子视图: 大小 有变化,位置有变化, 可见性 有变化 这三种变化 最终都会调用 invalidate()、requestLayout()、requestFocus()这三个方法 这三个方法 最终调用 ViewRoot的 schueduleTraversals(),该函数发一个异步消息 调用 performTraversals() 开始对整个View重新遍历
流程一:mesarue()过程
主要作用:为整个View树计算实际的大小,即设置实际的高(对应属性:mMeasuredHeight)和宽(对应属性: mMeasureWidth),每个View的控件的实际宽高都是由父视图和本身视图决定的。
具体的调用链如下: ViewRoot根对象地属性mView(其类型一般为ViewGroup类型)调用measure()方法去计算View树的大小,回调 View/ViewGroup对象的onMeasure()方法,该方法实现的功能如下: 1、设置本View视图的最终大小,该功能的实现通过调用setMeasuredDimension()方法去设置实际的高(对应属性: mMeasuredHeight)和宽(对应属性:mMeasureWidth) ; 2 、如果该View对象是个ViewGroup类型,需要重写该onMeasure()方法,对其子视图进行遍历的measure()过程。
2.1 对每个子视图的measure()过程,是通过调用父类ViewGroup.java类里的measureChildWithMargins()方法去 实现,该方法内部只是简单地调用了View对象的measure()方法。(由于measureChildWithMargins()方法只是一个过渡 层更简单的做法是直接调用View对象的measure()方法)。
流程二、layout布局过程:
主要作用 :为将整个根据子视图的大小以及布局参数将View树放到合适的位置上。 具体的调用链如下: host.layout()开始View树的布局,继而回调给View/ViewGroup类中的layout()方法。具体流程如下 1 、layout方法会设置该View视图位于父视图的坐标轴,即mLeft,mTop,mLeft,mBottom(调用setFrame()函数去实现) 接下来回调onLayout()方法(如果该View是ViewGroup对象,需要实现该方法,对每个子视图进行布局) ; 2、如果该View是个ViewGroup类型,需要遍历每个子视图chiildView,调用该子视图的layout()方法去设置它的坐标值。 layout函数原型为 ,位于View.java 流程三、draw()绘图过程 由ViewRoot对象的performTraversals()方法调用draw()方法发起绘制该View树,值得注意的是每次发起绘图时,并不 会重新绘制每个View树的视图,而只会重新绘制那些“需要重绘”的视图,View类内部变量包含了一个标志位DRAWN,当该视图需要重绘时,就会为该View添加该标志位。 调用流程 : mView.draw()开始绘制,draw()方法实现的功能如下: 1 、绘制该View的背景 2 、为显示渐变框做一些准备操作(见5,大多数情况下,不需要改渐变框) 3、调用onDraw()方法绘制视图本身 (每个View都需要重载该方法,ViewGroup不需要实现该方法) 4、调用dispatchDraw()方法绘制子视图(如果该View类型不为ViewGroup,即不包含子视图,不需要重载该方法) 值得说明的是,ViewGroup类已经为我们重写了dispatchDraw ()的功能实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。 4.1 dispatchDraw()方法内部会遍历每个子视图,调用drawChild()去重新回调每个子视图的draw()方法(注意,这个 地方“需要重绘”的视图才会调用draw()方法)。值得说明的是,ViewGroup类已经为我们重写了dispatchDraw()的功能 实现,应用程序一般不需要重写该方法,但可以重载父类函数实现具体的功能。
invalidate()方法 :
说明:请求重绘View树,即draw()过程,假如视图发生大小没有变化就不会调用layout()过程,并且只绘制那些“需要重绘的” 视图,即谁(View的话,只绘制该View ;ViewGroup,则绘制整个ViewGroup)请求invalidate()方法,就绘制该视图。
一般引起invalidate()操作的函数如下: 1、直接调用invalidate()方法,请求重新draw(),但只会绘制调用者本身。 2、setSelection()方法 :请求重新draw(),但只会绘制调用者本身。 3、setVisibility()方法 : 当View可视状态在INVISIBLE转换VISIBLE时,会间接调用invalidate()方法,继而绘制该View。 4 、setEnabled()方法 : 请求重新draw(),但不会重新绘制任何视图包括该调用者本身。
requestLayout()方法 :会导致调用measure()过程 和 layout()过程 。
说明:只是对View树重新布局layout过程包括measure()和layout()过程,不会调用draw()过程,但不会重新绘制 任何视图包括该调用者本身。 一般引起invalidate()操作的函数如下: 1、setVisibility()方法: 当View的可视状态在INVISIBLE/ VISIBLE 转换为GONE状态时,会间接调用requestLayout() 和invalidate方法。同时,由于整个个View树大小发生了变化,会请求measure()过程以及draw()过程,同样地,只绘制需要“重新绘制”的视图。
requestFocus()函数说明: 说明:请求View树的draw()过程,但只绘制“需要重绘”的视图。
|
3. 动画体系
4. 事件分发机制
输入消息获取
InputReader和InputDispatcher 运行在系统进程system_process中 InputReader 持续调用 输入设备驱动 InputDispatcher 从消息队列中获取消息, ① 直接派发到 客户端窗口 ② 派发送WmS,WmS经过一定的处理。若WmS没有处理,再派发到客户端窗口
App创建Window时, 本地创建ViewRoot对象,再通过IPC调用WmS的Session addWindow()。WmS会把Window的相关信息保存在InputMonitor中;再用InputManager把Window信息传递给InputDispatcher,同时保存在NativeInputManager中。
|
事件分发机制 窗口内部的默认处理逻辑,叫做 View系统 消息分类: 触摸消息,按键消息
1. 按键消息总体派发流程ViewRoot中定义了InputHandler对象,handleKey() -> dispatchKey() 发送一个异步DISPATCH_KEY消息,在deliverKeyEvent() 中处理: (1)调用 mView.dispatchKeyEventPreIme(), 在输入法窗口之前处理该事件, 需要重载该函数,若返回true,则先调用finishInputEvent() 报告WmS (2)若输入法窗口存在,把消息 派发到 输入法窗口 (3)调用deliverKeyEventToViewHierarchy(),将消息派发给真正的视图 ① checkForLeavingTouchModeAndConsume(),是否离开触摸模式 ② mView.dispatchKeyEvent()发给根视图,进入View树 应用程序,根视图 即 DecorView 非应用程序,根视图 ViewGroup的实现 ③ 若App没有处理该消息, 则默认去判断该消息对View树焦点的影响
根视图内部派发流程① 回调Activity包含的Window对象 ② 将消息派发给 DecorView ③ View内部的 onKeyDown 和 onKeyUp ④ Activity的 onKeyDown 和 onKeyUp ⑤ 按键消息没有被消耗掉,调PhoneWindow的 onKeyDown 和 onKeyUp
2. 触摸消息派发流程按键消息 要先经过WmS的处理再发送给客户端窗口 触摸消息 则直接派发到客户端窗口 (1) 物理屏幕像素 到 系统定义的逻辑像素的 转换 (2) 如果是DOWN,调用ensureTouchMode(true) 进入触摸模式 (3) 屏幕坐标 转化为 视图坐标。对于根视图X轴没有滚动,宽度为屏幕宽度 (4) mView.dispatchTouchEvent() 将消息 派发到 根视图 以及 整个View树 应用窗口: 根视图 == DecorView 非应用窗口: 根视图 == ViewGroup (5) 视图 没有消耗掉消息, 处理屏幕边界偏移
根视图内部消息派发流程① mView.dispatchTouchEvent() 调用Window对象PhoneWindow的superDispatchTouchEvent() –> DecorView的superDispatchTouchEvent -> DecorView中没有Callback则:ViewGroup的 dispatchTouchEvent ② 若在PhoneWindow中没有被消耗,调用onTouchEvent ViewGroup内部消息派发过程采用递归方式,首先 把消息 派发给View树中的最后一个子视图 若子视图没有消耗掉消息,才递归派发给父视图 View内消息检测机制三种检测: pre-pressed,pressed,长按 原理: 利用Handler发送异步延迟消息。根据触摸时间判断 检测过程在View的onTouchEvent中实现,如果我们重载了该函数,但没有调用super.onTouchEvent(),三种消息回调就不会执行。 也就是onLongClick不会被执行
|
5. 常见UI 布局优化方式
◆视图和布局优化 - 减少视图层级结构,重用layout代码,减少不必要的资源占用 http://www.open-open.com/lib/view/open1398175135593.html
1、RelativeLayout 优于 LinearLayout 针对大量Items时,通过Hierarchy Viewer查看两种布局方案的View层级图,RelativeLayout明显优于LinearLayout 2、UI资源的复用 <viewStub/>,<merge/>和<include/>
(1) <viewstub> 延迟加载。 viewstub是一个不可见的,大小为0的View,最佳用途就是实现View的延迟加载 被<viewStub/>标签所包裹的Views在默认状态下不会占用任何内存空间(visibility=GONE),通过方法inflate()来召唤系统加载其内部的Views。 (2) <merge/> :该标签在优化UI结构时起到很重要的作用,目的是通过删减多余或者额外的层级 <merge>也有一些使用限制: 只能用于xml layout文件的根元素 (3) <include> 重用layout代码 如果在某个布局里面需要用到另一个相同的布局设计,可以通过<include> 标签来重用layout代码: (4) layoutopt (Layout Optimization工具) 这工具可以分析所提供的Layout,并提供优化意见。在tools文件夹里面可以找到layoutopt.bat。
◆性能优化- 背景和图片优化,延迟加载 Drawing(绘制的刷新率) Startup Time (启动Activities的时间) getWindow().setBackgroundDrawable()方法来设置DecorView的background drawable 通过Theme定义Window’s background的图片,从而提高Activity的启动速度
◆Adapter优化: 对ListView优化就是对Adapter中的getView方法进行优化 留意getView里面的代码,要判断convertView是否为null,以便重用,减少对象的创建,减少内存占用 ◆内存分配优化 |
6. 常见性能优化方式
1.采用硬件加速,在androidmanifest.xml中application添加 android:hardwareAccelerated="true"。不过这个需要在android 3.0才可以使用。 2. View 中设置缓存属性. setDrawingCache为true. 3. 优化你的布局。通过Android sdk中tools目录下的layoutopt 命令查看你的布局是否需要优化。 4. 动态加载View. 采用ViewStub 避免一些不经常的视图长期握住引用. 5.将Acitivity 中的Window 的背景图设置为空。getWindow().setBackgroundDrawable(null); android的默认背景是不是为空。 6. 采用<merge> 优化布局层数。 采用<include >来共享布局。 7. 查看Heap 的大小 8. 利用TraceView查看跟踪函数调用。有的放矢的优化。 9. cursor 的使用。不过要注意管理好cursor,不要每次打开关闭cursor.因为打开关闭Cursor非常耗时。Cursor.require用于刷新cursor. 10.采用环形Buffer(可以采用链表数据结构实现)。可以设置一个链表长度的上限,根据手势的变化来不断地更新环形Buffer的内容。 11.采用SurfaceView在子线程刷新UI, 避免手势的处理和绘制在同一UI线程(普通View都这样做)。 12.采用JNI,将耗时间的处理放到c/c++层来处理。 13.有些能用文件操作的,尽量采用文件操作,文件操作的速度比数据库的操作要快10倍左右。 14. 懒加载和缓存机制。访问网络的耗时操作启动一个新线程来做,而不要再UI线程来做。
Android性能优化典范 http://www.open-open.com/lib/view/open1421723359718.html
主要从三个方面展开: Android的渲染机制,内存与GC,电量优化
1. Android的渲染机制 大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能
大多数操作都必须在16ms内完成, 以便达到流畅的画面所需要的60fps 用HierarchyViewer来查找Activity中的布局是否过于复杂, 也可以使用手机设置里 面的开发者选项,打开Show GPU Overdraw等选项进行观察。 还可以使用TraceView来观察CPU的执行情况,更加快捷的找到性能瓶颈。
2. 过度绘制 屏幕上的某个像素在同一帧的时间内被绘制了多次 打开Show GPU Overdraw的选项 Overdraw有时候是因为你的UI布局存在大量重叠的部分
3. 内存与GC Android系统里面有一个Generational Heap Memory的模型,系统会根据内存中不同 的内存数据类型分别执行不同的GC操作。 例如,最近刚分配的对象会放在Young Generation区域,这个区域的对象通常都是会快速被创建并且很快被销毁回收的, 同时这个区域的GC操作速度也是比Old Generation区域的GC操作速度更快的。
执行GC操作的时候,任何线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。 单个的GC并不会占用太多时间,但是大量不停的GC操作则会显著占用帧间隔时间(16ms)。
VM的回收机制给开发人员带来很大的好处,不用时刻处理对象的分配与回收,可以更加专注于更加高级的代码实现 但是在一个庞大的系统当中,还是免不了经常发生部分对象忘记回收的情况,这就是内存泄 漏。
内存泄漏指的是那些程序不再使用的对象无法被GC识别,这样就导致这个对象一直留在内存当中,占用了宝贵的内存空间。 显然,这还使得每级Generation的内存区域可用空间变小,GC就会更容易被触发,从而引起性能问题。
为了寻找内存的性能问题,Android Studio提供了工具来帮助开发者。
Memory Monitor:查看整个app所占用的内存,以及发生GC的时刻,短时间内发生大量的GC操作是一个危险的信号。 Allocation Tracker:使用此工具来追踪内存的分配,前面有提到过。 Heap Tool:查看当前内存快照,便于对比分析哪些对象有可能是泄漏了的,请参考前面的Case。
4. 电量优化 千万不能让你的应用成为消耗电量的大户。 我们应该尽量减少唤醒屏幕的次数与持续的时间,使用WakeLock来处理唤醒的问题,能够正确执行唤醒操作并根据设定及时关闭操作进入睡眠状态。 某些非必须马上执行的操作,例如上传歌曲,图片处理等,可以等到设备处于充电状态或者电量充足的时候才进行。 触发网络请求的操作,每次都会保持无线信号持续一段时间,我们可以把零散的网络请求打包进行一次操作,避免过多的无线信号引起的电量消耗。
Battery History Tool,它可以查看程序被唤醒的频率,又谁唤醒的,持续了多长的时间,这些信息都可以获取到。 |
7. 网络通信机制及访问策略,TCP/IP、 HTTP协议
TCP/IP Transmission Control Protocol 传输控制协议 Internet Protocol 互联网协议 UDP User Datagram Protocol 用户数据协议 Socket 是程序与网络间的一种接口,大部分网络应用程序都是点对点的,所谓点就是服务器端和客户端所执行的程序。 Socket 是用来接收和传送分组的一个端点。
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议。所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。
|
8. Android消息机制, 多线程
Android消息机制 Handler, Looper,MessageQueue
Looper – 一个线程只能有一个Looper
MessageQueue – 一个线程只能有一个MessageQueue 采用排队的方式对消息进行处理
Handler – 只能添加到有MessageQueue的线程中,一个线程可以有多个handler Handler的sendMessage() 函数 发送消息 Handler的handleMessage() 函数对消息进行处理
|
|
多线程 1. 普通线程 执行完run()方法内的代码后线程就结束
2. 异步线程 线程启动后 进入一个 死循环, 每循环一次,处理MessageQueue中的一个消息 如果MessageQueue为空,线程会暂停 使用条件: 任务要常驻,要根据消息不同执行不同操作
|
9. 熟悉SQL,常见本地化存储的优化
10. JNI 和 NDK
11. HTML5 和 JS
13. Java Server端
14. Linux & Kenel
15. Framework概述
1. 服务端WmS:管理个窗口(实际是View或ViewGroup)的叠放次序, 隐藏或显示 KeyQ类:WmS内部类,不断读取用户UI操作消息,放入消息队列QueueEvent中 AmS:管理所有应用程序的Activity
2. 客户端ActivityThread: App主线程类,所有apk有且仅有一个该类,该类的static main()是App入口。 ActivityThread所在的线程即为UI线程。 Activity: ActivityThread会根据用户操作 动态选择加载那个Activity对象
PhoneWindow: 包含一个DecorView(父类FrameLayout),并提供一组窗口操作API Window: 提供一组窗口操作API, 并不是WmS管理的窗口 DecorView: FrameLayout子类,相对普通的FrameLayout做了一定的修饰
ViewRoot:继承Handler,将WmS的IPC调用 转化为本地的异步调用 ViewRoot.W:继承于Binder。WmS通知客户端窗口时,是通过IPC调用,也就是调用该Binder。 然后,该Binder内部处理函数会给 所在的ViewRoot发一个Handler消息,以便异步处理
WindowManager: 客户端 创建窗口时向它申请,由它与WmS交互
3. Linux驱动与Framework相关的: ① SurfaceFlingger:把各个Surface显示在同一个屏幕上 ② Binder:提供跨进程消息传递
|
Apk的整个运行过程1. ActivityThread.main() -> prepareMainLooper(): 为UI线程创建MessageQueue 和 ActivityThread对象 ActivityThread对象初始化 -> 创建爱H(Handler) 和 ApplicationThread(Binder)对象。 Binder接收AmS中的IPC调用后,发送handler消息到MessageQueue, UI线程异步读取消息并进行相应操作(start,stop,pause) 2. UI线程 执行Looper.loop() 循环不断的读取、处理消息
AmS发送start Activity消息 –> ActivityThread 接收, 创建PhoneWindow类 -> DecorView类 -> 相应的View或ViewGroup。 创建完成后 –> 调用WindowManager -> 创建ViewRoot对象(包含W类)-> 调用WmS提供的远程接口添加窗口并显示到屏幕
用户在界面上操作 -> KeyQ把用户消息 存在QueueEvent中 -> InputDispatcherThread 逐个取出消息 并调用WmS相应函数处理
WmS发现消息属于哪个窗口时,调用相应的ViewRoot.W类,-> W 将消息传给ViewRoot -> ViewRoot把消息传给UI线程ActivityThread –> ActivityThread做相应处理
客户端中, 消息处理顺序 DecorView -> 内部View 或 ViewGroup -> PhoneWindow –> Activity
|
客户端中的线程每个Binder对应一个线程 Activity启动后 ①创建一个ViewRoot.W 对象, ②ActivityThread会创建一个ApplicationThread对象,这两Binder负责接收Linux Binder驱动和发送IPC调用 程序本身所在的线程即UI线程,所有处理用户消息和绘制界面 都由它完成。 一个APK只有一个UI线程
UI线程: 从ActivityThread运行,在该类的main()方法中 使用Looper.prepareMainLooper()为该线程添加Looper对象(包含MessageQueue)。所以我们可以直接定义Handler 普通线程:裸线程,没有Loop,不能直接定义Handler
|