• 我理解的Hanlder--android消息传递机制


      每一个学习Android的同学都会觉得Handler是一个神奇的东西,我也一样,开始我以为我懂了Handler的机制,后来发现自己是一知半解,昨天想想,我能否自己实现一个Handler,让子线程与ActivityUI线程通信,如果能够自己实现一个Handler,那必然是对Handler的消息传递机制理解渗透了。

    一、引入

      Android的UI是单线程控制的,实际上,成功的UI框架都是基于单线程的,多线程的UI框架往往因为解决并发和死锁的艰难而胎死腹中。只有UI线程能控制界面控件,但我们总是希望子线程的数据能更新到UI上,于是就有了Handler,它帮助我们实现了非UI线程向UI线程之间的通信,Handler的使用非常简单。

      一个简单的示例,让一个TextView动态显示秒数:  

     1 public class MainActivity extends Activity {
     2     private static final int UPDATE_TAG = 0x01;
     3     private TextView textView;
     4 
     5     @Override
     6     protected void onCreate(Bundle savedInstanceState) {
     7         super.onCreate(savedInstanceState);
     8         setContentView(R.layout.activity_main);
     9         textView = (TextView) findViewById(R.id.textview);
    10         // 启动一个线程
    11         new Thread(new Runnable() {
    12 
    13             @Override
    14             public void run() {
    15                 int count = 1;
    16                 while (true) {
    17                     // 发送计数值到handler
    18                     mHandler.obtainMessage(UPDATE_TAG, String.valueOf(count))
    19                             .sendToTarget();
    20                     try {
    21                         TimeUnit.SECONDS.sleep(1);
    22                     } catch (InterruptedException e) {
    23                         e.printStackTrace();
    24                     }
    25                 }
    26             }
    27 
    28         }).start();
    29     }
    30 
    31     public Handler mHandler = new Handler() {
    32         public void handleMessage(Message msg) {
    33             switch (msg.what) {
    34             case UPDATE_TAG:
    35                 // 显示计数值
    36                 textView.setText((String)msg.obj);
    37                 break;
    38             }
    39         }
    40     };
    41 }

      通过开启一个线程,线程每隔一秒发送一次计数给handler,让handler来更新TextView的内容,新开的子线程里面不能直接操控TextView,因为这违反了UI线程了单线程控制规范,如果你真要这么做,一运行就会得到CalledFromWrongThreadException异常。

    二、这不就是一个观察着模式么?

      最初自己理解的Handler就是一个典型的观察着模式。sendToTarget()这个方法一定以某种方式调用了Handler的 handleMessage(Message msg)方法,从而完成了我们指定的任务,于是我写了下面这样一个Handler和Message

    /***
     * 模拟Android的Handler,申明一个供子类覆盖的方法和多个生成消息的方法
     */
    public class Handler {
    
        /***
         * 子类覆盖本方法,实现想要的操作
         * 
         * @param msg
         */
        protected void handleMessage(Message msg) {
    
        }
    
        public Message obtainMessage(int what) {
            Message msg = new Message(this, what, -1, -1, null);
            return msg;
        }
    
        public Message obtainMessage(int what, Object obj) {
            Message msg = new Message(this, what, -1, -1, obj);
            return msg;
        }
    
        public Message obtainMessage(int what, int arg1, int arg2, Object obj) {
            Message msg = new Message(this, what, arg1, arg2, obj);
            return msg;
        }
    }

      下来就是Message类:  

    /**
     * 
     * 模拟Android的Message,这里为方便演示,只写了一个构造方法。构造方法中绑定了一个Handler
     */
    public class Message {
        private final Handler target;
        public final int what;
        public final int arg1;
        public final int arg2;
        public final Object obj;
    
        public Message(Handler target, int what, int arg1, int arg2, Object obj) {
            this.target = target;
            this.arg1 = arg1;
            this.arg2 = arg2;
            this.what = what;
            this.obj = obj;
        }
        /***
         * 利用OOP的多态,调用子类的覆盖方法
         */
        public void sendToTarget() {
            target.handleMessage(this);
        }
    }

      测试代码如下:  

    public class HandlerTest {
    
        public static void main(String[] args) {
            new Thread(new MyTask(Myhandler)).start();
        }
    
        static class MyTask implements Runnable {
            private Handler handler;
    
            public MyTask(Handler handler) {
                this.handler = handler;
            }
    
            @Override
            public void run() {
                while (true) {
                    handler.obtainMessage(0, this.toString()).sendToTarget();
                }
            }
    
        }
    
        static Handler Myhandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                case 0:
                    System.out.println("I am zero " + (String) msg.obj);
                    break;
                }
            }
        };
    
    }
    View Code

      真想说上面的代码体现了一个简单的观察着模式,上面的测试代码能运行。但是,但是这样的Handler能代替Android的Handler在Activity中使用吗,用上面的Handler去替代Android的handler,结果得到是CalledFromWrongThreadException。

      这很显然的,上面的Handler可以只模仿到Android Handler的形,没有模仿到其神,它只是通过一个回调模仿了Handler的工作外表,利用多态的伎俩去调用子类的handleMessage方法,想法是好的,但这最终相当于让子线程直接调用了handleMessage,从而让子线程对界面控件进行了操控,违背了UI界面的单线程控制原则,必须会报CalledFromWrongThreadException的异常。

    三、子线程是如何把消息传递给UI线程

      前面仿造的Handler让子线程通过Message操纵了UI控件,因此会报错,那Android是如何将子进程的消息发送给UI进程。实际上Handler的工作离不开以下几个组件:

    • Message: Handler接收和处理的对象
    • MessageQueue:消息队列,以FIFO的方式管理Message对象
    • Looper:管理MessageQueue的对象,不断地从MessageQueue中取消息,并发送该消息对应的Handler进行处理。每个线程只有一个Looper,UI线程在创建时就已经默认创建了一个Looper,因此在Activity中可以直接创建Handler即可发送和处理消息;如果在子线程中创建Handler,必须在创建前调用Looper.prepare();这样Looper在才能做好准备,并为当前线程创建MessageQueue,以便接受Handler的Message。可以看一下Looper.prepare()的源码
      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));
          }
          private Looper(boolean quitAllowed) {//私有的构造方法
              mQueue = new MessageQueue(quitAllowed);
              mRun = true;
              mThread = Thread.currentThread();
          }

      在子进程创建好Handler后,调用Looper.loop()处理消息。Looper.loop()是一个死循环,如果不执行quit(),其后的代码将不会执行。现在看一个例子,我们通过点击Button给子线程的Hanlder传递消息。

      public class MainActivity extends Activity {
          private Mythread thread;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_main);
              thread = new Mythread();
              thread .start();
        
          }
      
          class Mythread extends Thread {
              public Handler handler;
      
              @Override
              public void run() {
                  Looper.prepare();
                  // 在子线程中定义Handler
                  handler = new Handler() {
                      public void handleMessage(Message msg) {
                          Log.i("handleMessage", "子线程handler.handleMessage(...)");
                          // 接到消息让Looper退出死循环
                          handler.getLooper().quit();
                      }
                  };
                 Toast.makeText(MainActivity.this, "before Looper.loop()",
                          Toast.LENGTH_LONG).show();
                  // 死循环处理消息
                  Looper.loop();
                  Toast.makeText(MainActivity.this, "after Looper.loop()",
                          Toast.LENGTH_LONG).show();
      
              }
          };
      
          public void onClickButton(View v) {
              thread.handler.obtainMessage().sendToTarget();
          }            
      }

      可以看到线程启动后,会Toast"before Looper.loop()",但不会立刻Toast"after Looper.loop()",只有当我们按下Button后才会显示"after Looper.loop()",因为按下Button,会给子线程的Handler发送一个空消息,而我们定义Handler对消息的处理是调用looper的quit(),从而结束Looper.loop()。注意quit()并不是简单的终止Looper.loop(),而是设置MessageQueue的一个标志,让MessageQueue的next()方法返回null,从而结束Looper.loop(),详情请看MessageQueue源码,这里我们来看看loop()的源码:

    • public static void loop() {
            ...
              for (;;) {
                  Message msg = queue.next(); // might block
                  if (msg == null) {
                      // No message indicates that the message queue is quitting.
                      return;
                  }
          ...
      
                  msg.target.dispatchMessage(msg);
          ...
                  msg.recycle();
              }
          }

      另外跟踪sendToTarget()发现最终调用的方法是MessageQueue的enqueueMessage(Message msg, long when),这个方法只是把message放入队列中,并没有调用消息消息方法,消息的分发处理是在Looper的loop()中进行的。

    四、是什么东西再帮UI线程跑Looper.loop()

      现在理清了前面的问题又来了新的问题,是谁在帮UI线程跑Looper.loop():

    • 如果是其他线程帮它Looper.loop(),那么这明显违背了UI线程的单线程控制规范;
    • 如果是UI线程自己在跑,那它的死循环在那里跑着,怎么用工夫去执行onCreate(),onResume()...以及我们绑定的各种listener。

      如果既要满足UI线程的单线程控制规范,又要让死循环Looper.loop()跑着不漏掉一个消息,还要响应onCreate()、listener,只有一种办法,那就是对界面的所有的操作都是由loop来驱动分发的,onCreate(),onResume()...各种listener都是Looper.loop()来调用

      实际上Android就是这么干的,要理清onCreate等各种方法绑定的Message的过程不容易,但是我们可以很简单地验证我们的上面的假设,那就是在onCreate(),onResume(),以及listener的响应方法里面引发异常,然后看一下栈信息,我们在onCreate发放中加一句int a = 1/0,看看异常的栈信息:

      当发现神奇的Handler原来就是靠一个死循环来维持的,一切就变得豁然开朗了。

      如果再深入,理解Activity的启动机制,我们最终找到UI线程的Looper.loop()是在ActivityThread中的main方法中执行的,让我们再看看源码:  

    public final class ActivityThread {
    
                      ...
      public static void main(String[] args) {
            SamplingProfilerIntegration.start();
    
            // CloseGuard defaults to true and can be quite spammy.  We
            // disable it here, but selectively enable it later (via
            // StrictMode) on debug builds, but using DropBox, not logs.
            CloseGuard.setEnabled(false);
    
            Environment.initForCurrentUser();
    
            // Set the reporter for event logging in libcore
            EventLogger.setReporter(new EventLoggingReporter());
    
            Security.addProvider(new AndroidKeyStoreProvider());
    
            Process.setArgV0("<pre-initialized>");
            //为主线程开启Looper
            Looper.prepareMainLooper();
    
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            AsyncTask.init();
    
            if (false) {
                Looper.myLooper().setMessageLogging(new
                        LogPrinter(Log.DEBUG, "ActivityThread"));
            }
            //UI线程的死循环在这里
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    }

      

    五、真的能自己写一个Handler供Activity调用吗

      现在回到我最初的问题,我们可以自己写一个Handler供Activity调用吗?答案已经很明显了,如果你足够强,你真实现了一个功能一样的Handler,那么恭喜你,你是在重写了整个Activity组件。

       

      以上是我对Handler的理解,希望对你有所帮助,如果有总结得不恰当的地方,欢迎指正。

     感谢阅读,转载请注明出处:http://www.cnblogs.com/fengfenggirl/ 

  • 相关阅读:
    在Azure虚拟机上安装SQL server
    Azure的负载均衡机制
    如何在ARM中创建Express Route
    如何将已部署在ASM的资源迁移到ARM中
    Azure上的那些IP
    使用mysqlslap对mysql进行压测,观察Azure虚拟机cpu使用率
    在Azure虚拟机上安装VNC
    DataGridView中实现checkbox全选的自定义控件
    jquery checkbox 实现单选
    如何通过Azure Service Management REST API管理Azure服务
  • 原文地址:https://www.cnblogs.com/fengfenggirl/p/android_handler.html
Copyright © 2020-2023  润新知