如何在Android中判断某个线程是否是主线程

转帖: http://droidyue.com/blog/2014/07/12/check-main-thread-in-android-chinese-edition/

如何在Android中判断某个线程是否是主线程?对于这个问题,你可能说根据线程的名字,当然这个可以解决问题,但是这样是最可靠的么?万一某天Google一下子将线程的名字改称其他神马东西呢。

方法揭晓

下面的方法是最可靠的解决方案。

public static boolean isInMainThread(){
   return Looper.myLooper() == Looper.getMainLooper();
}

实际上,写到这里就基本解决了文章标题的问题了,但是仅仅研究到这里太肤浅了,刨的不够深,所以需要继续,希望你也可以继续读下去。

刨根问底

实验一

好,现在,我们对这个稳定的方法做一些测试,首先,下面的方法会增加一些调试打印信息。

private boolean isInMainThread(){
  Looper myLooper = Looper.myLooper();
  Looper mainLooper = Looper.getMainLooper();
  Log.i(LOGTAG,"isInMainThread myLooper="+myLooper+";mainLooper="+mainLooper);
  return myLooper == mainLooper;
}

好,然后我们在主线程中运行一个测试,调用上述方法。比如我们这样调用。

Log.i(LOGTAG,"testInMainThread inMainThread=" + isInMainThread());

OK,我们看一下输出日志。验证OK。

I/TestInMainThread(32028): isInMainThread myLooper=Looper{40d35ef8};mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testInMainThread inMainThread=true

实验二

现在我们继续在一个没有消息循环的非主线程,进行验证。

new Thread(){
  @Override public void run() {
      Log.i(LOGTAG,"testIn NOT in MainThread isMainThread="+isInMainThread());
      super.run();
  }
}.start();

正如我们看到的如下日志结果,主线程的Looper(翻译成循环泵,不是很好听)已经被初始化赋值。但是我们新创建的线程的looper还是null。这是因为Android中的线程默认没有一个和它绑定了的消息循环Threads by default do not have a message loop associated with them. Of course, the method works

I/TestInMainThread(32028): isInMainThread myLooper = null; mainLooper = Looper{40d35ef8}
I/TestInMainThread(32028): testIn NOT in MainThread isMainThread=false

实验三

继续,我们创建一个绑定了消息循环的线程,根据Android开发者文档说明,以下是一个典型的创建消息循环线程的示例,使用单独prepare()方法和loop()方法来创建一个绑定到Looper的Handler。

OK,现在再次检查以下日志,

I/TestInMainThread(32028): isInMainThread myLooper=Looper{40d72c58}; mainLooper=Looper{40d35ef8}
I/TestInMainThread(32028): testInNonMainLooperThread isMainThread=false

两个Looper都被初始化赋值了,但是他们是不同的对象。

原理发掘

但是,这是为什么呢,这里面有什么奥秘呢? 好,让我们看以下Looper.class

// sThreadLocal.get() will return null unless you‘ve called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

/**
 * Initialize the current thread as a looper, marking it as an
 * application‘s main looper. The main looper for your application
 * is created by the Android environment, so you should never need
 * to call this function yourself.  See also: {@link #prepare()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

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));
}

/**
 * Return the Looper object associated with the current thread.
 * Returns null if the calling thread is not associated with a Looper.
 */
public static Looper myLooper() {
    return sThreadLocal.get();
}

 /** Returns the application‘s main looper, which lives in the main thread of the application.
 */
public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}

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

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

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

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

引用:

其他

时间: 2024-10-21 07:48:12

如何在Android中判断某个线程是否是主线程的相关文章

如何在Android中为TextView动态设置drawableLeft等

如何在Android中为TextView动态设置drawableLeft等 两种方式: 方式1:手动设置固有边界 1 Drawable drawable = getResources().getDrawable(resId); 2 //注意查看方法TextView.setCompoundDrawables(Drawable, Drawable, Drawable, Drawable) 3 //的注释,要求设置的drawable必须已经通过Drawable.setBounds方法设置过边界参数 4

如何在Android中播放网络传输过来的AAC音频?

============问题描述============ 如何在Android中播放网络传输过来的AAC音频?每个包都有ADTS头的. ============解决方案1============ 如果是应用的话,直接设置MediaPlayer setDataSource设置为网络音频的uri,然后prepareAsync(), prepare完成后调用start不就可以了吗? framework层的话还是要用aacdecoder进行解码播放,要么用软件解码,要么用硬件解码.

【原创】如何在Android中为TextView动态设置drawableLeft等

如何在Android中为TextView动态设置drawableLeft等 两种方式: 方式1:手动设置固有边界 1 Drawable drawable = getResources().getDrawable(resId); 2 //注意查看方法TextView.setCompoundDrawables(Drawable, Drawable, Drawable, Drawable) 3 //的注释,要求设置的drawable必须已经通过Drawable.setBounds方法设置过边界参数 4

Android中判断网络连接是否可用及监控网络状态

Android中判断网络连接是否可用及监控网络状态 作者: 字体:[增加 减小] 类型:转载 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限,接下来详细介绍Android中判断网络连接是否可用及监控网络状态,感兴趣的朋友可以参考下 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"

十九、android中判断sim卡状态和读取联系人资料的方法

在写程序中,有时候可能需要获取sim卡中的一些联系人资料.在获取sim卡联系人前,我们一般会先判断sim卡状态,找到sim卡后再获取它的资料,如下代码我们可以读取sim卡中的联系人的一些信息. PhoneTest.java package com.android.test; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.datab

Android中Handler的使用方法——在子线程中更新界面

本文主要介绍Android的Handler的使用方法.Handler可以发送Messsage和Runnable对象到与其相关联的线程的消息队列.每个Handler对象与创建它的线程相关联,并且每个Handler对象只能与一个线程相关联. Handler一般有两种用途:1)执行计划任务,你可以再预定的实现执行某些任务,可以模拟定时器.2)线程间通信.在Android的应用启动时,会创建一个主线程,主线程会创建一个消息队列来处理各种消息.当你创建子线程时,你可以再你的子线程中拿到父线程中创建的Han

如何在android中利用Theme设置application 的所有listview的style?~

今天看VLC的源代码,发现一个很神奇的东西 所有listview的点击效果背景色都是橘黄色 花了点时间找了一下看看怎么实现的. 首先,定义一个<selector> like this: <selector> <item android:state_pressed="true"> <shape android:shape="rectangle"> <solid android:color="@color/

android中判断sim卡状态和读取联系人资料的方法

在写程序中,有时候可能需要获取sim卡中的一些联系人资料.在获取sim卡联系人前,我们一般会先判断sim卡状态,找到sim卡后再获取它的资料,如下代码我们可以读取sim卡中的联系人的一些信息. PhoneTest.java package com.android.test; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.datab

可动态增减的线程池,主线程accept——基于UNP代码修改

可动态增减的线程池,主线程accept——基于UNP代码修改 1.说明 线程池基于一个区间动态变化,在客户连接过多线程不够用时,动态增加一定数量的线程.在线程闲置数量多于一半时,动态减小线程数量到一个基准线. 这个例子模式为:半同步/半异步(half-sync/half-async) 2.代码相关说明 代码基于UNP的库函数,要想运行必须先安装相应库. 3.代码 #include "unpthread.h" #include <queue> #include <lis