美文网首页Lee_3do的博客
Looper Handler AsyncTask

Looper Handler AsyncTask

作者: lee_3do | 来源:发表于2015-11-04 11:08 被阅读60次

前置说明

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

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

Handler Looper

  • Handler 它的作用主要就是收发消息,它把消息发给发送给Looper管理的MessageQueue,并负责处理Looper发给它的消息.所以如果想使用Handler,所在线程必须有Looper.
  • Looper Looper负责管理MessageQueue,会循环的从MessageQueue中取出消息,并将消息分发给对应的Handler.
    主UI线程中已经初始化好了Looper对象,因此可以直接使用Handler.
    而对于非主线程,需要自己创建Looper对象,如下:
    if (Looper.myLooper() == null) {
    Looper.prepare();
    }
    调用prepare方法即可,但是一个线程只能有一个Looper,如果重复调用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));}
    因此建议在prepare之前加Looper.myLooper() == null判断,防止FC出现.

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

  • AsyncTask是另外一种后台执行程序回到前台更新UI的实现方法,AsyncTask是一个抽象类,我们可以自己继承实现,根据需要,复写如下方法即可:

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

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

相关文章

网友评论

    本文标题:Looper Handler AsyncTask

    本文链接:https://www.haomeiwen.com/subject/mngqhttx.html