关于Handler消息机制一点愚见与总结

Android中Looper详解
本文深入解析Android中的Looper机制,解释其在消息传递和线程管理中的作用。Looper作为消息循环的核心,确保消息队列中的消息能被正确处理,实现线程间的通信。

Handler消息机制在android刷新UI中是经常使用到的一个工具,但是Handler并不是专门用于刷新UI,是为了让任务能够轻易的切换到Handler中操作。

出现的缘由,主要是因为android中的UI组件为了追求效率,而放弃了对UI组件在并发情况下加锁情况的解决。而是规定了子线程无法访问UI控件。

这个时候,修改主线程的UI该怎么解决呢?这时就需要Handler的消息机制,来完成对UI操作切换到主线程。

Handler消息机制包含四个部分:Message,MessageQueue,Looper,Handler。

Message :顾名思义就是线程中传递消息。(IPC中Messenger中也用到Message)。

MessageQueue:是指消息队列,每个消息进入之后,就进入到该队列进行排队,等待Handler的处理。

Handler:处理发送过来的消息。,一般需要重写handleMessage()来达到目的。

Looper:可以说是核心的模块,作为每个线程MessageQueue的管理者。

上面个模块相信开发者都比较熟悉,这一篇文章主要是讲自己对Looper的理解和总结。

Looper从名字可以知道这是一个循环,具体是做什么的呢?

当我们在子线程中声明Handler的时候会发现下面这个错误:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

这个错误就是没有调用Looper.prepare()这个函数。

看看Android下面是怎么写的:

    public static final void prepare() {  
            if (sThreadLocal.get() != null) {  
                throw new RuntimeException("Only one Looper may be created per thread");  
            }  
            sThreadLocal.set(new Looper(true));  
    }  

从上面可以知道,Looper.prepare()是获取ThreadLocal中的数据(ThreadLocal是用于在线程中存储数据)。由于子线程声明Handler像主线程里面自己已经有MainLooper。所以我们必须在子线程中添加Looper.prepare(),获取存储在线程中数据。

别忘了Handler就是为了将任务切换到Handler线程中去。

Looper主要的工作除了获取线程上的数据,更重要的工作是在loop()方法中有一个死循环,不断的循环检测MessageQueue中是否为空:

源码:

    public static void loop() {  
            final Looper me = myLooper();  
            if (me == null) {  
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
            }  
            final 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();  

            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.recycle();  
            }  
    }  

可以知道在这个循环机制中,调用Message msg = queue.next();来不断的获取队列中下一个message,知道获取到的信息为null。接着通过看一下next()函数,真正的消息阻塞是来自于MessageQueue方法中next()。

MessageQueue的next()就是一个循环体,每当消息传来的时候,next()会返回消息,并且从MessageQueue这个链表中移除。

最后通过msg.target.dispatchMessage(msg);将消息传到Handler中处理。

而行代码就是切换线程的关键:
源码:

public void dispatchMessage(Message msg) {  
       if (msg.callback != null) {  
           handleCallback(msg);  
       } else {  
           if (mCallback != null) {  
               if (mCallback.handleMessage(msg)) {  
                   return;  
               }  
           }  
           handleMessage(msg);  
       }  
   } 

在这里就进行了对消息的处理,那么切换线程有从何而来呢?

当然是指这个msg.target这个对象了,它来源于下面:

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
           msg.target = this;  
           if (mAsynchronous) {  
               msg.setAsynchronous(true);  
           }  
           return queue.enqueueMessage(msg, uptimeMillis);  
       }  

这里的msg.target指派了this,是指插入队列函数enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)这个函数的上下文,哪里又调用它呢,就是Handler的sendMessage(Message msg)嵌套调用了。

这就完成了Handler的分析了,总结为一个图:
Handler机制

下面是一个错误的尝试:

    public class MyHandler extends Handler{
        private Looper myLooper;

        public MyHandler(Looper myLooper){
            this.myLooper = myLooper;
        }

        @Override
        public void handleMessage(Message msg){
            switch (msg.what) {
            case 2:
                Log.e("threadmsg",""+msg.what);
//              Message message = handler.obtainMessage();
//              message.what = 2;
//              handler.sendMessage(message);
                text.setText("hello world");
                myLooper.quitSafely();
                break;

            default:
                super.handleMessage(msg);
                break;
            }
        }
    }


 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView)findViewById(R.id.text);
        button = (Button)findViewById(R.id.change);
        Tbutton = (Button)findViewById(R.id.change2);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Message message = new Message();
                message.what = 1;
                handler.sendMessage(message);
            }
        });

        Tbutton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        Looper.prepare();
                        MyHandler myHandler = new MyHandler(Looper.myLooper());
                        Message msg = new Message();
                        msg.what = 2;
                        myHandler.sendMessage(msg);
                        Looper.loop();
                    }
                }).start();
            }
        });


    }


就会弹出下面这个错误警告,看到了吧,在线程中创建Handler,最后还是返回到handler的线程,违反了子线程不能访问 UI线程的原则。如果硬要这么要修改就必须中间多一个来自主线程创建的Handler才能够修改上面的textView,不推荐这么做。

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

正确的调用时第一个Button来调用调用创建在主线程的Handler:

public class MainActivity extends Activity {

    private TextView text;
    private Button button;
    private Button Tbutton;

    private Handler handler = new Handler(){

        public void handleMessage(Message msg){
            switch (msg.what) {
            case 1:
                text.setText("nice to meet you");
                break;

            case 2:
                text.setText("hello world");
            default:
                break;
            }
        }
    };

    public class MyHandler extends Handler{
        private Looper myLooper;

        public MyHandler(Looper myLooper){
            this.myLooper = myLooper;
        }

        @Override
        public void handleMessage(Message msg){
            switch (msg.what) {
            case 2:
                Log.e("threadmsg",""+msg.what);
                Message message = handler.obtainMessage();
                message.what = 2;
                message.sendToTarget();
                myLooper.quitSafely();
                break;

            default:
                super.handleMessage(msg);
                break;
            }
        }
    }



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = (TextView)findViewById(R.id.text);
        button = (Button)findViewById(R.id.change);
        Tbutton = (Button)findViewById(R.id.change2);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Message message = new Message();
                message.what = 1;
                handler.sendMessage(message);
            }
        });

        Tbutton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                // TODO Auto-generated method stub
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        Looper.prepare();
                        MyHandler myHandler = new MyHandler(Looper.myLooper());
                        Message msg = new Message();
                        msg.what = 2;
                        myHandler.sendMessage(msg);
                        Looper.loop();
                    }
                }).start();
            }
        });


    }




}

感谢任玉刚的开发探索与艺术,以及鸿样大神的启发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值