广

android开发

  • IOS开发
  • android开发
  • PHP编程
  • JavaScript
  • ASP.NET
  • ASP编程
  • JSP编程
  • Java编程
  • 易语言
  • Ruby编程
  • Perl编程
  • AJAX
  • 正则表达式
  • C语言
  • 编程开发

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

    2018-04-25 21:47:06 次阅读 稿源:互联网
    广告

    如果在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。

    代码如下:

    new Thread() {
      private Handler mHandler;
      @Override
      public void run() {
          Looper.prepare();
          mHandler = new Handler() {
                public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
          Log.i(LOGTAG, "testInNonMainLooperThread isMainThread="
                + isInMainThread());
          Looper.loop();
      }
         
    }.start();

    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一样。

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

    一起学吧部分文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与一起学吧进行文章共享合作。

    广告
    广告
    广告