Q(1):Handler中有Loop死循环,为什么没有阻塞主线程?
A:对于线程即是一段可执行的代码,当可执行代码执行完成后,线程生命周期便该终止了,线程退出。对于主线程,不希望被杀死,就要保持一只运行状态,死循环便能保证不会被退 出。然而looper.loop本身不会导致应用卡死, 真正会卡死主线程的操作的是,在一些回调方法 中如下方法:onCreate/onStart/onResume 等这些方法中操作时间过长,会导致掉桢,才导致ANR;
Q ( 2 ): 主线程的死循环一直运行是不是特别消耗CPU资源呢?
A:其实不然,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。
Q (3): 主线程的消息循环机制是什么?
A: Activity的生命周期都是依靠主线程的Looper.loop,当收到不同Message时则采用相应措施;比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法;再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。
Q(4) 主线程的消息又是哪来的呢?
A: 当然是App进程中的其他线程通过Handler发送给主线程;
1: system_server进程:
system_server进程是系统进程,java framework框架的核心载体,里面运行 了大量的系统服务,比如这里提供ApplicationThreadProxy(简称ATP), ActivityManagerService(简称AMS),这个两个服务都运行在system_server进程的不同线程中,由于ATP和AMS都是基于IBinder接口,都是binder线程,binder线程的创建与销毁都是由binder驱动来决定的。
2: App进程:
App进程则是我们常说的应用程序,主线程主要负责Activity/Service等组件的生命周期以及UI相关操作都运行在这个线程; 另外,每个App进程中至少会有两个binder线程 ApplicationThread(简称AT)和ActivityManagerProxy(简称AMP),除了图中画的线程,其中还有很多线程。
Q(5)Binder 是啥 ?
A: Binder用于不同进程之间通信,由一个进程的Binder客户端向另一个进程的服务端发送事务,比如图中线程2向线程4发送事务;而handler用于同一个进程中不同线程的通信.
(备注):
ActivityThread的main方法主要就是做消息循环,一旦退出消息循环,那么你的程序也就可以退出了。从消息队列中取消息可能会阻塞,取到消息会做出相应的处理。如果某个消息处理时间过长,就可能会影响UI线程的刷新速率,造成卡顿的现象。
Android艺术开发探索中写到:
ActivityThread通过ApplicationThread和AMS进行进程间通讯,AMS以进程间通信的方式完成ActivityThread的请求后会回调ApplicationThread中的Binder方法,然后ApplicationThread会向H发送消息,H收到消息后会将ApplicationThread中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行,这个过程就是。主线程的消息循环模型。
Q(6) ActivityThread 的动力是什么?
A: 进程 每个app运行时前首先创建一个进程,该进程是由Zygote fork出来的,用于承载App上运行的各种Activity/Service等组件。进程对于上层应用来说是完全透明的,这也是google有意为之,让App程序都是运行在Android Runtime。大多数情况一个App就运行在一个进程中,除非在AndroidManifest.xml中配置Android:process属性,或通过native代码fork进程。
线程 线程对应用来说非常常见,比如每次new Thread().start都会创建一个新的线程。该线程与App所在进程之间资源共享,从Linux角度来说进程与线程除了是否共享资源外,并没有本质的区别,都是一个task_struct结构体,在CPU看来进程或线程无非就是一段可执行的代码,CPU采用CFS调度算法,保证每个task都尽可能公平的享有CPU时间片。
Q(7) Handler 是如何能够线程切换?
A: 线程间是共享资源的,所以Handler处理不同线程问题就只要注意异步情况即可。
/**** Handler创建的时候会采用当前线程的Looper来构造消息循环系统,Looper在哪个线程创建,就跟哪个线程绑定,并且Handler是在他关联的Looper对应的线程中处理消息的****/.
注意:①线程是默认没有Looper的,如果需要使用Handler,就必须为线程创建Looper。我们经常提到的主线程,也叫UI线程,它就是ActivityThread,②ActivityThread被创建时就会初始化Looper,这也是在主线程中默认可以使用Handler的原因。
Q(7-1):系统为什么不允许在子线程中访问UI?
A(7-1):摘自《Android开发艺术探索》) 这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态,那么为什么系统不对UI控件的访问加上锁机制呢?缺点有两个: ①首先加上锁机制会让UI访问的逻辑变得复杂 ②锁机制会降低UI访问的效率,因为锁机制会阻塞某些线程的执行。 所以最简单且高效的方法就是采用单线程模型来处理UI操作。
备注:此篇文章只作为笔记记录。
感谢 文章原著:https://github.com/xiangjiana/Android-MS/blob/master/study/framework/Android%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6.md
网友评论