深入理解Android生命周期
面试时候常常被问到特别是Activity的生命周期 是怎样的,初学者仅仅会从简单的onCreate、onR esume等方法入手。向面试官介绍这些回调方法执 行的顺序,更好一点的会提到任务栈,或者是启 动模式,以为自己答的不错,而却不知道那仅仅 是皮毛而已。
在陈述之前,我想知道你是否对Handler
、Looper
、以及MessageQueue
有落了解。这可是面试官必考的问题之一。
如果看过相关的源码,你得了解以下的几条关于这几个类的信息。
通过Handler
我们可以从子线程向主线程发送消息,即所谓的更新UI。
Looper
是个消息循环,不断从MessageQueue
中取出消息,交给对应Handler
处理。
在主线程中创建的Hander的执行过程是在主线程,如果想要Hander处理在子线程,请使用HandlerThread
来创建,或者使用从子线程中创建的Handler
。子线程必须实现一个Looper消息循环。
你真的了解主线程吗?
所有的Java应用程序都是从main方法开始的,我说的没错吧,对于Java桌面程序或者JEE Servlet 容器。我想说,是 Android应用也是从Main 方法开始的。
当Android系统启动的时候,它会启动一个叫 ZygoteInit 的进程,这个进程就是一个 Dalvik VM。进程会在一个线程里面加载SDK相关的类,并处于等待状态,这里我不多说,我也就提一嘴这个进程。
当你启动一个Android应用的时候,Android系统会fork上面那个ZygoteInit进程。fork之后的那子进程的对应的那个线程(就是上面提到加载SDK后等待的线程)会停止等待,并调用ActivityThread.main()
Loopers
Loopers 存在目的,就是为了能让一个线程顺序的处理消息事件。
每一个Looper的内部拥有一个队列,叫MessageQueue
,里面存放这个各种消息对象Message
。
Looper
拥有loop()
方法,方法里面按顺序处理队列(MessageQueue)里面的消息,如果队列为空,那么就会等待。
Looper.loop()
内部实现也是很简单
void loop() {
while(true) {
Message message = queue.next();// block if empty
dispatchMessage(message);
message.recycle();
}
}
每一个Looper
都与一个线程相关联。创建一个Looper
并将其与当前线程相关联,你必须在线程里面调用 Looper.prepare()
方法,创建好的Looper
会放在ThreadLocal
里面,你可以通过Looper.myLooper()方法得到与当前线程关联的 Looper
对象,另外
这也使得这个线程变成了一个 Pipline线程。
HandlerThread
类内部就相当于创建一个上述 Pipline线程。通过HandlerThread
你无须自己创建 Looper
。并直接可以从中获取Looper对象,通过这个Looper对象和Handler你可以向这个线程发消息进行让它处理,这样处理是在子线程中进行的。
HandlerThread thread = new HandlerThread("handlerthread");
thread.start();// 启动这个线程
Looper looper = thread.getLooper();
Handlers
Handler
肯定是要配合Looper
来使用的,handler
存在的作用在 在任何线程中通过Handler
把消息放进Looper
里面的消息队列当中处理任何从Looper取出的消息,但处理线程就是与Looper
相关联的线程。
Handler handler = new Handler(looper) {
public void handleMessage(Message message) {
// 在与 looper 相关联的线程中处理消息
if (message.what == DO_SOMETHING) {
// do something
}
}
}
// 创建消息对象
Message message = handler.obtainMessage(DO_SOMETHING);
handler.sendMessage(message);
多个Handler
可以绑定同一个Looper
对象,让后Looper
会分发消息给message.target
Handler还有一种用法是传递一个Runable对象。
handler.post(new Runnable() {
public void run() {
// 运行在looper相关联的线程
}
});
一个Handler
创建可以不用传递给它Looper
对象
// 最好明确告诉它使用哪个Looper
Handler handler = new Handler();
对于Handler的无参数的构造函数,它会调用Looper.myLooper()
并且得到与当前线程相关联的Looper对象,这就导致整个Looper
可能不是你想要的那个Looper
,所以最好还是应该传递给它一个明确的Looper
。
大多数情况下,我们需要主线程的Looper
Handler handler = new Handler(Looper.getMainLooper());
回到我们提到的ActivtyThread.main()
在这里面它做了这样一件事
public class ActivityThread {
public static void main(String... args) {
Looper.prepare();
Looper.setMainLooper(Looper.myLooper());
// Post first message to the looper
{...}
Looper.loop();
}
}
你看到了,整个main thread就是一个Pipline 线程,无限处理各种消息。它的第一件事情就是创建Application类,并且调用Application.onCreate()回调方法。
Android 主线程生命周期
先看一个与Activty生命周期相关的例子,处理配置变化,比如更改屏幕显示方向这个老生常谈的问题。我们知道如果我们想用代码改变屏幕的方向,只需使用Activity#setRequestedOrientation(int)
方法。
当你一个Activity
以竖屏打开之后,在onCreate
里面调用setRequestedOrientation(int)
方法的时候,它的回调方法调用是怎样的?
public class MyActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
Log.d("Square", "onCreate()");
if (saveInstanceState == null) {
Log.d("Square", "Requesting orientation change");
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
}
}
protected void onResume() {
super.onResume();
Log.d("Square", "onResume");
}
protected void onPasue() {
super.onPasue();
Log.d("Square", "onPasue()");
}
protected void onDestory() {
super.onDestory();
Log.d("Square", "onDestory()")
}
}
如果你熟悉 Android 生命周期 你或许会预测到打印出的Log
onCreate()
Requesting orientation change
onResume()
onPause()
onDestory()
onCreate()
onResume()
Android 内部是怎样处理Orientation变化的呢?
这里有一条必须明确: 对于因Orientation
变化导致的Activity
的重构,实际上是通过向咱们的Main Looper
发送了几条消息到 Looper
内部的消息队列(MessageQueue
)当中去。MessageQueue
仅仅是一个单链表,每一个节点的 next
指向下一条消息。
所以更确切的说,请求设置Orientation改变的时候时候,会将 CONFIGURATION_CHANGED 和 RELAUNCH_ACTIVITY 消息发送到主线程的main looper内部的消息队列。随后会被取出按顺序处理。
Service 并不运行在后台线程
待补充..