• [Android]Handler的消息机制


              最经面试中,技术面试中有一个是Handler的消息机制,细细想想,我经常用到的Handler无非是在主线程(或者说Activity)新建一个Handler对象,另外一个Thread是异步加载数据,同时当他加载完数据后就send到主线程中的那个Handler对象,接着Handler来处理,刚才发送的一些消息。

             

     1 public class HandlerTestActivity extends Activity {
     2     private TextView tv;
     3     private static final int UPDATE = 0;
     4     private Handler handler = new Handler() {
     5 
     6         @Override
     7         public void handleMessage(Message msg) {
     8             // TODO 接收消息并且去更新UI线程上的控件内容
     9             if (msg.what == UPDATE) {
    10                 // Bundle b = msg.getData();
    11                 // tv.setText(b.getString("num"));
    12                 tv.setText(String.valueOf(msg.obj));
    13             }
    14             super.handleMessage(msg);
    15         }
    16     };
    17 
    18     /** Called when the activity is first created. */
    19     @Override
    20     public void onCreate(Bundle savedInstanceState) {
    21         super.onCreate(savedInstanceState);
    22         setContentView(R.layout.main);
    23         tv = (TextView) findViewById(R.id.tv);
    24 
    25         new Thread() {
    26             @Override
    27             public void run() {
    28                 // TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值
    29                 try {
    30                     for (int i = 0; i < 100; i++) {
    31                         Thread.sleep(500);
    32                         Message msg = new Message();
    33                         msg.what = UPDATE;
    34                         // Bundle b = new Bundle();
    35                         // b.putString("num", "更新后的值:" + i);
    36                         // msg.setData(b);
    37                         msg.obj = "更新后的值:" + i;
    38                         handler.sendMessage(msg);
    39                     }
    40                 } catch (InterruptedException e) {
    41                     e.printStackTrace();
    42                 }
    43             }
    44         }.start();
    45     }
    46 
    47 }
    View Code

    如图所示,每个Thread都一个Looper,这个Looper类是用于管理其中的消息队列(MessageQueue)的,那Handler是干嘛的呢,他是用来传递消息队列的。

    那下面就分析Looper、Hanlder方法吧。

    Looper方法是用来处理消息队列的,注意了,它和线程是绑定的。

    要是想在子线程中获取一个Looper该怎么做呢:

        Looper.prepare();
        Looper looper = Looper.myLooper();

    那么这些都干了哪些工作呢???

    来看下它的源码吧:

    Looper:

    ……
    //准备Looper相关事宜
       public static void prepare() {
         //只能有一个对象哦
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
             sThreadLocal.set(new Looper());
         }
       //构造函数
      /*新建一个消息队列
       * 把当前运行的线程作为运行线程
      */
    
        private Looper() {
            mQueue = new MessageQueue();
            mRun = true;
            mThread = Thread.currentThread();
        }
    
    
    

                 public static final Looper myLooper() {

                                //这个方法是从当前线程的ThreadLocal中拿出设置的looper

                     return (Looper)sThreadLocal.get();

                 }

    /**
         * Run the message queue in this thread. Be sure to call
         * {@link #quit()} to end the loop.
         */
        public static void loop() {
            Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            MessageQueue queue = me.mQueue;
            
            // Make sure the identity of this thread is that of the local process,
            // and keep track of what that identity token actually is.
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
            
            while (true) {
                Message msg = queue.next(); // might block
                if (msg != null) {
                    if (msg.target == null) {
                        // No target is a magic identifier for the quit message.
                        return;
                    }
    
                    long wallStart = 0;
                    long threadStart = 0;
    
                    // 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);
                        wallStart = SystemClock.currentTimeMicro();
                        threadStart = SystemClock.currentThreadTimeMicro();
                    }
    
                    msg.target.dispatchMessage(msg);
    
                    if (logging != null) {
                        long wallTime = SystemClock.currentTimeMicro() - wallStart;
                        long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;
    
                        logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                        if (logging instanceof Profiler) {
                            ((Profiler) logging).profile(msg, wallStart, wallTime,
                                    threadStart, threadTime);
                        }
                    }
    
                    // 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.recycle();
                }
            }
        }

    下面就来看下Handler:

    public Handler() {
            if (FIND_POTENTIAL_LEAKS) {
                final Class<? extends Handler> klass = getClass();
                if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                        (klass.getModifiers() & Modifier.STATIC) == 0) {
                    Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                        klass.getCanonicalName());
                }
            }
          //先获得一个Looper对象,这个要是在子线程里,是需要先prepare()的
    
       
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;
            mCallback = null;
        }
      /**
         * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
         * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
         *  If you don't want that facility, just call Message.obtain() instead.
       会从消息池里面取得消息队列
         */
        public final Message obtainMessage()
        {
            return Message.obtain(this);
        }

    那我现在写个小例子,是在子线程实现的消息的传递。

    @Override
        public void onClick(View v) {
            int id = v.getId();
            if (id == R.id.btn1) {
                new Thread() {
    
                    public void run() {
    
                        Log.i("log", "run");
    
                        Looper.prepare();
                        // Looper looper = Looper.myLooper();
                        Toast.makeText(MainActivity.this, "toast", 1).show();
                        Handler h = new Handler() {
    
                            @Override
                            public void handleMessage(Message msg) {
                                // TODO Auto-generated method stub
                                super.handleMessage(msg);
                                if (msg != null) {
                                    String strMsg = (String) msg.obj;
                                    System.out.println(strMsg);
                                }
    
                            }
    
                        };
                        //获取到Handler对象的消息
                        Message msg = h.obtainMessage();
                        msg.obj = "add";
                        msg.sendToTarget();
    
                        Looper.loop();// 进入loop中的循环,查看消息队列
    
                    };
    
                }.start();
    
            }
        }

    不知你是否理解,这个小Demo中,我们需要注意:

    1  子线程也是可以有Handler的,其实Handler只是从当前的线程中获取到Looper来监听和操作MessageQueue的。

    2 子线程需要先prepare()才能获取到Looper的,是因为在子线程只是一个普通的线程,其ThreadLoacl中没有设置过Looper,所以会抛出异常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是设置了Looper的。

    而对于主线程里面的Handler,是没有以上的麻烦的,因为这个在Activity创建时,就已经初始化了Looper等其他工作了。

    另外可以看下参考文章中的子线程中Toast

    参考文章:

    Android之Handler用法总结

    Android Handler机制

    子线程中Toast

  • 相关阅读:
    Linux下安装软件遇见的问题汇总
    使用AlarmManager定期执行工作
    android打开文件、保存对话框、创建新文件夹对话框(转载)
    一些算法的整理
    安卓adb调试命令常见的问题
    java下的串口通信-RXTX
    Unity 协程运行时的监控和优化
    Unity3D 协程的介绍和使用
    游戏服务器:到底使用UDP还是TCP
    Unity 可重复随机数
  • 原文地址:https://www.cnblogs.com/Cyning/p/3618364.html
Copyright © 2020-2023  润新知