大家在编写android项目的时候应该用到Handler了吧.

一般做法应该都是先在主线程中创建一个Handler实例

Handler handler = new Handler{

public void handleMessage(Message msg) {
//做相应处理
}

}

然后通过handler.sendMessage(Message msg)发送消息.

最后在handleMessage()回调中做相应的处理.

那么这里有几个疑问:什么是主线程?发送消息到哪里去了?handleMessage()为什么会被回调?

1.主线程也就是UI线程,所有的处理用户消息,以及绘制界面的工作都在该线程中完成的,UI线程是从ActivityThread运行的,

分析点击android桌面app图标启动应用程序的过程这篇文章第三十二步三十三步中可以看到,第一次启动应用程序时,系统会为应用程序创建一个进程,从ActivityThread的main()方法开始执行,这个main()也就是UI线程的开始.

2.主线程中用handler发送消息消息发送到哪去了?这里我们来看一下main()方法,

在frameworks/base/core/java/android/app/ActivityThread.java中

public static void main(String[] args) {      ......        Looper.prepareMainLooper();......        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

这里调用了两个方法Looper.prepareMainLooper()和Looper.loop();

首先看第一个:

在frameworks/base/core/java/android/os/Looper.java中

public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }
接着看prepare()函数:

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

这里有一个sThreadLocal对象,这是什么呢

public class Looper {    private static final String TAG = "Looper";    // sThreadLocal.get() will return null unless you've called prepare().    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();........

它是Looper类的一个静态成员.ThreadLocal类作用是提供"线程局部存储",简单的理解是:ThreadLocal对象内部会根据调用prepare()函数的线程的id保存一个数据对象,这个数据对象就是所谓的"线程局部存储对象",像这里就是主线程调用的,那么就为主线程保存一个数据对象,这个数据对象就是Looper(上面的泛型就是Looper),第一次调用prepare()函数,sThreadLocal肯定还没有存储的,所以通过sThreadLocal.set()函数设置进去,对应get()函数就是取出来.所以从上面prepare()函数可以看出来,prepare()函数在一个线程中只能调用一次,因为第二次调用该函数,get()获取的就不是null,就会报异常.

一般一个应用程序都在一个进程中,所以ActivityThread类的main()函数只会执行一次,所以prepare()函数只会执行一次.

通过上面分析我们知道,每个线程通过prepareMainLooper()函数调用prepare()函数,设置一个Looper对象,那么它们保存的Looper对象是不相同的.

好了,继续看到主线程的Looper对象是如何构造的.sThreadLocal.set(new Looper(quitAllowed));

private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mRun = true;        mThread = Thread.currentThread();    }

这里MessageQueue是一个消息队列,上面handler发送的消息就是发送到这个队列里了.消息队列采用排队的方式对消息进行处理,即先到的消息会先得到处理,但如果消息本身指定了被处理的时刻,则必须等到该时刻才能处理该消息.对于消息队列我就没有深究了.

这里就是给Looper对象创建了一个消息队列.

然后回到prepareMainLooper()函数,接着看sMainLooper = myLooper();

 public static Looper myLooper() {        return sThreadLocal.get();    }

这一步其实就是把prepare()函数设置的 Looper对象赋值给sMainLooper变量.

ActivityThread类的main()函数里面的prepareMainLooper()函数做了两件事,第一是创建了一个MessageQueue(消息队列),第二是创建了一个Looper对象,一个线程只有一个Looper对象,因此只有一个MessageQueue(消息对象).

ActivityThread类的main()函数里面的另一个函数Looper.loop();

public static void loop() {        final Looper me = myLooper();        if (me == null) {            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");        }        final MessageQueue queue = me.mQueue;        // Make sure the identity of this thread is that of the local process,        // and keep track of what that identity token actually is.        Binder.clearCallingIdentity();        final long ident = Binder.clearCallingIdentity();        for (;;) {...
这个函数的就是从主线程的Looper对象的消息队列中不断的读取和发送消息.一个死循环吧.

读取消息然后回调对应handler的handleMessage(Message msg) 方法.

上面的三个问题我们了解差不多,但是又有新的疑问,new Handler()发送消息就怎么就会发送到主线程的Looper对象里面的消息队列里面去呢??

好了,我们来看一下Handler的构造方法:

 public Handler() {        this(null, false);    }    public Handler(Callback callback, boolean async) {     ...        mLooper = Looper.myLooper();//        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;        mCallback = callback;        mAsynchronous = async;    }

通过构造函数知道,在构造handler对象时(无参数),首先会取得当前线程对应的looper对象.如果该线程没有设置过looper对象,那么就会报异常.一般在我们在程序中new Handler()都是在主线程中,所以这次获取的是主线程的looper对象,而主线程的looper在程序启动的时候就创建好了(activitythread的main()方法里面),所以平时我们这样创建都没有报错.

这样一来我们也知道了,如果在一个线程中使用handler,那么该线程中必须创建了Looper对象.有了Looper对象也就有了消息队列(它在looper的构造方法里面创建).

这样线程称呼为异步消息线程.

总结一下:异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待.

android应用程序的主线程就这样一个异步消息线程.

那么除主线程外,我们自定义线程可不可定义成异步消息线程呢?当然可以,android提供了一个HandlerThread这个类

frameworks/base/core/java/android/os/HandlerThread.java

写一个线程MyThread 继承HandlerThread, 看它的run()方法

   public void run() {        mTid = Process.myTid();        Looper.prepare();        synchronized (this) {            mLooper = Looper.myLooper();            notifyAll();        }        Process.setThreadPriority(mPriority);        onLooperPrepared();        Looper.loop();        mTid = -1;    }    

是不是有点熟悉,就是先创建MessageQueue消息队列和Looper对象,然后进入死循环了.那么现在就可以使用handler向该线程的消息队列发送消息了,这里得注意了handler怎么与这个线程的消息队列关联起来.handler另外的构造函数:

 public Handler(Looper looper) {        this(looper, null, false);    }
没错,直接传入该线程的looper对象就可以该线程的looper对象怎么获取呢:

 public Looper getLooper() {        ....      return mLooper;    }

也就是MyThread对象调用getLooper()就可以了.

这样想要往这个线程发送消息,构建一个handler传入looper对象.

我们在项目用得最多的应该是往主线程发送更新UI的消息,因为更新UI必须要在主线程中,不然会报异常.







更多相关文章

  1. 细读《深入理解 Android(安卓)内核设计思想》(四)Binder 机制 [中]
  2. 关于Android(Java)创建匿名线程
  3. Android(安卓)通知栏Notification总结一:基本用法
  4. Android之使用MediaMetadataRetriever类获取媒体信息
  5. android解决:使用多线程和Handler同步更新UI
  6. Android(安卓)的消息机制(2)
  7. Android异步更新UI的方式之使用AsyncTask异步任务
  8. java 课堂总笔记
  9. Android多线程下安全访问数据库

随机推荐

  1. mysql 8.0.12 安装配置方法图文教程(windo
  2. mysql 8.0.11 安装步骤详解
  3. mysql 8.0.12 简单安装教程
  4. mysql 8.0.12 安装使用教程
  5. mysql 8.0.12 快速安装教程
  6. 彻底卸载mysql 个人亲测!
  7. Windows10下mysql 8.0.12解压版安装配置
  8. mysql 8.0.12 winx64解压版安装图文教程
  9. mysql5.7版本root密码登录问题的解决方法
  10. mysql 8.0.11安装配置方法图文教程