- 系统只在Activity异常终止的时候才会调用onSaveInstanceState和onRestoreInstanceState来存储和恢复数据,其他情况不会触发这个过程。
android中的IPC方式
-
Bundle
-
文件共享
-
AIDL
-
Messenger
-
ContentProvider
-
Socket
-
进程和线程是包含与被包含的关系。
Bundle
- 简单易用
- 只能传输Bundle支持的数据类型,不支持int,long,但可以使用String类型包装
- 四大组件间的进程间通信
文件共享
- 简单易用
- 不适合高并发场景,并且无法做到进程间的即时通信
- 无并发访问情形,交换简单的数据实时性不高的场景
AIDL
- 功能强大,支持一对多并发通信,支持实时通信
- 使用稍复杂,需要处理好线程同步
- 一对多通信且有RPC需求
Messenger
- 功能一般,支持一对多串行通信,支持实时通信
- 不能很好处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型
- 低并发的一对多即时通信,无RPC需求,或者无须要返回结果的RPC需求
ContentProvider
- 在数据源访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作
- 可以理解为受约束的AIDL,主要提供数据源的CRUD操作
- 一对多的进程间的数据共享
Socket
- 功能强大,可以通过网络传输字节流,支持一对多并发实时通信
- 实现细节稍微有点繁琐,不支持直接的RPC
- 网络数据交换
View事件体系
MotionEvent
TouchSlop
View的滑动
滑动在Android开发中具有很重要的作用,不管一些滑动效果多么绚丽,归根结底,它们都是由不同的滑动外加一些特效所组成的。因此,掌握滑动的方法是实现绚丽的自定义控件的基础。
通过三种方式可以实现View的滑动:第一种是通过view本什么提供的scrollTo/scrollBy方法来实现滑动;第二种是通过动画给view施加平移效果来实现滑动;第三种是通过改变view的layoutParams使得view重新布局从而实现滑动。
- scrollTo/scrollBy:操作简单,适合对view内容的滑动;
- 动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果;
- 改变布局参数:操作稍微复杂,适用于有交互的view。
实现弹性滑动
- Scroller
- Handler#postDelayed
- Thread#Sleep
View的事件分发机制
- dispatchTouchEvent
- onInterceptTouchEvent
- onTouchEvent
当一个点击事件产生后,它的传递过程遵循如下顺序:Activity->Window->View。
事件传递机制的结论
- 同一个事件序列是指从手指接触屏幕的那一刻起,到手指离开屏幕的那一刻结束,在这个过程中所产生的一系列事件,这个事件序列以down事件开始,中间含有数量不定的move事件,最终以up事件结束。
- 正常情况下,一个事件序列只能被一个View拦截且消耗。这一条的原因可以参考下一条,因为一旦一个元素拦截了某次事件,那么同一个事件序列内的所有事件都会直接交给它处理,因此同一个事件序列中的事件不能分别有两个view同时处理,但是通过特殊手段可以做到,比如一个view将本该自己处理的事件通过onTouchEvent强行传递给其他View处理。
- 某个view一旦决定拦截,那么这一个事件序列都只能由它来处理(如果事件序列能够传递给它的话),并且它的onInterceptTouchEvent不会再被调用。这条也很好理解,就是说当一个view决定拦截一个事件后,那么系统会把同一个事件序列内的其他方法都直接交给它来处理,因此就不会再调用这个view的onInterceptTouchEvent去询问它是否要被拦截了。
- 某个view一旦开始处理事件,如果它不消耗ACTION_DOWN事件(onTouchEvent返回;额false),那么同一个事件序列中的其他事件都不会再交给它来处理,并且事件将重新交给它的父元素去处理,即父元素的onTouchEvent会被调用。意思就是事件一旦交给一个view处理,那么它就必须消耗掉,否则一事件序列中剩下的事件就不再交给他来处理了,这就好比上级交给程序员一件事,如果这件事没有处理好,短期内上级就不敢再把事情交给这个程序员做了,二者是类似的道理。
- 如果View不消耗除ACTION_DOWN以外的其他事件,那么这个点击事件会消失,此时父元素的onTouchEvent并不会被调用,并且当前view可以持续收到后续的事件,最终这些消失的点击事件会传递给Activity处理。
- ViewGroup默认不拦截任何事件。Android源码中ViewGroup的onInterceptTouchEvent方法默认返回false。
- view没有onINterceptTouchEvent方法,一旦有点击事件传递给他,那么它的onTouchEvent方法就会被调用。
- view的onTouchEvent默认都会消耗事件(返回true),除非它是不可点击的(clickable和longClickable同时为false)。View的longClickable属性默认都为false,clickable属性要分情况,比如button的clickable属性默认为true,而textview的clickable属性默认为false。
- view的enable属性不影响onTouchEvent的默认返回值。哪怕一个view是disable状态的,只要它的clickable或者longClickable有一个为true,那么它的onTouchEvent就返回true。
- onClick会发生的前提是当前view是可点击的,并且它收到了down和up的事件。
- 事件传递过程是由外向内的,即事件总是先传递给父元素,然后再由父元素分发给子View,通过requestDisallowInterceptTouchEvent方法可以在子元素中干预父元素的事件分发过程,但是ACTION_DOWN事件除外。
顶级view对点击事件的分发过程
点击事件达到顶级view(一般是一个viewGroup)以后,会调用ViewGroup的dispatchTouchEvent方法,然后的逻辑是这样的:如果顶级ViewGroup拦截事件即onInterceptTouchEvent返回true,则事件由ViewGroup处理,这时如果ViewGroup的mOntouchListener被设置,则onTouch会调用,否则onTouchEvent会被调用。也就是说,如果都提供的话,onTouch会屏蔽掉onTouchEvent。在onTouchEvent中,如果设置了mOnclickListener,则onClick会被调用。如果顶级ViewGroup不拦截事件,则事件会传递给它所在的点击事件链上的子view,这时子view的dispatchTouchEvent会被调用。到此为止,事件已经从顶级view传递给了下一层view,接下来的传递过程和顶级view是一致的,如此循环,完成整个事件的分发。
View的滑动冲突
View的绘制流程
获取view的宽高的四种方法
- Activity/View#WindowFocusChanged
- view.post(runnable)
- viewTreeObserver
- view.measure(int widthMeasureSpec,int heightMeasureSpec)
自定义view
分类:
- 继承view重写onDraw方法
- 继承ViewGroup派生特殊的Layout
- 继承特定的view(比如TextView)
- 继承特定的ViewGroup(比如linearlayout)
RemoteViews
- RemoteViews在Android中的使用场景有两种:通知栏和桌面小部件。
Window和WindowManager
一个应用处于停止状态分为两种情形
- 第一种是应用安装后未运行
- 第二种是应用被手动或者其他应用强停了
Android的消息机制
Android的消息机制主要是指Handler的运行机制,Handler的运行需要底层的MessageQueue和Looper的支撑。MessageQueue的中文翻译是消息队列,顾名思义,它的内部存储了一组消息,以队列的形式对外提供插入和删除的工作。虽然叫消息队列,但是它的内部存储结构并不是真正的队列,而是采用单链表的数据结构来存储消息列表。Looper的中文翻译为循环,在这里可以理解为消息循环。由于MessageQueue只是一个消息的存储单元,它不能去处理消息,而Looper就填补了这个功能,Looper会以无限循环的形式去查找是否有新消息,如果有的话就处理消息,否则就一直等待着。Looper中还有一个特殊的概念,那就是TheadLocal,ThreadLocal并不是线程,它的作用是可以在每个线程中存储数据。我们知道,handler创建的时候会采用当前线程的Looper来构造消息循环系统,那么Handler内部如何获取到当前线程的Looper呢?这就要使用ThreadLocal了,ThreadLocal可以在不同的线程中互不干扰地存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。当然需要注意的是,线程是默认没有Looper的,如果需要使用handler就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,他就是ActivityThread,ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用handler的原因。
系统为什么不允许在子线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那为什么系统不对UI控件的访问加上锁限制呢?缺点有两个:
- 首先加上锁机制会让UI访问的逻辑变得复杂
- 其次锁机制会降低UI访问的频率,因为锁机制会阻塞某些线程的执行。
Android的主线程就是ActivityThread,主线程的入口方法为main,在main方法中系统会通过Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,并通过Looper.loop()来开启主线程的消息循环。
ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。
Android的线程和线程池
不同形式的线程虽然都是线程,但是它们仍然具有不同的特性和使用场景。AsyncTask封装了线程池和Handler,它主要是为了方便开发者在子线程中更新UI。HandlerThread是一种具有消息循环的线程,在它的内部可以使用Handler。IntentService是一个服务,系统对其进行了封装使其可以更方便地执行后台任务,IntentService内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出。从任务执行的角度来看,IntentService的作用很像一个后台线程,但是IntentService是一种服务,它不容易被系统杀死从而可以尽量保证任务的执行,而如果是一个后台线程,由于这个时候进程中没有活动的四大组件,那么这个进程的优先级就会非常低,会很容易被系统杀死,这就是IntentService的优点。
Android中的线程池
线程池的优点
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
- 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
- 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能。
ThreadPoolExcutor执行任务
- 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务。
- 如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插入到任务队列中排队等待执行。
- 如果在步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
- 如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExcutor会调用RejectedExcutionHandler的rejectedExecution方法来通知调用者。
线程池的分类
- FixedThreadPool
- CachedThreadPool
- ScheduledThreadPool
- SingleThreadExecutor
Bitmap的加载和Cache
LRU(Least Recently Used),LRU是近期最少使用算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有两种:LruCache和DiskLruCache。前者用于实现内存缓存,而后者则充当了存储设备缓存。
- 强引用:直接的对象引用。
- 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收。
- 弱引用:当一个对象只有弱引用存在时,此对象会随时被gc回收。
Android的动态加载技术
Android性能优化
- 布局优化
- 绘制优化
- 内存泄漏优化
- 响应速度优化
- ListView优化
- Bitmap优化
- 线程优化
每个线程中只能存在一个Looper
每个线程中可以有很多个Handler,即一个Looper可以处理来自多个Handler的消息。Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler。
ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立改变自己的副本,而不会影响其他线程所对应的副本。
从线程的角度看,目标变量就像是线程的本地变量,这也是类名中‘Local’所要表达的意思。
触发onSaveInstanceState(Bundle outState)的条件:
- 当用户按下HOME键时
- 从最近应用中选择运行其他的程序时
- 按下电源按键(关闭屏幕显示)时
- 从当前activity启动一个新的activity时。
- 屏幕方向切换时(无论竖屏切横屏还是横屏切竖屏都会调用)
在前4中情况下,当前activity的生命周期为: onPause->onSaveInstanceState->onStop
通过Excutors提供四种线程池
- newFixedThredPool,创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
- newCachedThreadPool,创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程。
- newSingleThreadExcutor,创建一个单线程化的线程池,它只会用唯一的工作现场来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
- newScheduledThreadPool,创建一个定长线程池,支持定时及周期性任务执行。
- 数据量比较小,并且需要频繁的试用Map存储数据的时候,推荐试用ArrayMap。
- 而数据量比较大的时候,则推荐使用HashMap。
Activity启动过程全解析
主要对象功能介绍
- ActivityManagerServices,简称AMS,服务端对象,负责系统中所有Activity的生命周期
- ActivityThread,App的真正入口。当开启App之后,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。与ActivityManagerServices配合,一起完成Activity的管理工作。
- ApplicationThread,用来实现ActivityManagerService与ActivityThread之间的交互。在ActivityManagerService需要管理相关Application中的Activity的生命周期时,通过ApplicationThread的代理对象与ActivityThread通讯。
- ApplicationThreadProxy,是ApplicationThread在服务器端的代理,负责和客户端的ApplicationThread通讯。AMS就是通过该代理与ActivityThread进行通信的。
- Instrumentation,每一个应用程序只有一个Instrumentation对象,每个Activity内都有一个对该对象的引用。Instrumentation可以理解为应用进程的管家,ActivityThread要创建或暂停某个Activity时,都需要通过Instrumentation来进行具体的操作。
- ActivityStack,Activity在AMS的栈管理,用来记录已经启动的Activity的先后关系,状态信息等。通过ActivityStack决定是否需要启动新的进程。
- ActivityRecord,ActivityStack的管理对象,每个Activity在AMS对应一个ActivityRecord,来记录Activity的状态以及其他的管理信息。其实就是服务器端的Activity对象的映射。
- TaskRecord,AMS抽象出来的一个“任务”的概念,是记录ActivityRecord的栈,一个“Task”包含若干个ActivityRecord。AMS用TaskRecord确保Activity启动和退出的顺序。如果你清楚Activity的4中launchMode,那么对这个概念应该不陌生。
Init
- 整个Android系统的启动分为Linux Kernel的启动和Android系统的启动。Linux Kernel启动起来后,然后运行第一个用户程序,在Android中就是init程序。
- init是所有Linux程序的起点,而Zygote于android,是所有java程序的孵化池。
- init是zygote的父进程,而system_server和其他所有的com.xxx结尾的应用程序都是从zygote fork而来。
SystemServer
- ActivityManagerService
- PackageManagerService
- WindowManagerService
经过上面这些步骤,我们的ActivityManagerService对象已经创建好了,并且完成了成员变量初始化。而且在这之前,调用createSystemContext()创建系统上下文的时候,也已经完成了mSystemContext和ActivityThread的创建。注意,这是系统进程开启时的流程,在这之后,会开启系统的Launcher程序,完成系统界面的加载和显示。
其实服务器客户端的概念不仅仅存在于Web开发中,在Android的框架设计中,使用的也是这一种模式。服务器端指的就是所有App共用的系统服务,比如我们这里提到的ActivityManagerService,和前面提到的PackageManagerService,WindowManagerService等等,这些基础的系统服务是被所有的App公用的,当某个App想实现某个操作的时候,要告诉这些系统服务,比如你想打开一个App,那么我们知道了包名和MainActivity类名之后就可以打开。
App与AMS通过Binder进行IPC通信,AMS(SystemServer进程)与zygote通过Socket进行IPC通信。
那么AMS有什么用呢?在前面我们知道了,如果想打开一个App的话,需要AMS去通知zygote进程,除此之外,其实所有的Activity的开启,暂停,关闭都需要AMS来控制,所以我们说,AMS负责系统中所有Activity的生命周期。
在Android系统中,任何一个Actiivty的启动都是由AMS和应用程序进程(主要是ActivityThread)相互配合来完成的。AMS服务统一调度系统中所有进程的Activity启动,而每个Activity的启动过程则由其所属的进程具体来完成。
Launcher本质上也是一个应用程序,和我们的App一样,也是继承自Actiivty。