整个View树的绘图流程是在ViewRootImpl类的performTraversals()方法(这个方法巨长)开始的,该函数做的执行过程主要是根据之前设置的状态,判断是否重新计算视图大小(measure)、是否重新放置视图的位置(layout)、以及是否重绘 (draw)。
Activity、View及Window之间关系
View
View(包括ViewGroup)使用的是组合模式,将View组成成树形结构,以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。View主要是用于绘制我们想要的结果,是一个最基本的UI组件。
Window
简单地说,Window
表示一个窗口,一般来说,Window
大小取值为屏幕大小。但是这不是绝对的,如对话框、Toast等就不是整个屏幕大小。你可以指定Window
的大小。Window
包含一个View tree
和窗口的layout
参数。
感觉Window的理解比较抽象,Window相当于一个容器,里面“盛放”着很多View,这些View是以树状结构组织起来的。
Android系统架构
应用程序(Applications)
Android会同一系列核心应用程序包一起发布,该应用程序包包括email客户端,SMS短消息程序,日历,地图,浏览器,联系人管理程序等。所有的应用程序都是使用JAVA语言编写的。通常开发人员就处在这一层。
应用程序框架(Application Frameworks)
提供应用程序开发的各种API进行快速开发,也即隐藏在每个应用后面的是一系列的服务和系统,大部分使用Java编写,所谓官方源码很多也就是看这里,其中包括:
- 丰富而又可扩展的视图(Views):可以用来构建应用程序, 它包括列表(lists),网格(grids),文本框(text boxes),按钮(buttons), 甚至可嵌入的web浏览器。
- 内容提供器(Content Providers):使得应用程序可以访问另一个应用程序的数据(如联系人数据库), 或者共享它们自己的数据
- 资源管理器(Resource Manager):提供 非代码资源的访问,如本地字符串,图形,和布局文件( layout files )。
- 通知管理器 (Notification Manager):使得应用程序可以在状态栏中显示自定义的提示信息。
- 活动管理器( Activity Manager):用来管理应用程序生命周期并提供常用的导航回退功能。
Event
事件的分发机制由三个重要方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent
事件分发:public boolean dispatchTouchEvent(MotionEvent ev):用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的DispatchTouchEvent方法的影响,表示是否消耗当前事件。
事件拦截:public boolean onInterceptTouchEvent(MotionEvent event):在上述方法内部调用,用来判断是否拦截某个事件,如果当前View拦截了某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
事件响应:public boolean onTouchEvent(MotionEvent event):在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。
三者的关系可以总结为如下伪代码:
Handler
1.Looper.prepare
首先从ThreadLocal
中获取一个Looper
,如果没有则向ThreadLocal
中添加一个new Looper
,同时新建一个MessageQueue
。
主线程的Looper在ActivityThread创建。
ThreadLocal
ThreadLocal
是Java提供的用于保存同一进程中不同线程数据的一种机制。每个线程中都保有一个ThreadLocalMap
的成员变量,ThreadLocalMap
内部采用WeakReference
数组保存,数组的key即为ThreadLocal
内部的Hash值。
Android面试题
APK安装过程
应用安装涉及到如下几个目录:
- system/app:系统自带的应用程序,无法删除
- data/app:用户程序安装的目录,有删除权限。安装时把apk文件复制到此目录
- data/data:存放应用程序的数据
- data/dalvik-cache:将apk中的dex文件安装到dalvik-cache目录下
复制APK安装包到data/app目录下,解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录,并在data/data目录下创建对应的应用数据目录。
invalidate()和postInvalidate() 的区别
invalidate()
是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。postInvalidate()
在工作者线程中被调用。
导入外部数据库
Android系统下数据库应该存放在 /data/data/com.*.*(package name)/
目录下,所以我们需要做的是把已有的数据库传入那个目录下。操作方法是用FileInputStream
读取原数据库,再用FileOutputStream
把读取到的东西写入到那个目录。
ListView原理及优化
##原理
ListView的实现离不开Adapter。可以这么理解:ListView中给出了数据来的时候,View如何实现的具体方式,相当于MVC中的V;而Adapter提供了相当于MVC中的C,指挥了ListView的数据加载等行为。
提一个问题:假设ListView中有10W个条项,那内存中会缓存10W个吗?答案当然是否定的。那么是如何实现的呢?下面这张图可以清晰地解释其中的原理: