美文网首页
Android Framework系列5-3 UI线程

Android Framework系列5-3 UI线程

作者: 无为3 | 来源:发表于2021-03-06 19:59 被阅读0次

应用的UI线程

本章主要围绕以下几点讲解:

  • 什么是UI线程
  • UI线程的启动流程,消息循环是怎么创建的

什么是UI线程

  • UI线程就是刷新UI所在的线程
  • UI是单线程刷新的

UI为啥是单线程刷新?如果是多线程刷新行不行?如果是多线程刷新UI的话,那么系统框架中就需要到处上锁,很容易出问题。但如果是单线程的话既简单又有效,所以一直以来UI框架基本都是单线程的。

那么问题来了:我们平常说的主线程跟UI线程有什么区别,主线程就一定是UI线程吗?
大家都知道耗时的操作不要在UI线程中进行,应该在子线程中处理,处理完后再到UI线程中刷新UI:有如下两个方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)



Activity.runOnUiThread(Runnable)

// 代码来自Android23中:Activity.java
final Handler mHandler = new Handler();
public final void runOnUiThread(Runnable action) {
        if (Thread.currentThread() != mUiThread) {
            mHandler.post(action);
        } else {
            action.run();
        }
    }
  • 如果当前线程不是UI线程,则mHandler post到消息队列中。
  • 如果是,则直接run()。
  • mHandler是Activity中的全局变量,它没有指定Looper;也就是说当前Activity在哪个线程中创建,mHandler用的就是哪个线程的Looper。

mUiThread又是什么时候初始化的呢?

// 代码来自Android23中:Activity.java
final void attach(Context context, ) {
     ......
     mUiThread = Thread.currentThread();
     ......
}

mUiThread是在attach方法中初始化的,attach方法是在Activity启动时调用,具体看 Activity的启动。 Activity的启动流程:创建Activity对象---> 创建Context ---> 调用attach给Activity赋上下文---> onCreate();这几个步骤都是在应用进程的主线程中进行的

所以对于Activity来说:UI线程 == 主线程


View.post(Runnable)

   // 代码来自Android23中:View.java
   public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }
        ViewRootImpl.getRunQueue().post(action);
        return true;
    }
// 代码来自Android23中:ViewRootImpl.java

static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();

public ViewRootImpl(Context context, Display display) {
      mAttachInfo = new View.AttachInfo(......);
}

static RunQueue getRunQueue() {
     RunQueue rq = sRunQueues.get();
}

  • mAttachInfo是ViewTree第一次绘制的时候(也就是在onResume回调之后),递归的给所有子View赋上一个mAttachInfo。
  • mAttachInfo是在ViewRootImpl构造函数中初始化,ViewRootImpl是在主线程中创建,它的mHandler变量用的Looper是主线程Looper。
  • 如果mAttachInfo为null(例如在onCreate中就直接调用View.post(..)), 则先把Runable丢到RunQueue中,等到ViewRootImpl创建好了,再丢到ViewRootImpl所在的线程中处理。

所以:View.post(..)最终都会丢到ViewRootImpl所在的线程中去处理,ViewRootImpl是在主线程中创建,也就是对于View来说:UI线程 == 主线程。
回顾下ViewRootImpl在主线程中创建:


ViewRootImpl创建

小总结:

  • 对Activity来说,UI线程就是主线程(activity.runOnUiThread)
  • 对View来说,UI线程就是ViewRootImpl创建的时候所在的线程(View.post(Runnable r))
  • Activity的DecorView对应的ViewRootImpl是在主线程创建的。

UI线程启动流程

应用进程启动的时候,主线程就有了,我们在回顾下:


主线程启动

入口函数也就是我们ActivityThread.main():

// 代码来自Android23中:ActivityThread.java
public static void main(String[] args) {
      Looper.prepareMainLooper();
      ......
      Looper.loop();
}
  • Looper.prepareMainLooper() 创建主线程的Looper。
  • Looper.loop();进入looper循环。

以上的大家应该都挺熟悉。就不说了。

总结:

这章的一些结论都是大家比较熟悉的,例如应用进程中主线程就是UI线程,可能平时大家都没区分这两个概念,举个例子:

new Thread() {
      public void run() {
            Looper.prepare();
            ......
            getWindowManager().addView(view, params);
            Looper.loop();
      }
}.start();
  • 这个例子getWindowManager().addView时会创建ViewRootImpl,也就是说ViewRootImpl是在子线程中创建的,这时这个子线程就是UI线程,在里面是可以去更新UI;
  • 反而在应用进程的主线程中如果去更新view的绘制则会报CalledFromWrongThreadException。
// 代码来自Android23中:ViewRootImpl.java
void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }

CalledFromWrongThreadException中的错误提示也说的是原线程才能更新View而不是主线程才能更新View。

相关文章

  • Android Framework系列5-3 UI线程

    应用的UI线程 本章主要围绕以下几点讲解: 什么是UI线程 UI线程的启动流程,消息循环是怎么创建的 什么是UI线...

  • 子线程不能更新UI?

    看这里看这里:Android中子线程真的不能更新UI吗 只能在UI线程访问UI原因 Android的UI访问是没有...

  • 关于Android在非UI线程更新UI的问题。

    为了解决在Android非UI线程更新UI这个问题,Android提供了一些方法,从其他线程访问UI线程。 Act...

  • 理解JS 线程,和Android线程

    1,Android开发都知道,UI线程,和子线程; UI线程:可以操作ui界面, 子线程:不可以操作ui界面 2,...

  • Android Framework学习之UI线程

    1.什么是UI线程? 2.UI线程的启动流程,消息循环是怎么创建的? 3.了解Android的UI显示原理,UI线...

  • Android 的Handler消息机制

    Handler作用 Android 规定只有在主线程(UI线程)更新UI,否则会抛出异常,并且Android又建议...

  • Android系列:彻底了解Handler

    一、Handler 1.1 Android为什么==非ui线程==不能==更新ui== UI线程的机制 为什么UI...

  • Android 面试问题

    Android线程安全Android一般情况下会使用一个主线程控制UI,非主线程无法控制UI,Android4.0...

  • Android 真的不能在子线程更新UI吗?

    写过Android 代码的同学应该都听过Android不能在子线程更新UI,只能在主线程即UI线程处理视图。 猜一...

  • 消息通信

    为什么需要消息通信? Android的主线程即UI线程是非安全线程,只允许UI线程更新UI状态。其它任务线程处理完...

网友评论

      本文标题:Android Framework系列5-3 UI线程

      本文链接:https://www.haomeiwen.com/subject/sdrrqltx.html