Android Handler消息机制从原理到应用详解

来源:互联网 发布:天津航空机电知乎 编辑:程序博客网 时间:2024/06/06 22:16

对于跨进程IPC通信,Android提供了一个完整的框架Binder,而对于线程之间的消息传递,Android同样提供了一个强大的消息机制:Handler/Looper/MessageQueue,通过Handler我们很容易的实现在UI线程与其他线程之间的消息传递。这篇文章,就来看一看Android消息机制的具体应用以及原理。主要有3个方面:

  • Android消息机制相关的基本概念;
  • Android Handler的具体应用实例;
  • 从源码的角度直接深入了解Handler/Looper/MessageQueue的机制;

首先来看下相关的概念。

基本概念

Handler

一个Handler用于发送或者处理与一个线程的消息队列(MessageQueue)相关联的消息或者可执行对象(Runnable)。每个Handler实例跟一个线程以及线程消息队列相关联。当新建一个Handler实例时,该Handler就跟创建它的线程以及线程队列绑定在一起。此后,它就会将消息以及可执行对象传送到消息队列中,并且处理从消息队列中出来的消息。

一般,Handler有两个用途:(1)对消息以及可执行对象进行调度,以便未来某个时间点执行之;(2)向另一个线程发送一个消息或者执行对象。

当应用程序进程创建完成后,主线程负责运行一个用于管理应用内的对象,如activities,broadcast receivers的消息队列。用户可以创建自己的线程,通过一个Handler跟主线程进行通信。

Looper

Looper用于为线程运行一个消息循环;线程默认是没有消息循环的,要建立一个新的Looper,首先需要在线程内调用prepare,然后调用loop进入消息处理。

MessageQueue

MessageQueue用于保存被looper分发出来的消息。消息并不是直接添加到MessageQueue中的,而是通过跟looper相关联的Handler进行传递。

Android应用实例

