1. Handler 的常用使用方法
Handler是安卓提供的一种消息机制。通常用于接受子线程发送的数据,并用此数据配合主线程更新UI。
比如,点击一个按钮去服务器请求数据。如果直接在主线程(UI线程)做请求操作,界面会出现假死现象, 如果长时间还没有完成的话,会收到Android系统的一个错误提示 "应用程序无响应(ANR)"。为什么呢?因为在Android里, App的响应能力是由Activity Manager和Window Manager系统服务来监控的. 通常在如下三种情况下会弹出ANR对话框:
1:KeyDispatchTimeout(谷歌default 5s,MTK平台上是8s) --主要类型 按键或触摸事件在特定时间内无响应
2:BroadcastTimeout(10s) BroadcastReceiver在特定时间内无法处理完成
3:ServiceTimeout(20s) --小概率类型 Service在特定的时间内无法处理完成。
Handler发送消息有多种方式
- msg.sendToTarget();
- mHandler.sendMessageAtFrontOfQueue();
- mHandler.sendEmptyMessage()
- mHandler.sendEmptyMessageDelayed()
- mHandler.post()
- mHandler.postDelayed()
那我们就把这些耗时操作放在子线程中执行好了。 可问题来了,需要把数据填充到相关控件中展示。但Android中更新UI只能在主线程中更新,子线程中操作是危险的,所以需要使用handler。
// 创建一个Handler 对象,运行在主线程 ui线程中
private final Handler myHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 0:
String reslut = (String) msg.obj;
textView.setText(reslut);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.myText);
button = findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
threadRun();
}
});
}
@Override
protected void onResume() {
super.onResume();
}
// 创建个 子线程
private void threadRun() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int i = 0; i < 100; i++) {
result += i;
}
// 休眠个 6 s
Thread.sleep(6000);
Message message = Message.obtain();
message.what = 0;
message.obj = result;
// 通过handler将子线程数据传给主线程
myHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
2. 消息处理机制的重要的类
-
ThreadLocal
ThreadLocal 是线程的局部变量, 是每一个线程所单独持有的。当使用ThreadLocal维护变量的时候 为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题。
-
Message
Message 实现Parcelable 接口,也即实现了序列化,说明Message可用于进程间通信。
-
MessageQueue
MessageQueue确实是用来存放消息(Message对象)的容器,是一个单向链表,Message对象有个next字段保存列表中的下一个,MessageQueue中的mMessages保存链表的第一个元素。
-
Looper
Looper 循环器,Looper是为了为线程提供消息循环。默认情况下,线程没有Looper,可以使用prepare()获取循环,并使用loop()方法开始处理信息,只到循环停止。
与信息循环的大部分交互的都是通过Handler类。
一个MessageQueue需要一个Looper。
-
Handler
Handler 处理者
负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,保存到消息队列中,例如更新UI等。
结合上文的的代码示例以及上图的实现流程,要使用Handler实现异步消息处理,首先我们需要在主线程中创建Handler对象并重写handleMessage()方法,然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handlerr将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。由于Halldler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,从而实现子线程通过Handler机制实现UI线程操作的目的。
3. 源码分析
先看下 主线程的创建:
ActivityThread就是我们常说的主线程或UI线程,ActivityThread的main方法是一个APP的真正入口,MainLooper在它的main方法中被创建。Handler、Looper都是在这个阶段进行创建和初始化的
- frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
// Call per-process mainline module initialization.
initializeMainlineModules();
Process.setArgV0("<pre-initialized>");
// 开始主线程循环
Looper.prepareMainLooper();
// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
// 创建 thread 对象
ActivityThread thread = new ActivityThread();
// 在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
// 获取到 Handler 对象
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
--------------------------------------
其中:获取到 Handler 对象
final H mH = new H();
@UnsupportedAppUsage
public Handler getHandler() {
return mH;
}
class H extends Handler {
public static final int BIND_APPLICATION = 110;
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
。。。。。。
// 创建服务
case CREATE_SERVICE:
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
("serviceCreate: " + String.valueOf(msg.obj)));
}
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
// 绑定服务
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
主线程的Handler作为ActivityThread的成员变量,是在ActivityThread的main方法被执行,ActivityThread被创建时进行初始化的。MessageQueue在Looper创建的时候作为成员变量被初始化创建。
1) Handler sendMessage发送消息到消息队列
子线程要和ui线程通信,发消息,那就 从 myHandler.sendMessage(message) 开始分析:
- frameworks/base/core/java/android/os/Handler.java
public final boolean sendMessage(@NonNull Message msg) {
// 延时为 0
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// 先获取到消息队列
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// 最终调用到消息队列去 入队列
return queue.enqueueMessage(msg, uptimeMillis);
}
调用enqueueMessage()将Message送到MessageQueue中去。那么MessageQueue是什么呢?顾名思义是消息队列,其实在我们创建Handler的时候,它需要与Looper作关联,Looper类有一个成员变量
看一下 获取到消息队列 MessageQueue queue = mQueue,mQueue是哪里来的
是在 Handler 的构造函数中赋予值的
public Handler(@Nullable Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
// 这里通过 Looper 获取到 looper 对象
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 通过 looper 获取到 消息队列
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
// 第二个构造方法,直接传入 looper
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
MessageQueue的 enqueueMessage 方法 去入队列
- frameworks/base/core/java/android/os/MessageQueue.java
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
// 如果队列前面没有元素p == null,如果没有延时,时间为0,则马上执行,则直接入队列首位(链表实现),等待loop的轮询
// 排在队列第一位的先执行
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
// 遍历所有的message对象
for (;;) {
prev = p;
p = p.next;
//
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
如果上面的条件都不符合就进入else中,我们可以看到17行是有个for的死循环遍历已有的message对象,其中第20行有个if语句when < p.when when是新消息的执行时间,p.when的是队列中message消息的执行时间,如果找到比新的message还要晚执行的消息,就执行插入链表的操作:
msg.next = p;
prev.next = msg;
也就是把插到该消息的前面,优先执行新的消息。
2)创建Looper对象
在前面,可以看到主线程创建的时候会调用prepareMainLooper方法创建一个looper方法,所以应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了,无需在主线程创建looper。
- frameworks/base/core/java/android/os/Looper.java
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static void prepareMainLooper() {
// 在prepare 会创建个对象 Looper
// bool 参数是用于判断是否可以 退出循环,主线程为 false,不退出
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
// 创建对象 Looper,保存到 sThreadLocal中
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
// 通过 sThreadLocal获取对应创建的 Looper
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
sThreadLocal字段被static final修饰,说明是类共享的常量,该常量为每个线程都提供类Looper的副本。
如何保证一个线程中只有一个Looper? Looper的构造方法是private,只能在prepare中调用。并且如果一个ThreadLocal获取到相应的value,说明已经创建过。会抛出异常。在其构造函数中,每个looper对象都会绑定当前线程与一个消息队列
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
// 获取当前线程
mThread = Thread.currentThread();
}
一个最标准的异步消息处理线程的写法:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
// 在子线程中发送消息
public void click(View v){
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler.sendMessage();
Looper.loop();
}
}) .start();
}
3)使用 Looper开始工作
就是调用loop()方法:轮询获取消息Message
- frameworks/base/core/java/android/os/Looper.java
public static void loop() {
// 获取 Looper 对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
me.mSlowDeliveryDetected = false;
//
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
loopOnce 方法:
@SuppressWarnings("AndroidFrameworkBinderIdentity")
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 获取到 消息队列的元素
Message msg = me.mQueue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
// 将消息分发,发送给 自己定义的子类
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (me.mSlowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
me.mSlowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
me.mSlowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
return true;
}
Linux的一个进程间通信机制:管道(pipe)。原理:在内存中有一个特殊的文件,这个文件有两个句柄(引用),一个是读取句柄,一个是写入句柄
主线程Looper从消息队列读取消息,当读完所有消息时,进入睡眠,主线程阻塞。子线程往消息队列发送消息,并且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。所以不会过多消耗性能。
从MessageQueue读取数据
- frameworks/base/core/java/android/os/MessageQueue.java
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
// 获取当前时间
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 查找是否有异步消息在队列中
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
// 此处处理延时任务
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出对应的 message
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
nextPollTimeoutMillis是很重要的参数。正常循环时,此参数为0。当有延迟消息,消息没准备好时,则会设置一个延迟时间,让下一次循环执行。当message为空时,此参数会设置成-1。
nativePollOnce方法是一个本地方法,也是阻塞这个消息队列的方法。当前面参数为-1时,就会使消息队列陷入等待状态。
Handler 消息处理机制流程:
1.如果希望Handler正常工作,在当前线程中要有一个Looper对象
2.在初始化Looper对象时会创建一个与之关联的MessageQueue;
3.MessageQueue存放并管理Message
4.Message是Handler接收与处理的消息对象
所以,安卓源码的Handler处理分为 接受和处理2个大流程:
1. 发送消息: 通过post,send等方法(实质都是send)发送消息Message(runnable最后也转化为message)实现。最终都是调用MessageQueue的enqueueMessage方法将Message插入MessageQueue
2. 接收消息: 我们必须开启Looper.loop()来轮询,通过调用MessageQueue中的next()方法移除并获取之前发送的Message对象(MessageQueue为空时,next阻塞)。获得的Message对象最终交由发送该消息的Handler对象(message.target)的dispatchMessage方法处理。而dispatchMessage方法最后会走到handlerMessage()方法去。所以我们在创建Handler时才能够在其handlerMessage()或callback
4) 常见面试题
1. 主线程中的Looper.loop()一直无限循环为什么不会造成ANR?
因为当Looper处理完所有消息的时候会进入阻塞状态,当有新的Message进来的时候会打破阻塞继续执行。
这其实没理解好ANR这个概念。ANR,全名Application Not Responding。当我发送一个绘制UI 的消息到主线程Handler之后,经过一定的时间没有被执行,则抛出ANR异常。Looper的死循环,是循环执行各种事务,包括UI绘制事务。Looper死循环说明线程没有死亡,如果Looper停止循环,线程则结束退出了。Looper的死循环本身就是保证UI绘制任务可以被执行的原因之一。同时UI绘制任务有同步屏障,可以更加快速地保证绘制更快执行。
2. 主线程为什么不用初始化Looper?
因为应用在启动的过程中就已经初始化主线程Looper了。
每个java应用程序都是有一个main方法入口,Android是基于Java的程序也不例外。Android程序的入口在ActivityThread的main方法中:
public static void main(String[] args) {
...
// 初始化主线程Looper
Looper.prepareMainLooper();
...
// 新建一个ActivityThread对象
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 获取ActivityThread的Handler,也是他的内部类H
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
...
Looper.loop();
// 如果loop方法结束则抛出异常,程序结束
throw new RuntimeException("Main thread loop unexpectedly exited");
}
main方法中先初始化主线程Looper,新建ActivityThread对象,然后再启动Looper,这样主线程的Looper在程序启动的时候就跑起来了。我们不需要再去初始化主线程Looper。
3. Handler如何保证MessageQueue并发访问安全?
循环加锁,配合阻塞唤醒机制。
我们可以发现MessageQueue其实是“生产者-消费者”模型,Handler不断地放入消息,Looper不断地取出,这就涉及到死锁问题。如果Looper拿到锁,但是队列中没有消息,就会一直等待,而Handler需要把消息放进去,锁却被Looper拿着无法入队,这就造成了死锁。Handler机制的解决方法是循环加锁。在MessageQueue的next方法中:
Message next() {
...
for (;;) {
...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
...
}
}
}
我们可以看到他的等待是在锁外的,当队列中没有消息的时候,他会先释放锁,再进行等待,直到被唤醒。这样就不会造成死锁问题了。
那在入队的时候会不会因为队列已经满了然后一边在等待消息处理一边拿着锁呢?这一点不同的是MessageQueue的消息没有上限,或者说他的上限就是JVM给程序分配的内存,如果超出内存会抛出异常,但一般情况下是不会的。
4. Handler是如何切换线程的?
使用不同线程的Looper处理消息。
代码的执行线程,并不是代码本身决定,而是执行这段代码的逻辑是在哪个线程,或者说是哪个线程的逻辑调用的。每个Looper都运行在对应的线程,所以不同的Looper调用的dispatchMessage方法就运行在其所在的线程了。
5. Handler的阻塞唤醒机制是怎么回事?
答: Handler的阻塞唤醒机制是基于Linux的阻塞唤醒机制。
这个机制也是类似于handler机制的模式。在本地创建一个文件描述符,然后需要等待的一方则监听这个文件描述符,唤醒的一方只需要修改这个文件,那么等待的一方就会收到文件从而打破唤醒。和Looper监听MessageQueue,Handler添加message是比较类似的。
6. 解决Handler的内存泄漏方法
匿名内部类的Handler写法, 内部类持有外部类也就是activity的引起,造成内存泄漏。
最直接的思路就是避免使用非静态内部类。使用Handler的时候,放在一个新建的文件中来继承Handler或者使用静态的内部类来替代。静态内部类不会隐含的持有外部类的引用,因此这个activity也就不会出现内存泄漏问题。
两种解决办法如下:
-
弱引用(WeakReference)
public class SampleActivity extends Activity {
/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
* 弱引用的方式
*/
private static class MyHandler extends Handler {
private final WeakReference<SampleActivity> mActivity;
public MyHandler(SampleActivity activity) {
mActivity = new WeakReference<SampleActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SampleActivity activity = mActivity.get();
if (activity != null) {
//to Something
}
}
}
2. 使用内部静态类
//定义成static的,因为静态内部类不会持有外部类的引用
private final MyHandler mHandler = new MyHandler(this);
private static final Runnable sRunnable = new Runnable() {
@Override
public void run() {//to something}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
finish();
}
}
其实android中出现的大部分的内存泄漏都是Activity使用非静态内部类导致的,所以我们在使用内部类的时候要格外注意,如果其持有的引用超过了生命周期的范围,就极有可能会出现内存泄漏。
参考: