美文网首页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