在Android应用开发过程中,为了保持UI的响应,通常需要将一些耗时的操作放到非UI线程,然后将结果返回到UI线程。这里,假如我们需要从网络上下载一副图片,然后在ImageView中显示。对于网络访问这种耗时的操作,启动一个新的线程来运行。因此,首先我们自定义一个线程类用于下载图片:

    /**     * thread to download image from a given URL     */    public class DownloadThread extends Thread{        private static final String LOG_TAG = "DownloadThread";        private static final int MSG_DOWNLOAD_IMAGE = 0x01;        private static final int DEFAULT_CONNECT_TIMEOUT = 10*1000;        private static EventHandler mHandler = new EventHandler();        private Handler mMainHandler; // main thread handler        public DownloadThread(Handler main){            super(LOG_TAG);            mMainHandler = main;        }        @Override        public void run(){            //启动线程消息循环            Looper.prepare();            // 新建一个线程Handler            //mHandler = new EventHandler();            //进入消息循环            Looper.loop();        }        public void addNewTask(String url){            if(URLUtil.isNetworkUrl(url)){                Message msg = mHandler.obtainMessage(MSG_DOWNLOAD_IMAGE,url);                mHandler.sendMessage(msg);            }else{                //向主线程发送错误消息                String error = "illegal format image url";                Message msg = mMainHandler.obtainMessage(MainActivity.MSG_IMAGE_DOWNLOAD_FAIL,error);                msg.sendToTarget();            }        }        private void downloadImage(String imgUrl){            Log.v(LOG_TAG, "downloadImage(): url = " + imgUrl);            // notify main thread that it starts            Message msg = mMainHandler.obtainMessage(MainActivity.MSG_IMAGE_DOWNLOAD_START);            msg.sendToTarget();            InputStream in = null;            try {                // configure Http connection                URL myUrl = new URL(imgUrl);                HttpURLConnection urlCnn = (HttpURLConnection)myUrl.openConnection();                urlCnn.setDoInput(true);                urlCnn.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT);                // get input stream from the given url                in = urlCnn.getInputStream();                Bitmap bitmap = BitmapFactory.decodeStream(in);                Bundle bundle = new Bundle();                bundle.putParcelable("bitmap",bitmap);                // send message containing data to the main thread                Message msg1 = mMainHandler.obtainMessage(MainActivity.MSG_IMAGE_DOWNLOAD_SUCCESS);                msg1.setData(bundle);                msg1.sendToTarget();            }catch (MalformedURLException e){                e.printStackTrace();                String error = "wrong format url";                Message message = mMainHandler.obtainMessage(MainActivity.MSG_IMAGE_DOWNLOAD_FAIL,error);                message.sendToTarget();            }catch (IOException e){                e.printStackTrace();                String error = "network connection error";                Message message = mMainHandler.obtainMessage(MainActivity.MSG_IMAGE_DOWNLOAD_FAIL,error);                message.sendToTarget();            }        }        // 自定义一个事件处理Handler        private final class EventHandler extends Handler{            @Override            public void handleMessage(Message msg){                int w = msg.what;                switch (w){                    case MSG_DOWNLOAD_IMAGE:                        String url = (String)msg.obj;                        downloadImage(url);                        break;                    default:                        break;                }            }        }    }

注意,启动一个新的线程时,需要调用prepareloop两个函数,以确保消息循环处于运行状态。接着,在MainActivity中启动该线程用于下载图片:

    public class MainActivity extends AppCompatActivity {        private static final String TAG = MainActivity.class.getSimpleName();        public static final int MSG_IMAGE_DOWNLOAD_START = 0x01;        public static final int MSG_IMAGE_DOWNLOAD_FAIL = 0x02;        public static final int MSG_IMAGE_DOWNLOAD_SUCCESS = 0x03;        private Handler mH;        @Override        protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);            setContentView(R.layout.activity_main);            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);            setSupportActionBar(toolbar);            mIvFilm = (ImageView)findViewById(R.id.iv_film);            mH = new H();        }        @Override        public void onResume(){            super.onResume();            DownloadThread downloadThread = new DownloadThread(mH);            downloadThread.start();            String url = "http://image.baidu.com/search/redirect?tn=redirect&word=j&juid=9127CC&sign=ciwziioaoz&url=http%3A%2F%2Fwww.4493.com%2Fmotemeinv%2F6156%2F1.htm&objurl=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fdc54564e9258d109a4d1165ad558ccbf6c814d23.jpg";            // 下载图片            downloadThread.addNewTask(url);        }        // 自定义一个Handler对象,用于处理UI线程的消息        private final class H extends Handler{            @Override            public void handleMessage(Message msg){                int w = msg.what;                switch (w){                    case MSG_IMAGE_DOWNLOAD_START:                        Toast.makeText(MainActivity.this,"start download",Toast.LENGTH_SHORT).show();                        break;                    case MSG_IMAGE_DOWNLOAD_SUCCESS:                        Bitmap bitmap = msg.getData().getParcelable("bitmap");                        mIvFilm.setImageBitmap(bitmap);                        Toast.makeText(MainActivity.this,"download complete",Toast.LENGTH_SHORT).show();                        break;                    case MSG_IMAGE_DOWNLOAD_FAIL:                        Toast.makeText(MainActivity.this,"download failure",Toast.LENGTH_SHORT).show();                        break;                    default:                        break;                }            }        }    }

可见,对于一个新的线程来说,使用Handler来处理消息或者可执行对象时,需要做如下几件事情:

  1. 自定义一个Handler,用于处理Looper发送过来的消息;
  2. 启动线程消息循环: Looper.prepare(),将线程与Looper进行绑定;
  3. 进入消息循环: Looper.loop(),等待接收并处理消息。

Handler原理详解

如下图所示,为HandlerLooper以及MessageQueue三者之间的关系。

  • Looper不断查询消息队列中的消息,如果发现有新的消息,则将其发送给对应的Handler执行;
  • Handler接收到来自Looper的消息后,对其进行处理;
  • MessageQueue接收发自从Handler产生的消息,将其放入队列;

Handler_Looper_MessageQueue

明白了三者的作用,接下来就来看一看具体的实现细节。

线程与Looper的绑定

调用prepare()函数后,新建一个Looper实例与线程进行绑定,并将其保存在线程本地变量中。

    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));    }    ...    // 新建一个与该线程对应的MessageQueue    private Looper(boolean quitAllowed) {        mQueue = new MessageQueue(quitAllowed);        mThread = Thread.currentThread();    }

源码:/android/frameworks/base/core/java/android/os/Looper.java

