view.post 和handler发送消息的区别

有个同事问我这个问题,我们往主线程发送一个runnable,有两种方法可以使用,一种是定义一个主线程的handler,通过handler.postRunnable()来执行,一种方法是通过view.post来执行,那么这两种方式有什么区别,具体使用时应该怎么选择。
我们首先要明白的就是两种方式有什么区别,要搞清楚这个问题,就要分析系统究竟是怎么实现的。对于handler.postRunnable 这个不用说,就是往主线程的消息队列扔了一个消息,后面looper会从消息队列取出这条消息,执行这个runnable。
对于view.post 方法就要剖析源码了:

    public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }

首先是判断attachInfo 是否为空,如果不为空的话,就从attachInfo.mHandler来post这个runnable对象,看这个handler的注释:


        /**
         * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This
         * handler can be used to pump events in the UI events queue.
         */
        final Handler mHandler;

注释明白说了,可以用过handler来往ui线程发送消息;
那这个attachInfo 是什么时候赋值,什么情况下为空,再看源码,

    /**
     * {@hide}
     */
    AttachInfo mAttachInfo;

...


    /**
     * @param info the {@link android.view.View.AttachInfo} to associated with
     *        this view
     */
    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
        //System.out.println("Attached! " + this);
        mAttachInfo = info;
        ...
        }

mAttachIn默认就是空,dispatchAttachedToWindow这里对mAttachIn赋值,从注释看这个函数的执行时机是这个view被关联到window的时候;

    void dispatchDetachedFromWindow() {
        ....
        mAttachInfo = null;
        ....
    }

所以就是当view还没有关联到一个具体的window的时候,这个attachinfo就是空;
根据源码,如果attachinfo未空的话将执行下面的代码:

        // Assume that post will succeed later
        ViewRootImpl.getRunQueue().post(action);

ViewRootImpl.getRunQueue()的源码:

    static RunQueue getRunQueue() {
        RunQueue rq = sRunQueues.get();
        if (rq != null) {
            return rq;
        }
        rq = new RunQueue();
        sRunQueues.set(rq);
        return rq;
    }

再看RunQueue的代码:

    /**
     * The run queue is used to enqueue pending work from Views when no Handler is
     * attached.  The work is executed during the next call to performTraversals on
     * the thread.
     * @hide
     */
    static final class RunQueue {
        private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();

        void post(Runnable action) {
            postDelayed(action, 0);
        }

        void postDelayed(Runnable action, long delayMillis) {
            HandlerAction handlerAction = new HandlerAction();
            handlerAction.action = action;
            handlerAction.delay = delayMillis;

            synchronized (mActions) {
                mActions.add(handlerAction);
            }
        }


        void executeActions(Handler handler) {
            synchronized (mActions) {
                final ArrayList<HandlerAction> actions = mActions;
                final int count = actions.size();

                for (int i = 0; i < count; i++) {
                    final HandlerAction handlerAction = actions.get(i);
                    handler.postDelayed(handlerAction.action, handlerAction.delay);
                }

                actions.clear();
            }
        }
        }

看到这里应该明白其实就是往RunQueue的mActions这个ArrayList里添加数据,执行的函数是executeActions(Handler handler),而这个方法的调用地方是在:

    private void performTraversals() {
            ....

        // Execute enqueued actions on every traversal in case a detached view enqueued an action
        getRunQueue().executeActions(mAttachInfo.mHandler);
        ....
        }

这就跟清楚了,就是在对view进行绘制的时候会把mActions
里的内容全部通过主线程handler发送到主线程消息队列。
这样就是把view.post分析清楚了;
总结下: view.post也是往主线程发消息,只是发消息要看view的状态,如果view已经被关联到window的话,直接通过handler发送,如果不是的话,则把消息添加到RunQueue.mActions中,到下次view绘制时再把mAction中的消息发到主线程消息队列,这里如果aAction中的runnable对象没有被及时发出去的话,回导致内存泄露,以为runnable常常作为匿名内部类,会持有对外部类的引用.
从使用上看在有view引用变量的情况下,使用view.post方便,不需要自己去定义一个handler,定义handler一不小心就会引起内存泄露。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
package com.example.ksswj; import androidx.appcompat.app.AppCompatActivity; import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.Socket; import java.net.UnknownHostException; public class MainActivity extends AppCompatActivity { private EditText ipEditText; private EditText portEditText; private TextView messageTextView; private BufferedReader in; private Socket socket; private Handler handler = new Handler(); @SuppressLint("MissingInflatedId") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ipEditText = findViewById(R.id.ipEditText); portEditText = findViewById(R.id.portEditText); messageTextView = findViewById(R.id.messageTextView); Button connectButton = findViewById(R.id.connectButton); connectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String ip = ipEditText.getText().toString(); int port = Integer.parseInt(portEditText.getText().toString()); new Thread(new Runnable() { @Override public void run() { try { socket = new Socket(ip, port); in = new BufferedReader(new InputStreamReader(socket.getInputStream())); // 在页面中的窗口中实时显示消息 while (true) { String message = in.readLine(); if (message == null) { break; } handler.post(new Runnable() { @Override public void run() { messageTextView.append(message + "\n"); } }); } } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } }); } @Override protected void onDestroy() { super.onDestroy(); try { if (in != null) { in.close(); } if (socket != null) { socket.close(); } } catch (IOException e) { e.printStackTrace(); } } }我还需要在上面代码中加入一个对话框,能够向端口发送消息,能帮我加上代码吗
最新发布
05-31

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值