handler使用:
1)若在Activity主线程中直接用handler - handler和looper在主线程
handler和looper均在主线程,这里可以不需要自己调looper,因为activity的ActivityThread中已经创建并调了loop,而且由于handler一般是用来更新主线程UI的,所以需要保证消息分发来自同一个线程,否则界面更新处理来自多个looper发送的消息会出现布局混乱,而加锁等线程同步会让更新变的更慢还可能出现死锁,得不偿失。
ActivityThread的main函数代码段最下面
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// Install selective syscall interception
AndroidOs.install();
long startTime = CheckTime.getTime();
// 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);
Process.setArgV0("<pre-initialized>");
...
Looper.prepareMainLooper(); //here prepare 创建looper
...
ActivityThread thread = new ActivityThread();
...
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
CheckTime.checkTime(startTime, "ActivityThreadMain", 400);
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //looper.loop 从messageQueneu取消息分发
throw new RuntimeException("Main thread loop unexpectedly exited");
}
上面代码可看到ActivityThread中已经有调用prepareMainLooper,所以当我们在Activity主线程中调用Looper.prepare时,会报 E AndroidRuntime: Caused by: java.lang.RuntimeException: Only one Looper may be created per thread 这个crash.
这个异常抛出位置即在Looper.java的这里,这里sThreadLocal可以看到是static修饰的,该ThreadLocale里是有个map,以当前线程为key,value为传入的值。所以当get到当前线程中loop值不为null,即抛出该异常。
也是因为sLocaleThread,所以Handler可以轻松的取到每个线程的looper,从而获取到messageQueue中的消息
// sThreadLocal.get() will return null unless you've called prepare().
@UnsupportedAppUsage
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}
2)在Activity主线程中创建子线程,并使用handle更新UI - handler和looper在子线程中
如上,这种方式即常用handler方式,在子线程中做一些耗时操作,完成后发送handler消息,在主线程中更新UI。这里使用时就需要自己调用Loop的prepare在当前子线程创建的looper的Thread及MessageQueue做事件分发
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
上面prepare代码中调到的new Looper()即这块代码,thread 相当于looper这里可以看到looper和MessageQueue是一一对应的。
3)使用HandlerThread - handler和looper在子线程
android 开发现在更推崇HandlerThread 这种方式来实现,HandlerThread已经帮我们创建好子线程,并prepare,loop了looper
HandlerThread继承Thread,重写了run方法,在run方法中创建looper并loop
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
当使用该HandlerThread get looper时,如下,可以看到如果子线程是alive的,则会等待looper被创建,并返回。所以这里返回的looper是运行在子线程中的。
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}