Lee_3do的博客

Looper Handler AsyncTask

2015-11-04  本文已影响60人  lee_3do

前置说明

Android 的UI操作并非线程安全的,所以Android制定了一条规则:只能在主线程中更新UI.
当然这条规则存在一些例外:

http://www.zhihu.com/question/24764972
关于这个例外,大致就是如果在onCreate期间在非主线程更新了UI,基于一些时序的情况,有可能是不报错更新UI成功的;另外progressBar等一些UI组件的方法加了synchronized方法,是可以在非主线程中更新的;而Toast的显示虽然也是操作了textView等UI组件,但是它的机制使用了binder跟系统进程交互,机制完全不同,不展开讨论.

Handler Looper

prepare方法调用后,调用Looper的loop方法,会循环取出消息并分发:

    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和Handler小结一下:

  1. 首先Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

  2. Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

  3. Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

  4. Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

  5. 在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

Handler的leak问题
Handler会经常遇到一个leak的警告:This Handler class should be static or leaks might occur
最简单的处理方式是:将Handler声明为static并持有其外部类的WeakReference(弱引用).
代码如下:

    static class MHandler extends Handler {
    WeakReference<OuterClass> outerClass;
    MHandler(OuterClass activity) {
        outerClass = new WeakReference<OuterClass>(activity);
    }
    @Override
    public void handleMessage(android.os.Message msg) {
        OuterClass theClass = outerClass.get();
        switch (msg.what) {
        case 0: {
            //使用theClass访问外部类成员和方法
            break;
        }
        default: {
            Log.w(TAG, "未知的Handler Message:" + msg.what);
        }
        }
    }
}

AsyncTask

onPreExecute 在执行前调用
doInBackground 后台执行操作
onProgressUpdate更新进度,在doInBackground 中调用publishProgress方法即会触发
onPostExecute 后台操作执行完成后调用

注意:上面四个方法,只有doInBackground 是在非UI线程执行的,其余均在主线程中执行,所以耗时操作都需要放在doInBackground 中;AsyncTask的实例必须在UI线程创建,execute方法必须在主线程中调用.

上一篇下一篇

猜你喜欢

热点阅读