runnable.run()

是当前线程立即同步执行。

view.post(runnable)

是把任务提交到这个View所在线程的消息队列,通常等主线程之后执行。

1. Runnable.run()

  • 当前线程立即执行;
  • 没执行完之前,后面的代码不会继续

2. view.post(runnable)

  • 不会直接调用run(),而是把Runnable放进与view关联线程的消息队列。
  • 绝大多数情况下, view在主线程创建和使用,所以通常可以理解为把runnable提交给主线程,稍后执行。

3 .举例说明

Log.d("Test", "1");

view.post(() -> {
    Log.d("Test", "Runnable");
});

Log.d("Test", "2");

输出是

1
2
Runnable

Log.d("Test", "1");

runnable.run();

Log.d("Test", "2");

输出是

1
Runnable
2

4. 即使在主线程,view.post(runnable)也不会立即执行

比如

button.setOnClickListener(v -> {
    Log.d("Test", "before");

    v.post(() -> {
        Log.d("Test", "posted");
    });

    Log.d("Test", "after");
});

输出一般是:

before
after
posted

因为当前点击事件也是主线程正在处理的一条消息。 post()提交后新的任务需要等点击回调执行完,主线程回到消息循环后,才有机会处理。

button.setOnClickListener(v -> {
    Log.d("Test", "before");

    runnable.run();

    Log.d("Test", "after");
});

输出是:

before
posted
after

5. 为什么常用view.post()获取宽高

通常:

view.post(() -> {
    int width = view.getWidth();
    int height = view.getHeight();
});

原因不是为了切线程,而是为了延迟到当前布局流程之后执行。 例如在onCreate()中:

int width = view.getWidth()

可能会得到0,因为此时View还没有完成测量和布局。

用view.post(),任务会等到当前主线程任务之后执行。很多时候,到它执行时,View已经完成了一轮测量和布局,因此可以拿到尺寸。

post()本身表达的是“排队执行”,并不严格承诺View一定完成布局。

如果需要明确的监听,需要其他的函数

6. post()的返回值

post()返回boolean,表示任务是否成功加入队列,但是不表示任务是否已经执行完成,只代表提交情况。

7.post()不等于开启新线程

view.post(() -> {
    performHeavyTask();
});

这并不会把任务放到后台线程,恰恰相反,通常会放到主线程。 假如performHeavyTask()需要很耗时,需要5s,那么主线程会卡住5s,界面会卡顿甚至出现ANR。