Android Handler、Loop、MessageQueue的工作原理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liuwan1992/article/details/52838587

为了更好地理解 Handler 的工作原理,下面先介绍一下与 Handler 一起工作的几个组件。

        Message :Handler 接收和处理的消息对象。

        Looper :每个线程只能拥有一个 Looper 。它的 loop 方法负责读取 MessageQueue 中的消息,读到消息之后就把消息交给发送

消息的 Handler 处理。

        MessageQueue :消息队列,它采用先进先出的方式管理 Message 。程序创建 Looper 对象时,会在它的构造器中创建 

MessageQueue 对象。

Looper 的构造器代码如下:

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
        该构造器使用了 private 修饰,表明程序员无法通过构造器创建 Looper 对象。从上面的代码不难看出,程序在初始化 Looper 时

创建一个与之关联的 MessageQueue ,这个 MessageQueue 就负责管理消息。

        Handler :它的作用有两个——发送消息和处理消息,程序使用 Handler 发送消息,由 Handler 发送的消息必须被送到指定的 

MessageQueue 。也就是说,如果希望 Handler 正常工作,必须在当前线程中有一个 MessageQueue ;否则消息就没有 

MessageQueue 进行保存了。不过 MessageQueue 是由 Looper 负责管理的,也就是说,如果希望 Handler 正常工作,必须在当前

线程中有一个 Looper 对象。为了保证当前线程中有 Looper 对象,可以分如下两种情况处理。

        在主 UI 线程中,系统已经初始化了一个 Looper 对象,因此程序直接创建 Handler 即可,然后就可通过 Handler 来发送消息、处

理消息了。

        程序员自己启动的子线程,必须自己创建一个 Looper 对象,并启动它。创建 Looper 对象调用它的 prepare() 方法即可。

        prepare() 方法保证每个线程最多只有一个 Looper 对象。prepare() 方法的源代码如下:

    public static void prepare() {
        prepare(true);
    }

    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));
    }
        接下来调用 Looper 的静态 loop() 方法来启动它。loop() 方法使用一个死循环不断取出 MessageQueue 中的消息,并将取出的消

息分给该消息对应的 Handler 进行处理。下面是 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 (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
下面分别演示了 Looper 的两种处理情况:

public class LooperActivity extends Activity {

    private Button button;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Toast.makeText(LooperActivity.this, "接收消息:" + msg.obj, Toast.LENGTH_LONG).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                message.obj = "jack";
                handler.sendMessage(message);
            }

        });
    }

}
public class LooperActivity2 extends Activity {

    private Button button;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new MyThread()).start();

        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                Message message = Message.obtain();
                message.obj = "jack";
                handler.sendMessage(message);
            }

        });
    }

    public class MyThread implements Runnable {

        @Override
        public void run() {
            // 循环消息队列
            Looper.prepare();
            handler = new Handler() {

                @Override
                public void handleMessage(Message msg) {
                    Toast.makeText(LooperActivity2.this, "从UI主线程中接收消息:" + msg.obj, Toast.LENGTH_LONG).show();
                }

            };
            // 直到消息队列循环结束
            Looper.loop();
        }

    }

}

归纳起来,Looper、MessageQueue、Handler 各自的作用如下。

        Looper :每个线程只有一个 Looper ,它负责管理 MessageQueue ,会不断地从 MessageQueue 中取出消息,并将消息分给

对应的 Handler 处理。

        MessageQueue :由 Looper 负责管理,它采用先进先出的方式来管理 Message 。

        Handler :它能把消息发送给 Looper 管理的 MessageQueue ,并负责处理 Looper 分给它的消息。

在线程中使用 Handler 的步骤如下。

        1、调用 Looper 的 prepare() 方法为当前线程创建 Looper 对象,创建 Looper 对象时,它的构造器会创建与之配套的 

MessageQueue 。

        2、有了 Looper 之后,创建 Handler 子类的实例,重写 handleMessage() 方法,该方法负责处理来自于其他线程的消息。

        3、调用 Looper 的 loop() 方法启动 Looper。

猜你喜欢

转载自blog.csdn.net/liuwan1992/article/details/52838587
今日推荐