Android知识点总结

》Android 四大启动模式:

standard: 标准模式也是系统的默认模式。每次启动一个 Activity 都会重新创建一个新的实例,不管这个实例是否已经存在。

singleTop 栈顶复用模式:如果新 Activity 已经位于任务栈的栈顶,那么此 Activity 不会被重新创建,同时它的 onNewIntent 方法会被调用,通过此方法的参数可以取出当前请求的信息。这个 Activity 的 onCreate、onStart 不会被系统重新调用,因为它并没有发生改变。如果新 Activity 的实例已经存在但不是位于栈顶,那么新 Activity 仍然会重建。

singleTask:栈内复用模式。这是一种单实例模式,在这种模式下,只要 Activity 在一个栈中存在,那么多次启动此 Activity 都不会重新创建实例,复用时会将它上面的 Activity 全部出栈,同时它的 onNewIntent 方法会被调用。

singleInstance:单实例模式。该模式除了具备 singleTask 模式的所有特性外,该模式的 Activity 只能单独的位于一个任务栈中,具有全局唯一性,即整个系统中只有这一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例

》事件分发机制

事件:在我们通过屏幕与手机交互的时候,每一次点击、长按、移动等都是一个个事件。按照面向对象的思想,这些一个个事件都被封装成了MotionEvent。
分发机制:就是某一个事件从屏幕传递给app视图中的各个View,然后由其中的某个View来使用这一事件或者忽略这一事件,这整个过程的控制就是分发机制了。

要注意的是,事件分发机制中,事件是按一个事件序列的形式分发给View的。这一序列由 ACTION_DOWN 开始,经过一系列 ACTION_MOVE 等事件,最后以 ACTION_UP 事件结束。这一个序列中的所有事件,要么被忽略,要么就只能有一个事件能使用。要是同一个序列,比如从按下到移动这一系列的动作,不同的View都能接受的话,那整个界面就会非常混乱,而且逻辑很复杂。

  • dispatchTouchEvent : 分发事件
  • onInterceptTouchEvent : 拦截事件
  • onTouchEvent : 消费事件

参考:https://zhuanlan.zhihu.com/p/27608989

》自定义View的绘制流程

onMesure:测量。系统会先根据xml布局文件和代码中对控件属性的设置,来获取或者计算出每个View和ViewGrop的尺寸,并将这些尺寸保存下来。

onLayout:布局。根据测量出的结果以及对应的参数,来确定每一个控件应该显示的位置。

onDraw:绘制。确定好位置后,就将这些控件绘制到屏幕上。

Android知识点总结

如何对自定义View进行控制

1. 如果想控制View在屏幕上的渲染效果,就在重写onDraw()方法,在里面进行相应的处理。

2. 如果想要控制用户同View之间的交互操作,则在onTouchEvent()方法中对手势进行控制处理。

3. 如果想要控制View中内容在屏幕上显示的尺寸大小,就重写onMeasure()方法中进行处理。

4. 在 XML文件中设置自定义View的XML属性。

5. 如果想避免失去View的相关状态参数的话,就在onSaveInstanceState() 和 onRestoreInstanceState()方法中保存有关View的状态信息。

》handler机制

1、handler是android线程之间的消息机制,主要的作用是将一个任务切换到指定的线程中去执行,android系统中的一个例子就是主线程中的所有操作都是通过主线程中的handler去处理的。

2、Handler的运行需要底层的 messagequeue和 looper做支撑。

3、
3 .1 、首先说messagequeue,messagequeue 是 一 个 消 息 队 列 , 它是采用单链表的数据结构来存储消息的,因为单链表在插入删除上 的效率非常高。Meaasgequeue主要包含一个是插入消 息的 enqueuemessage方法,和一个取出一条消息的next方法。
3.2、然后说 looper,looper在安卓的消息机制中是扮演着消息调度的角色,具体来说就是他会不停的从 messagequeue中查看 是否有新消息,如果有就会立刻处理,否则会一直阻塞在messagequeue的next那里。构成 一个 looper是需要一个 messagequeue,而构成一个 handler则需 要一个 looper,looper一般是调用Looper.prepare()方法使用 threadlocal在线程的ThreadLocalMap中存储一个looper的,线程中有了looper之后就可以在这个线程中创建一个 handler了。
3.3、最后说 handler,hanlder的构成是需要一个 looper,主线程 之中,在activitythread的main方法中(程序入口)通过 looper.preparemainlooper在主线 程中存储一个 looper,而在子线程之中,我们则需要手动的通过 looper的 prepare在子线程中存储一个 looper,然后通过 looper.loop 开启一个消息循环。 handler发送消息的过程,handler.send (message)方法实际上是往构成他的 looper的 messagequeue中 插入了一条消息,在将这条消息插入 messagequeue中之前,他需 要将此消息的 target变量指向当前发它的 handler,looper发现构成它的 messagequeue中有消息时, looper的 loop方法就会从 messagequeue中取出这条消息,然后调 用 msg.target.dispatchmessage,即调用发送这个消息的 handler 的 dispatchmessage方法来处理这个消息,注意,dispatchmessage 方法是在构成 handler的 looper中的loop方法中调用的,所以处理消息的逻辑就切换到了handler所在的线程之中了。

4、android系统中的一个例子就是主线程中的所有操作都是通过主线程中的handler去处理的。例如activity的生命周期方法调用就是通过主线程中的handler去处理的。
在app的主线程中有一个类是activitythread,这个类中有一个main方法是app程序的入口,在main方法中使用Looper.prepareMainLooper(),在主线程中设置了一个looper,然后创建了一个applicationthread的线程用于和server进程中applicationthreadproxy进行进程通信,最后调用了Looper.loop()开启消息循环。
在activity的生命中期中,比如说系统服务ActivityManagerService调用applicationthreadproxy通过Binder给当前app进程中的applicationthread发送了一个暂停activity的操作。app进程中的applicationthread便会通过在主线程中的handler将这个暂停activity的消息插入到主线程的messagequeue中去处理。

5、主线程的死循环一直运行是不是特别消耗CPU资源呢?
这里就涉及到Linux pipe/epoll机制,在主线程的MessageQueue没有消息时,主线程便阻塞在loop的queue.next()中的nativePollOnce()方法里,相当于java层的线程waite机制,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达时调用nativewake,通过往pipe管道写端写入数据来唤醒主线程工作。相当于java层的notify机制,去唤醒主线程,然后处理消息,所以主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

6、 Android中为什么主线程不会因为Looper.loop()里的死循环导致(anr)卡死?
首先主线程中的死循环不会导致app anr,它会使得主线程阻塞在messagequeue的next中的nativePollOnce()方法里,当有消息来时就会唤醒主线程进行消息处理,即使主线程在休眠的时候也有其他的线程(applicationthread)在处理事件。
为什么主线程中会采用死循环呢?
线程是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了。而对于主线程,我们是绝不希望会被运行一段时间就退出,所以采用死循环保证它不会被退出。
而anr原因是,主线程中messagequeue中一个message的处理时间过长,导致接下来的消息无法处理,比如说一个消息的处理时间超过了5秒,导致用户的输入无法响应,才会出现anr。

参考:https://blog.csdn.net/wsq_tomato/article/details/80301851