android Handler分析

来源:互联网 发布:智能手机自动开机软件 编辑:程序博客网 时间:2024/06/07 05:02

在 android 获取网络数据同时更新UI时, 经常用到Handler。 由于 android 系统规定只能在 主线程中才能更新UI界面, 而主线程中进行耗时的操作容易造成程序阻塞崩溃, 所以耗时的网络操作又必须放到子线程中, 这时我们可以通过Handler从子线程获取必要的信息到主线程中进行UI更新。默认情况下Handler和主线程相关联, 所以说Handler的handleMessage()方法在主线程运行。

Handler的一般用法:

public class MainActivity extends AppCompatActivity {    private static final int UPDATE_UI = 1;    private TextView mTextView;    private Handler mHandler = new Handler() {        @Override        public void dispatchMessage(Message msg) {            super.dispatchMessage(msg);        }        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case UPDATE_UI: {                    mTextView.setText(mTextView.getText().toString() + (int) msg.obj);                    break;                }                default: break;            }        }    };    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (TextView) findViewById(R.id.text_view);        new Thread(new Runnable() {            @Override            public void run() {                Message message = Message.obtain(mHandler, UPDATE_UI, 3);                message.sendToTarget();            }        }).start();    }}
我创建了一个Handler实例,重写 handleMessage() 方法,主要在该方法里进行UI相关的操作, 因为这时候的操作已经在主线程中了。

在子线程中创建Message实例, 通过Handler发送该消息, 就可以讲该消息放送到主线程中。 接下来根据源码来分析Handler的处理过程。


1、消息队列MessageQueue

对于Handler发送的消息, 最后都会调用MessageQueue的enqueueMessage()方法,将消息存放在队列中,该方法主要代码:

synchronized (this) {            if (mQuitting) {                IllegalStateException e = new IllegalStateException(                        msg.target + " sending message to a Handler on a dead thread");                Log.w(TAG, e.getMessage(), e);                msg.recycle();                return false;            }            msg.markInUse();            msg.when = when;            Message p = mMessages;            boolean needWake;            if (p == null || when == 0 || when < p.when) {                // New head, wake up the event queue if blocked.                msg.next = p;                mMessages = msg;                needWake = mBlocked;            } else {                // Inserted within the middle of the queue.  Usually we don't have to wake                // up the event queue unless there is a barrier at the head of the queue                // and the message is the earliest asynchronous message in the queue.                needWake = mBlocked && p.target == null && msg.isAsynchronous();                Message prev;                for (;;) {                    prev = p;                    p = p.next;                    if (p == null || when < p.when) {                        break;                    }                    if (needWake && p.isAsynchronous()) {                        needWake = false;                    }                }                msg.next = p; // invariant: p == prev.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }
通过链表的方式, 当前消息 msg 根据时间 when 插入到链表中。

Looper的loop()方法会将消息从队列中取出,主要通过调用MessageQueue的next() 方法,next()方法返回队列消息,同时将当前事件从队列中删除,next()方法主要代码:

for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {                // Try to retrieve the next message.  Return if found.                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }                // Process the quit message now that all pending messages have been handled.                if (mQuitting) {                    dispose();                    return null;                }                // If first time idle, then get the number of idlers to run.                // Idle handles only run if the queue is empty or if the first message                // in the queue (possibly a barrier) is due to be handled in the future.                if (pendingIdleHandlerCount < 0                        && (mMessages == null || now < mMessages.when)) {                    pendingIdleHandlerCount = mIdleHandlers.size();                }                if (pendingIdleHandlerCount <= 0) {                    // No idle handlers to run.  Loop and wait some more.                    mBlocked = true;                    continue;                }                if (mPendingIdleHandlers == null) {                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];                }                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);            }
这是一段死循环代码,如果没有新的消息加入队列, 就会一直处于next() 方法中。 如果消息队列中有消息, 调用该方法,对应的消息从链表中删除。

MessageQueue最终要的就是这2个功能, 还是很容易理解的。

2、Looper

Looper会不断的从消息队列中查看是否有新消息, 如果有新消息, 那就将其取出处理, 否则就会阻塞。Looper创建时会创建一个消息队列, 同时获得当前线程:

 private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread(); }
该构造方法是一个私有的,Looper.prepare()方法内部调用了Looper的构造函数,也就是会说调用prepare()方法,才算构建了Looper实例。

loop()方法被调用后会在当前线程中不断查看消息队列,处理消息事件,分析一下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            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            final long traceTag = me.mTraceTag;            if (traceTag != 0) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            try {                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            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();        }    }

首先在该方法中会获取当前线程和消息队列,之后是一个死循环,不断地查看消息队列中的信息。 调用MessageQueue的next()方法获取队列中的消息, 由于next() 方法也是一个死循环, 而且没有消息的时候会造成阻塞, 所以当队列中无消息的时候,loop()方法会阻塞。 如果next()方法返回了消息, 会调用 msg.target.dispatchMessage(msg); 也就是调用发送消息的Handler的dispatchMessage()方法。这样事件消息就到了发送消息的Handler的线程中了。所以可以通过Looper将消息发送到指定的线程中。


3、 Handler

在Looper中我们调用Handler的dispatchMessage()方法,分发消息。查看Handler的该方法的代码:

    /**     * Handle system messages here.     */    public void dispatchMessage(Message msg) {        if (msg.callback != null) {            handleCallback(msg);        } else {            if (mCallback != null) {                if (mCallback.handleMessage(msg)) {                    return;                }            }            handleMessage(msg);        }    }

如果当前消息设置了Runnable,会调用 handleCallback() 方法,该方法内部调用了Message.callback.run()。如果没有设置Runnable,会判断当前Handler是否设置了Callback接口,如果设置了接口就会回调handleMessage()方法,不然调用Handler本身的handleMessage()方法。 Handler的handleMessage()方法是一个空方法,根据需要重写该方法。


现在整个消息的处理过程应该是一目了然了。 

Handler将消息发送至消息队列,Looper不断在后台获取消息,调用Handler的dispatchMessage()方法分发消息到指定的线程中,最后处理消息。


0 0
原创粉丝点击