zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android中检测当前是否为主线程最可靠的解决方法

Android方法线程 解决 是否 检测 当前 可靠
2023-06-13 09:15:40 时间

如果在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;
   }
}

对于主线程来说,prepareMainLooper这个方法会被Android运行环境调用,而不是程序显式调用。通过这个方法,主线程的looper被创建,并且将对象引用传递给sMainLooper。所以保证了主线程myLooper()获取到的引用和getMainLooper()获取到的都是同一个引用。

对于没有消息循环的非主线程,默认的当前线程的looper是null,因为你从来没有手动地调用prepare(),所以它和主线程的looper不一样。

对于绑定了消息循环的非主线程,当调用Looper.prepare方法时,主线程的Looper已经由Android运行环境创建,当调用prepare方法后,绑定到这个非主线程的looper被创建,当然,这不可能和主线程的Looper一样。

综上所述,这个方法是可靠的。