安卓学习笔记之Handler更新UI的几种方法及对比

Handler更新UI的几种方法


  • handler发送消息msg的方式
  • 调用handler的post(Runnable)方法
  • runOnUiThread(Runnable)方法
  • 控件view调用自身的post(Runnable)方法

注意:view.post方法的使用,该view一定是会被添加到视图层级中的,否则无效。

实例代码:

// 第一种 
    public void handlerpost() {
        handler.post(new Runnable() {

            @Override
            public void run() {
                tv.setText("handlerpost更新啦");
            }
        });
    }
    //第二种
    public void runUI() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                tv.setText("runUI更新啦");

            }
        });
    }

    //第三种
    public void sendmessage() {
        handler.sendEmptyMessage(1);
    }

    //第四种
    public void viewpost() {
        tv.postDelayed(new Runnable() {

            @Override
            public void run() {
            tv.setText("view更新啦");

            }
        }, 0);
    }

几种常见更新方式的执行顺序对比

考虑如下代码:

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ViewGroup root = (ViewGroup) LayoutInflater.from(this).inflate(R.layout.activity_main, null);
        setContentView(root);

        final Button button = new Button(this);
        root.addView(button);
        button.setText("add-view-test");

    // 1
        button.post(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "button.post");
                Toast.makeText(getApplicationContext(), "button.post", Toast.LENGTH_SHORT).show();
            }
        });

     // 2
        getWindow().getDecorView().post(new Runnable() { // Decor的dispatchAttachedToWindow最先被调用
            @Override
            public void run() {
                Log.e(TAG, "decor.post");
                Toast.makeText(getApplicationContext(), "decor.post", Toast.LENGTH_SHORT).show();
            }
        });

    // 3
        handler.post(new Runnable() {
            @Override
            public void run() {
                Log.e(TAG, "handler.post");
                Toast.makeText(getApplicationContext(), "handler.post", Toast.LENGTH_SHORT).show();
            }
        });

    // 4
        runOnUiThread(new Runnable() { 
            @Override
            public void run() {
                Log.e(TAG, "runOnUiThread");
                Toast.makeText(getApplicationContext(), "runOnUiThread", Toast.LENGTH_SHORT).show();
            }
        });

        new Thread() {
            @Override
            public void run() {  
                super.run();
                SystemClock.sleep(3000);
                handler.post(new Runnable() { // post到UI线程执行
                    @Override
                    public void run() {
                        button.setText("change text");
                    }
                });
            }
        }.start();
    }

上述几种方式的log打印顺序?


执行时间顺序:

4 -> 3 -> 2 -> 1

如下:
E/MainActivity: runOnUiThread
E/MainActivity: handler.post
E/MainActivity: decor.post
E/MainActivity: button.post

分析:

@Override
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

runOnUiThread在主线程调用,直接执行action.run(); 因此最快

再看Handler:

 public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}

handler.post会发送消息到主线程来执行,紧跟其后执行。

1和2都调用了view的post方法

 public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }

此处注意代码调用的时机,在onCreate执行时,view还没有attach到window,所以mAttachInfo=null,所以走getRunQueue().post(action); 并将Runnable添加到了getRunQueue()得到的mRunQueue中。当onResume执行时,才会调用View的dispatchAttachedToWindow,然后才对Runnable做处理。

2比1快?

因为DecorView是顶级View,它的dispatchAttachedToWindow最先被调用,在该方法中会处理post的Runnable:

void dispatchAttachedToWindow(AttachInfo info, int visibility) {
       mAttachInfo = info;
      ...
       // Transfer all pending runnables.
     // 处理Runnable
        if (mRunQueue != null) {  
           mRunQueue.executeActions(info.mHandler);
           mRunQueue = null;
       }
       performCollectViewAttributes(mAttachInfo, visibility);
       onAttachedToWindow();

       ListenerInfo li = mListenerInfo;
       final CopyOnWriteArrayList<OnAttachStateChangeListener> listeners =
               li != null ? li.mOnAttachStateChangeListeners : null;
       if (listeners != null && listeners.size() > 0) {
           // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
           // perform the dispatching. The iterator is a safe guard against listeners that
           // could mutate the list by calling the various add/remove methods. This prevents
           // the array from being modified while we iterate it.
           for (OnAttachStateChangeListener listener : listeners) {
               listener.onViewAttachedToWindow(this);
           }
       }

      ...
       onVisibilityChanged(this, visibility);
      ...
   }

mRunQueue.executeActions(info.mHandler);

public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                 // 遍历mActions,并将Runnable post到主线程消息队列
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

当View已经attach到了window时,view.post与handler.post一样,都是直接发送消息到主消息队列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值