android随笔

  • 系统只在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。

你可能感兴趣的:(总结,阅读)