同时,创建一个消息队列:

    public final class MessageQueue {        // True if the message queue can be quit.        private final boolean mQuitAllowed;        @SuppressWarnings("unused")        private long mPtr; // used by native code        private boolean mQuitting;        ....        private native static long nativeInit();        private native static void nativeDestroy(long ptr);        private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/        private native static void nativeWake(long ptr);        private native static boolean nativeIsPolling(long ptr);        private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);        MessageQueue(boolean quitAllowed) {            mQuitAllowed = quitAllowed;            //本地初始化            mPtr = nativeInit();        }        ....    }

源码: /android/frameworks/base/core/java/android/os/MessageQueue.java

初始化本地消息队列(新建一个本地消息队列):

    static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {        NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();        if (!nativeMessageQueue) {            jniThrowRuntimeException(env, "Unable to allocate native queue");            return 0;        }        nativeMessageQueue->incStrong(env);        return reinterpret_cast<jlong>(nativeMessageQueue);    }    // 本地消息队列    NativeMessageQueue::NativeMessageQueue() :            mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {        //首次为空        mLooper = Looper::getForThread();        if (mLooper == NULL) {            mLooper = new Looper(false);            Looper::setForThread(mLooper);        }    }

源码: /android/frameworks/base/core/jni/android_os_MessageQueue.cpp

新建一个本地Looper,监听I/O事件:

    Looper::Looper(bool allowNonCallbacks) :            mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),            mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),            mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {        mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);        LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",                            strerror(errno));        AutoMutex _l(mLock);        rebuildEpollLocked();    }    void Looper::rebuildEpollLocked() {        // Close old epoll instance if we have one.        if (mEpollFd >= 0) {            close(mEpollFd);        }        // Allocate the new epoll instance and register the wake pipe.        mEpollFd = epoll_create(EPOLL_SIZE_HINT);        struct epoll_event eventItem;        memset(& eventItem, 0, sizeof(epoll_event));         eventItem.events = EPOLLIN;        eventItem.data.fd = mWakeEventFd;        int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem);        for (size_t i = 0; i < mRequests.size(); i++) {            const Request& request = mRequests.valueAt(i);            struct epoll_event eventItem;            request.initEventItem(&eventItem);            int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, request.fd, & eventItem);            if (epollResult < 0) {                ALOGE("Error adding epoll events for fd %d while rebuilding epoll set: %s",                      request.fd, strerror(errno));            }        }    }

源码: /android/system/core/libutils/Looper.cpp

有关异步I/O EPOLL相关资料:

  1. http://davmac.org/davpage/linux/async-io.html
  2. poll/select/epoll 性能比较
  3. https://linux.die.net/man/4/epoll
  4. select-poll-epoll-practical-difference-for-system-architects/
  5. http://blog.lucode.net/linux/epoll-tutorial.html

线程进入消息循环

线程与Looper绑定后,运行消息队列,准备处理消息:

    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;        for (;;) {            // 查询消息            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            ....            try {                // 发送消息到目标Handler                msg.target.dispatchMessage(msg);            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            ....            msg.recycleUnchecked();        }    }

不断查询消息队列,有消息时直接返回消息:

    public final class MessageQueue {        // 查询可用的消息        Message next() {            // mPtr指向本地的nativeMessageQueue            final long ptr = mPtr;            int pendingIdleHandlerCount = -1; // -1 only during first iteration            int nextPollTimeoutMillis = 0;            for (;;) {                // 查询本地消息                nativePollOnce(ptr, nextPollTimeoutMillis);                synchronized (this) {                    Message prevMsg = null;                    Message msg = mMessages;                    if (msg != null && msg.target == null) {                        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 {                            // 获得消息,返回                            mBlocked = false;                            if (prevMsg != null) {                                prevMsg.next = msg.next;                            } else {                                mMessages = msg.next;                            }                            msg.next = null;                            msg.markInUse();                            return msg;                        }                    } else {                        // No more messages.                        nextPollTimeoutMillis = -1;                    }                    ....                    //消息队列为空,或者第一个消息尚未就绪                    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);                }                //第一次才会执行:空闲等待                for (int i = 0; i < pendingIdleHandlerCount; i++) {                    final IdleHandler idler = mPendingIdleHandlers[i];                    mPendingIdleHandlers[i] = null; // release the reference to the handler                    boolean keep = false;                    try {                        keep = idler.queueIdle();                    } catch (Throwable t) {                        Log.wtf(TAG, "IdleHandler threw exception", t);                    }                    if (!keep) {                        synchronized (this) {                            mIdleHandlers.remove(idler);                        }                    }                }                // Reset the idle handler count to 0 so we do not run them again.                pendingIdleHandlerCount = 0;            }        }    }

目标Handler接收到消息,直接调用dispatchMessage():

    public class Handler {        public void dispatchMessage(Message msg) {            // 有回调,直接调用回调函数            if (msg.callback != null) {                handleCallback(msg);            } else {                // handler本身有回调函数,调用回调函数处理消息                if (mCallback != null) {                    if (mCallback.handleMessage(msg)) {                        return;                    }                }                // 调用自定义的函数处理消息                handleMessage(msg);            }        }        // 执行回调        private static void handleCallback(Message message) {            message.callback.run();        }    }

(全文完)

参考文献

  • http://gityuan.com/2015/12/26/handler-message-framework/
0 0
原创粉丝点击