Android中检测当前是否为主线程最可靠的解决方法
如果在Android中判断某个线程是否是主线程?对于这个问题,你可能说根据线程的名字,当然这个可以解决问题,但是这样是最可靠的么?万一某天Google一下子将线程的名字改称其他神马东西呢。
方法揭晓
下面的方法是最可靠的解决方案。
publicstaticbooleanisInMainThread(){
returnLooper.myLooper()==Looper.getMainLooper();
}
实际上,写到这里就基本解决了文章标题的问题了,但是仅仅研究到这里太肤浅了,刨的不够深,所以需要继续,希望你也可以继续读下去。
刨根问底
实验一
好,现在,我们对这个稳定的方法做一些测试,首先,下面的方法会增加一些调试打印信息。
privatebooleanisInMainThread(){
LoopermyLooper=Looper.myLooper();
LoopermainLooper=Looper.getMainLooper();
Log.i(LOGTAG,"isInMainThreadmyLooper="+myLooper
+";mainLooper="+mainLooper);
returnmyLooper==mainLooper;
}
好,然后我们在主线程中运行一个测试,调用上述方法。比如我们这样调用。
Log.i(LOGTAG,"testInMainThreadinMainThread="+isInMainThread());
OK,我们看一下输出日志。验证OK。
I/TestInMainThread(32028):isInMainThreadmyLooper=Looper{40d35ef8};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028):testInMainThreadinMainThread=true
实验二
现在我们继续在一个没有消息循环的非主线程,进行验证。
newThread(){
@Override
publicvoidrun(){
Log.i(LOGTAG,"testInNOTinMainThreadisMainThread="
+isInMainThread());
super.run();
}
}.start();
正如我们看到的如下日志结果,主线程的Looper(翻译成循环泵,不是很好听)已经被初始化赋值。但是我们新创建的线程的looper还是null。这是因为Android中的线程默认没有一个和它绑定了的消息循环(Threadsbydefaultdonothaveamessageloopassociatedwiththem.Ofcourse,themethodworks)
I/TestInMainThread(32028):isInMainThreadmyLooper=null;mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028):testInNOTinMainThreadisMainThread=false
实验三
继续,我们创建一个绑定了消息循环的线程,根据Android开发者文档说明,以下是一个典型的创建消息循环线程的示例,使用单独prepare()方法和loop()方法来创建一个绑定到Looper的Handler。
newThread(){
privateHandlermHandler;
@Override
publicvoidrun(){
Looper.prepare();
mHandler=newHandler(){
publicvoidhandleMessage(Messagemsg){
//processincomingmessageshere
}
};
Log.i(LOGTAG,"testInNonMainLooperThreadisMainThread="
+isInMainThread());
Looper.loop();
}
}.start();
OK,现在再次检查以下日志,
I/TestInMainThread(32028):isInMainThreadmyLooper=Looper{40d72c58};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028):testInNonMainLooperThreadisMainThread=false
两个Looper都被初始化赋值了,但是他们是不同的对象。
原理发掘
但是,这是为什么呢,这里面有什么奥秘呢?好,让我们看以下Looper.class
//sThreadLocal.get()willreturnnullunlessyou'vecalledprepare(). staticfinalThreadLocal<Looper>sThreadLocal=newThreadLocal<Looper>(); privatestaticLoopersMainLooper; //guardedbyLooper.class
/** *Initializethecurrentthreadasalooper,markingitasan *application'smainlooper.Themainlooperforyourapplication *iscreatedbytheAndroidenvironment,soyoushouldneverneed *tocallthisfunctionyourself. Seealso:{@link#prepare()} */ publicstaticvoidprepareMainLooper(){ prepare(false); synchronized(Looper.class){ if(sMainLooper!=null){ thrownewIllegalStateException("ThemainLooperhasalreadybeenprepared."); } sMainLooper=myLooper(); } }
privatestaticvoidprepare(booleanquitAllowed){ if(sThreadLocal.get()!=null){ thrownewRuntimeException("OnlyoneLoopermaybecreatedperthread"); } sThreadLocal.set(newLooper(quitAllowed)); }
/** *ReturntheLooperobjectassociatedwiththecurrentthread. *ReturnsnullifthecallingthreadisnotassociatedwithaLooper. */ publicstaticLoopermyLooper(){ returnsThreadLocal.get(); }
/**Returnstheapplication'smainlooper,whichlivesinthemainthreadoftheapplication. */ publicstaticLoopergetMainLooper(){ synchronized(Looper.class){ returnsMainLooper; } }