深入了解Android的View工作原理

本文章讲解的内容是深入了解Android的View工作原理,建议对着示例项目阅读文章,示例项目链接如下:

ViewDemo

本文章分析的相关的源码基于Android SDK 29(Android 10.0,即Android Q)

Android的窗口结构

先从ActivitystartActivity系列方法开始说起,我选了一个常用的startActivitiy(Intent intent)方法,源码如下所示:

// Activity.java
@Override
public void startActivity(Intent intent) {
    this.startActivity(intent, null);
}

看下startActivity(Intent intent, @Nullable Bundle options)方法,源码如下所示:

// Activity.java
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
    if (options != null) {
        // 如果形式参数options不为空,就调用startActivityForResult(@RequiresPermission Intent intent, int requestCode)方法
        startActivityForResult(intent, -1, options);
    } else {
        // 如果形式参数options为空,就调用startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法,这里形式参数options为空,所以调用这个方法
        startActivityForResult(intent, -1);
    }
}

看下startActivityForResult(@RequiresPermission Intent intent, int requestCode)方法,源码如下所示:

// Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode) {
    startActivityForResult(intent, requestCode, null);
}

startActivityForResult(@RequiresPermission Intent intent, int requestCode)方法最终也是调用startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法。

看下startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法,源码如下所示:

// Activity.java
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    // 如果是第一次启动,成员变量mParent为空,否则不为空
    if (mParent == null) {
        // 如果是第一次启动,就执行以下逻辑
        options = transferSpringboardActivityOptions(options);
        // 调用Instrumentation的execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options)方法
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {
            // 如果是请求一个结果(result),我们可以使Activity不可见,直到收到结果,在onCreate(Bundle savedInstanceState)方法或者onResume()期间设置此代码将在此期间隐藏Activity,以避免闪烁,只有在请求结果时才可以这样做,因为这样可以保证在Activity完成时我们将获得信息,无论它发生了什么
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
    } else {
        // 如果不是第一次启动,就执行以下逻辑
        if (options != null) {
            // 如果形式参数options不为空,就调用startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            // 如果形式参数option为空,就调用startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode)方法
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

看下startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode)方法,源码如下所示:

// Activity.java
public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
        int requestCode) {
    startActivityFromChild(child, intent, requestCode, null);
}

startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode)方法最终也是调用startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法。

看下tartActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent, int requestCode, @Nullable Bundle options)方法,源码如下所示:

// Activity.java
public void startActivityFromChild(@NonNull Activity child, @RequiresPermission Intent intent,
        int requestCode, @Nullable Bundle options) {
    options = transferSpringboardActivityOptions(options);
    // 调用Instrumentation的execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options)方法
    Instrumentation.ActivityResult ar =
        mInstrumentation.execStartActivity(
            this, mMainThread.getApplicationThread(), mToken, child,
            intent, requestCode, options);
    if (ar != null) {
        mMainThread.sendActivityResult(
            mToken, child.mEmbeddedID, requestCode,
            ar.getResultCode(), ar.getResultData());
    }
    cancelInputsAndStartExitTransition(options);
}

我们可以看到,其实最终就是调用InstrumentationexecStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options)方法,源码如下所示:

// frameworks/base/core/java/android/app/Instrumentation.java
@UnsupportedAppUsage
public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    // 省略部分代码
    try {
        intent.migrateExtraStreamToClipData(who);
        intent.prepareToLeaveProcess(who);
        // 调用ActivityTaskManagerService的startActivity方法
        int result = ActivityTaskManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
         checkStartActivityResult(result, intent);
     } catch (RemoteException e) {
         throw new RuntimeException("Failure from system", e);
     }
     return null;
}

ActivityTaskManager.getService()方法得到的是IActivityTaskManager接口,它是通过进程间通讯(Inter-Process Communication,即IPC)调用的,IActivityTaskManager服务端ActivityTaskManagerService,所以最终是调用ActivityTaskManagerServicestartActivity方法,后面的调用过程,这里就不再详细地讲解,最后调用的是ActivityThread类的handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, Intent customIntent)方法,源码如下所示:

// frameworks/base/core/java/android/app/ActivityThread.java
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
                                     PendingTransactionActions pendingActions, Intent customIntent) {
    // 省略部分代码

    // 调用performLaunchActivity(ActivityClientRecord r, Intent customIntent)方法
    final Activity a = performLaunchActivity(r, customIntent);

    // 省略部分代码

    // 返回Activity
    return a;
}

看下performLaunchActivity(ActivityClientRecord r, Intent customIntent)方法,源码如下所示:

// frameworks/base/core/java/android/app/ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // 省略部分代码

    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        // 反射创建Activity
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to instantiate activity " + component
                            + ": " + e.toString(), e);
        }
    }

    try {
        // 创建Application对象,它是个单例,一个进程只有一个Application
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        // 省略部分代码

        if (activity != null) {
            // 省略部分代码
            // 调用Activity的attach方法,这个方法会创建PhoneWindow,它是Window抽象类的子类
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback,
                    r.assistToken);

            // 省略部分代码
            // 调用Activity的onCreate方法
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onCreate()");
            }
            r.activity = activity;
        }
        // 设为ON_CREATE状态
        r.setState(ON_CREATE);

        // updatePendingActivityConfiguration()方法的作用是从mActivities中读取数据以更新ActivityClientRecord,它在不同的线程中运行,所以要取ResourcesManager作为锁对象
        synchronized (mResourcesManager) {
            mActivities.put(r.token, r);
        }

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to start activity " + component
                            + ": " + e.toString(), e);
        }
    }

    // 返回Activity
    return activity;
}

在这个方法中,会调用Activityattach方法,源码如下所示:

// frameworks/base/core/java/android/app/Activity.java
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
                  Instrumentation instr, IBinder token, int ident,
                  Application application, Intent intent, ActivityInfo info,
                  CharSequence title, Activity parent, String id,
                  NonConfigurationInstances lastNonConfigurationInstances,
                  Configuration config, String referrer, IVoiceInteractor voiceInteractor,
                  Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    // 省略部分代码

    // 创建PhoneWindow对象,并且赋值给成员变量mWindow
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    // 省略部分代码
}

可以看到,成员变量mWindow其实就是PhoneWindow对象。

然后我们接着前面的逻辑,接着就会调用ActivityThread类的handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions)方法,然后Activity类的onStart方法就会被调用,然后调用ActivityThread类的handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason)方法,源码如下所示:

// frameworks/base/core/java/android/app/ActivityThread.java
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
                                 String reason) {
    // 省略部分代码

    // 调用Activity的onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    // 省略部分代码

    final Activity a = r.activity;

    // 省略部分代码
    if (r.window == null && !a.mFinished && willBeVisible) {
        // 得到Window,Window是抽象类,其中,PhoneWindow类继承Window类,它是Window类的唯一子类
        r.window = r.activity.getWindow();
        // 得到DecorView
        View decor = r.window.getDecorView();
        // 将DecorView设为不可见
        decor.setVisibility(View.INVISIBLE);
        // 得到ViewManager,ViewManager是一个接口,WindowManger也是一个接口,它继承ViewManager接口,其中,WindowManagerImpl类实现ViewManager接口,这里的vm可以看作是WindowMangerImpl对象
        ViewManager wm = a.getWindowManager();
        // 省略部分代码
        if (a.mVisibleFromClient) {
            if (!a.mWindowAdded) {
                a.mWindowAdded = true;
                // 调用WindowManagerImpl类的addView(View view, ViewGroup.LayoutParams params)方法,执行绘制流程
                wm.addView(decor, l);
            } else {
                // 省略部分代码
            }
        }

    } else if (!willBeVisible) {
        // 如果Window已经被添加,但是在恢复(resume)期间启动了另一个Activity,这样的话,就不要使Window可见
        if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
        r.hideForNow = true;
    }

    // 省略部分代码
}

View的绘制流程会在Activity的onResume方法之后执行。

最后调用WindowManagerImpl类的addView(View view, ViewGroup.LayoutParams params)方法,分别传入了DecorViewPhoneWindow属性,源码如下所示:

// frameworks/base/core/java/android/view/WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    // 省略部分代码

    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        // 调用WindowManagerGlobal的addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)方法
        mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
    }

    // 省略部分代码
}

看下WindowManagerGlobal类的addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow)方法,源码如下所示:

// frameworks/base/core/java/android/view/WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
                    Display display, Window parentWindow) {
    // 省略部分代码

    ViewRootImpl root;
    View panelParentView = null;

    synchronized (mLock) {
        // 省略部分代码

        // 创建ViewRootImpl对象
        root = new ViewRootImpl(view.getContext(), display);

        // 设置DecorView的LayoutParams
        view.setLayoutParams(wparams);

        // 将DecorView添加到View的ArrayList中
        mViews.add(view);
        // 将ViewRootImpl添加到ViewRootImpl的ArrayList中
        mRoots.add(root);
        // 将DecorView的LayoutParams添加到WindowManager.LayoutParams的ArrayList中
        mParams.add(wparams);

        try {
            // 调用ViewRootImpl的setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // 抛出BadTokenException或者InvalidDisplayException
            if (index >= 0) {
                removeViewLocked(index, true);
            }
            throw e;
        }
    }
}

看下ViewRootImpl类的setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    synchronized (this) {
        if (mView == null) {
            // 如果成员变量mView为空,就将传进来的DecorView赋值给它
            mView = view;

            // 省略部分代码
            // 调用requestLayout()方法
            requestLayout();
            // 省略部分代码

            // 调用View的assignParent(ViewParent parent)方法
            view.assignParent(this);
            // 省略部分代码
        }
    }
}

先看下View类的assignParent(ViewParent parent)方法,源码如下所示:

// frameworks/base/core/java/android/view/View.java
@UnsupportedAppUsage
void assignParent(ViewParent parent) {
    if (mParent == null) {
        // 如果这个View还没有parent,就将传进来的ViewRootImpl赋值给成员变量mParent
        mParent = parent;
    } else if (parent == null) {
        // 如果形式参数parent为空,就将成员变量mParent设为空
        mParent = null;
    } else {
        // 如果这个View已经有parent,再次设置parent就会抛出RuntimeException异常
        throw new RuntimeException("view " + this + " being added, but"
                + " it already has a parent");
    }
}

这个方法的作用是将ViewRootImpl和DecorView关联起来,这里传进来的是ViewRootImplViewRootImpl实现ViewParent接口,每一个Activity根布局DecorViewDecorViewparentViewViewRootImpl,所以在子元素中调用invalidate()方法这类的方法的时候,需要遍历找到parentView,最后都会调用ViewRootImpl相关的方法。

requestLayout()方法的作用是检查当前线程是不是创建ViewRootImpl所在的线程如果是就通知View执行绘制流程否则就抛出CalledFromWrongThreadException异常,将会在后面详细讲解。

总结一下:

Window是一个抽象类,它的具体实现是PhoneWindowWindowManager是外界访问Window的入口,Window的实现是位于WindowManagerService中,WindowMangerWindowManagerService是通过进程间通信(Inter-Process Communication,简称IPC)交互的。

Window分为三种窗口类型

  • 应用窗口对应于一个Activity,创建一个应用窗口只能在Activity内部完成,层级在1~99之间。
  • 子窗口必须依附于任何类型的父窗口,层级在1000~1999之间。
  • 系统窗口不需要对应于任何Activity,应用程序不能创建系统窗口,层级在2000~2999之间。

要注意的是,层级大的窗口会覆盖在层级小的窗口上。

setContentView(@LayoutRes int layoutResID)

我们看下一个经常使用的方法:setContentView(@LayoutRes int layoutResID)方法,要注意的是,我们的Activity类是继承AppCompatActivity类,源码如下所示:

// AppCompatActivity.java
@Override
public void setContentView(@LayoutRes int layoutResID) {
    // 调用AppCompatDelegate的setContentView(int resId)方法
    getDelegate().setContentView(layoutResID);
}

看下getDelegate()方法,源码如下所示:

// AppCompatActivity.java
@NonNull
public AppCompatDelegate getDelegate() {
    if (mDelegate == null) {
        // 如果mDelegate为空,就调用AppCompatDelegate抽象类的create(@NonNull Activity activity, @Nullable AppCompatCallback callback)方法
        mDelegate = AppCompatDelegate.create(this, this);
    }
    return mDelegate;
}

Appcompat最开始是出现在com.android.support:appcompat-v7库中,它是为了让Android SDK 7(Android 2.1,即Android Eclair)以上的设备可以使用ActionBar,而在com.android.support:appcompat-v7:21以上的库,AppCompat可以为Android SDK 7(Android 2.1,即Android Eclair)以上的设备带来Material Color PaletteWidget着色Toolbar等功能,并且用AppCompatActivity替代ActionBarActivity,在Android SDK 28(Android 9.0,即Android Pie)发布后,appcompat库都迁移到了AndroidX库,AndroidX库是Android Jetpack组件。

看下AppCompatDelegate抽象类的create(@NonNull Activity activity, @Nullable AppCompatCallback callback)方法,源码如下所示:

// AppCompatDelegate.java
@NonNull
public static AppCompatDelegate create(@NonNull Activity activity,
        @Nullable AppCompatCallback callback) {
    // 创建AppCompatDelegateImpl对象
    return new AppCompatDelegateImpl(activity, callback);
}

AppCompatDelegateImpl类继承AppCompatDelegate抽象类,看下AppCompatDelegateImpl类的setContentView(int resId)方法,源码如下所示:

// AppCompatDelegateImpl.java
@Override
public void setContentView(int resId) {
    // 创建DecorView,并且将其添加到Window
    ensureSubDecor();
    // 找到DecorView中的contentView
    ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
    // 移除contentView中所有的View
    contentParent.removeAllViews();
    // 根据传入的resId将要加载的XML布局添加到contentView
    LayoutInflater.from(mContext).inflate(resId, contentParent);
    // onContentChanged()方法是一个钩子方法,它会在屏幕的内容视图发生变化时调用
    mAppCompatWindowCallback.getWrapped().onContentChanged();
}

ensureSubDecor()方法的作用是创建DecorView并且将其添加到Window中同时创建面板菜单,源码如下所示:

// AppCompatDelegateImpl.java
private void ensureSubDecor() {
    if (!mSubDecorInstalled) {
        // 如果还没有创建DecorView,就调用createSubDecor()方法创建DecorView
        mSubDecor = createSubDecor();

        // 得到title的字符
        CharSequence title = getTitle();
        if (!TextUtils.isEmpty(title)) {
            // 如果设置了title,就执行以下逻辑
            if (mDecorContentParent != null) {
                mDecorContentParent.setWindowTitle(title);
            } else if (peekSupportActionBar() != null) {
                peekSupportActionBar().setWindowTitle(title);
            } else if (mTitleView != null) {
                mTitleView.setText(title);
            }
        }

        applyFixedSizeWindow();

        onSubDecorInstalled(mSubDecor);

        // 标记已经创建DecorView
        mSubDecorInstalled = true;

        // 创建面板菜单
        PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
        if (!mIsDestroyed && (st == null || st.menu == null)) {
            // 通过添加刷新通知消息到主线程对应的消息队列中来刷新界面,目的是防止onCreateOptionsMenu(Nenu menu)方法在Activity调用onCreate(@Nullable Bundle savedInstanceState)方法的途中被调用
            invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
        }
    }
}

createSubDecor()方法的作用是创建DecorView并且将其添加到Window中,源码如下所示:

// AppCompatDelegateImpl.java
private ViewGroup createSubDecor() {
    TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);

    if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
        a.recycle();
        // 如果使用AppCompatActivity,但是没有设置一个Theme.AppCompat的主题,就抛出IllegalStateException异常
        throw new IllegalStateException(
                "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
    }

    // 设置Window的属性
    if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
    } else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) {
        // 不允许没有标题的actionBar
        requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
    }
    if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
        requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
    }
    if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
        requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
    }
    mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
    a.recycle();

    // 调用ensureWindow()方法来检查DecorView是否已经添加到Window
    ensureWindow();
    // 调用成员变量mWindow的getDecorView()方法创建DecorView,要注意的是,成员变量mWindow声明为Window,Window是一个抽象类,这里的实现类是PhoneWindow,下面会详细讲解
    mWindow.getDecorView();

    // 得到LayoutInflater
    final LayoutInflater inflater = LayoutInflater.from(mContext);
    ViewGroup subDecor = null;


    // 根据标志添加到对应的布局
    if (!mWindowNoTitle) {
        if (mIsFloating) {
            // 如果窗口需要浮动,就添加id是abc_dialog_title_material的布局
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_dialog_title_material, null);

            // 浮动窗口不能有操作栏,重置标志
            mHasActionBar = mOverlayActionBar = false;
        } else if (mHasActionBar) {
            // 如果窗口有actionBar,就执行以下逻辑
            TypedValue outValue = new TypedValue();
            mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);

            Context themedContext;
            if (outValue.resourceId != 0) {
                themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
            } else {
                themedContext = mContext;
            }

            // 使用主题化上下文(themedContext)添加id是abc_screen_toolbar的布局
            subDecor = (ViewGroup) LayoutInflater.from(themedContext)
                    .inflate(R.layout.abc_screen_toolbar, null);

            mDecorContentParent = (DecorContentParent) subDecor
                    .findViewById(R.id.decor_content_parent);
            mDecorContentParent.setWindowCallback(getWindowCallback());

            // 将特性传给DecorContentParent
            if (mOverlayActionBar) {
                mDecorContentParent.initFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
            }
            if (mFeatureProgress) {
                mDecorContentParent.initFeature(Window.FEATURE_PROGRESS);
            }
            if (mFeatureIndeterminateProgress) {
                mDecorContentParent.initFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
            }
        }
    } else {
        if (mOverlayActionMode) {
            // 如果需要覆盖Activity的内容,就添加id是abc_screen_simple_overlay_action_mode的布局
            subDecor = (ViewGroup) inflater.inflate(
                    R.layout.abc_screen_simple_overlay_action_mode, null);
        } else {
            // 如果不需要覆盖Activity的内容,就添加id是abc_screen_simple的布局
            subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
        }

        // 省略部分代码
    }

    if (subDecor == null) {
        // 如果DecorView这个时候还是空,就抛出IllegalArgumentException异常
        throw new IllegalArgumentException(
                "AppCompat does not support the current theme features: { "
                        + "windowActionBar: " + mHasActionBar
                        + ", windowActionBarOverlay: "+ mOverlayActionBar
                        + ", android:windowIsFloating: " + mIsFloating
                        + ", windowActionModeOverlay: " + mOverlayActionMode
                        + ", windowNoTitle: " + mWindowNoTitle
                        + " }");
    }

    if (mDecorContentParent == null) {
        // 如果成员变量mDecorContentParent为空,就将DecorView中的id为title的TextView赋值给成员变量mTitleView
        mTitleView = (TextView) subDecor.findViewById(R.id.title);
    }

    // 让装饰可以选择适合系统窗口,例如:Window的装饰
    ViewUtils.makeOptionalFitsSystemWindows(subDecor);

    final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
            R.id.action_bar_activity_content);

    // 得到contentView
    final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
    if (windowContentView != null) {
        while (windowContentView.getChildCount() > 0) {
            // 如果已经有View添加到Window的contentView,就把它们迁移到contentView
            final View child = windowContentView.getChildAt(0);
            windowContentView.removeViewAt(0);
            contentView.addView(child);
        }

        windowContentView.setId(View.NO_ID);
        // 给contentView添加android.R.id.content的id,对Fragment挺有用
        contentView.setId(android.R.id.content);

        // decorContent有一个前景可绘制的设置(windowContentOverlay),设为空,是因为我们自己处理它
        if (windowContentView instanceof FrameLayout) {
            ((FrameLayout) windowContentView).setForeground(null);
        }
    }

    // 调用PhoneWindow的setContentView(View view)方法
    mWindow.setContentView(subDecor);

    contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
        @Override
        public void onAttachedFromWindow() {}

        @Override
        public void onDetachedFromWindow() {
            dismissPopups();
        }
    });

    // 返回DecorView
    return subDecor;
}

总结一下,ViewRoot的实现是ViewRootImpl类,WindowMananger负责窗口的管理,它是连接WindowManagerDecorView的纽带,View三大流程(measure、layout、draw)都是通过ViewRoot来完成,当Activity创建完成后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并且通过ViewRootImpl类的setView(View view, WindowManager.LayoutParams attrs, View panelParentView)方法和DecorView建立连接,每一个Activity都包含一个Window,它对应的窗口类型应用窗口,它的具体实现是PhoneWindowPhoneWindow包含DecorViewDecorView包含TitleView(ActionBar的容器)ContentView(窗口内容的容器),其中,ContentView是一个FrameLayout,它的idandroid.R.id.content,我们调用ActivitysetContentView方法,就是将内容添加到这个FrameLayout中,它实际上是调用PhoneWindowsetContentView方法。

绘制流程

绘制流程requestLayout()方法开始,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        // 调用checkThread()方法
        checkThread();
        mLayoutRequested = true;
        // 调用scheduleTraversals()方法
        scheduleTraversals();
    }
}

checkThread()方法的作用是检查当前线程是不是创建ViewRootImpl所在的线程,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
void checkThread() {
    if (mThread != Thread.currentThread()) {
        // 如果当前线程不是创建ViewRootImpl所在的线程就抛出CalledFromWrongThreadException异常
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

scheduleTraversals()方法的作用是让Android系统优先执行跟View更新相关的异步消息优先处理跟View更新相关的逻辑,在深入了解Android消息机制和源码分析(Java层和Native层)(上)深入了解Android消息机制和源码分析(Java层和Native层)(下)这两篇文章有提及过,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    // 省略部分代码
    final Thread mThread;
    // 省略部分代码

    public ViewRootImpl(Context context, Display display) {
        // 得到当前线程
        mThread = Thread.currentThread();
    }

    @UnsupportedAppUsage
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            // 添加同步屏障消息
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 执行添加同步屏障消息
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

    // 省略部分代码

    // 创建TraversalRunnable对象
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

    // 省略部分代码
}

成员变量mTraversalRunnableTraversalRunnable类型,TraversalRunnableViewRootImpl的被关键字final内部类,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        // 调用doTraversal()方法
        doTraversal();
    }
}

看下doTraversal()方法,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // 删除同步屏障消息
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        // 调用performTraversals()方法
        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}

看下performTraversals()方法,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
    // 省略部分代码

    if (mFirst || windowShouldResize || insetsChanged ||
            viewVisibilityChanged || params != null || mForceNextWindowRelayout) {
        // 省略部分代码

        if (!mStopped || mReportNextDraw) {
            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
                    (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
            if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
                    || mHeight != host.getMeasuredHeight() || contentInsetsChanged ||
                    updatedConfiguration) {
                int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
                int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                if (DEBUG_LAYOUT) Log.v(mTag, "Ooops, something changed!  mWidth="
                        + mWidth + " measuredWidth=" + host.getMeasuredWidth()
                        + " mHeight=" + mHeight
                        + " measuredHeight=" + host.getMeasuredHeight()
                        + " coveredInsetsChanged=" + contentInsetsChanged);

                // 调用performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)方法,分别传入的是根视图的宽的MeasureSpec和高的MeasureSpec
                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

                // 实现WindowManager.LayoutParams中的weight,根据需要增加尺寸,并且在需要时重新测量
                int width = host.getMeasuredWidth();
                int height = host.getMeasuredHeight();
                boolean measureAgain = false;

                if (lp.horizontalWeight > 0.0f) {
                    width += (int) ((mWidth - width) * lp.horizontalWeight);
                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
                            MeasureSpec.EXACTLY);
                    measureAgain = true;
                }
                if (lp.verticalWeight > 0.0f) {
                    height += (int) ((mHeight - height) * lp.verticalWeight);
                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
                            MeasureSpec.EXACTLY);
                    measureAgain = true;
                }

                if (measureAgain) {
                    if (DEBUG_LAYOUT) Log.v(mTag,
                            "And hey let's measure once more: width=" + width
                                    + " height=" + height);
                    // 如果需要重新测量,就再次调用performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)方法,分别传入的是根视图的宽的MeasureSpec和高的MeasureSpec
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
                }

                layoutRequested = true;
            }
        }
    } else {
        // 省略部分代码
    }

    // 省略部分代码

    final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
    boolean triggerGlobalLayoutListener = didLayout
            || mAttachInfo.mRecomputeGlobalAttributes;
    if (didLayout) {
        // 调用performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)方法
        performLayout(lp, mWidth, mHeight);

        // 省略部分代码
    }

    // 省略部分代码

    boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;

    if (!cancelDraw) {
        // 省略部分代码

        // 调用performDraw()方法
        performDraw();
    } else {
        if (isViewVisible) {
            // 如果取消draw,同时View是可见的,就再次调用scheduleTraversals()方法
            scheduleTraversals();
        } else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
            for (int i = 0; i < mPendingTransitions.size(); ++i) {
                mPendingTransitions.get(i).endChangingAnimations();
            }
            mPendingTransitions.clear();
        }
    }

    // 省略部分代码
}

View绘制三大流程分别从performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)方法、performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)方法和performDraw()方法开始。

measure流程

MeasureSpecView类的静态内部类,它代表了一个32位int值高2位代表SpecMode(测量模式)低30位代表SpecSize(在某个测量模式下的规格大小),源码如下所示:

// View.java
public static class MeasureSpec {
    private static final int MODE_SHIFT = 30;
    private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

    /** @hide */
    @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
    @Retention(RetentionPolicy.SOURCE)
    public @interface MeasureSpecMode {}

    public static final int UNSPECIFIED = 0 << MODE_SHIFT;

    public static final int EXACTLY     = 1 << MODE_SHIFT;

    public static final int AT_MOST     = 2 << MODE_SHIFT;

    // 省略部分代码
}

SpecMode分为三种模式,如下所示:

  • UNSPECIFIED父元素没有对子元素添加任何约束,它可以是任何大小,这个模式一般是在系统内部使用。
  • EXACTLY父元素已经确定了子元素的精确大小,也就是子元素的最终大小由SpecSize的值决定,对应于LayoutParams中的match_parent和具体数值这两种模式。
  • AT_MOST子元素的大小不能大于父元素的SpecSize的值,子元素的默认大小,对应于LayoutParams中的wrap_content。

ViewMeasureSpec是由父元素的MeasureSpec自身的LayoutParams共同决定的。

measure流程ViewRootImpl类的performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec)方法开始,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
    if (mView == null) {
        return;
    }
    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
    try {
        // 调用View的measure(int widthMeasureSpec, int heightMeasureSpec)方法
        mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

看下View类的measure(int widthMeasureSpec, int heightMeasureSpec)方法,源码如下所示:

// View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
    // 省略部分代码

    if (forceLayout || needsLayout) {
        // 省略部分代码
        if (cacheIndex < 0 || sIgnoreMeasureCache) {
            // 调用onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法
            onMeasure(widthMeasureSpec, heightMeasureSpec);
            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
        } else {
            // 省略部分代码
        }

        // 省略部分代码
    }

    // 省略部分代码
}

看下onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,源码如下所示:

// View.java
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

开发者可以重写这个方法来改变测量View的逻辑。

先看下getSuggestedMinimumWidth()方法和getSuggestedMinimumHeight()方法,源码如下所示:

// View.java
protected int getSuggestedMinimumHeight() {
    return (mBackground == null) ? mMinHeight : max(mMinHeight, mBackground.getMinimumHeight());

}

protected int getSuggestedMinimumWidth() {
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}

getSuggestedMinimumWidth()方法返回的是View的建议的最小宽度,如果View没有背景,就返回minWidth,否则就返回背景的最小宽度

getSuggestedMinimumHeight()方法返回的是View的建议的最小高度,如果View没有背景,就返回minHeight,否则就返回背景的最小高度

然后看下getDefaultSize(int size, int measureSpec)方法,源码如下所示:

// View.java
public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    // 得到测量模式
    int specMode = MeasureSpec.getMode(measureSpec);
    // 得到在这种测量模式下的规格大小
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

最后看下setMeasuredDimension(int measuredWidth, int measuredHeight)方法,源码如下所示:

// View.java
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
    boolean optical = isLayoutModeOptical(this);
    if (optical != isLayoutModeOptical(mParent)) {
        Insets insets = getOpticalInsets();
        int opticalWidth  = insets.left + insets.right;
        int opticalHeight = insets.top  + insets.bottom;

        // 得到这个View的测量宽度
        measuredWidth  += optical ? opticalWidth  : -opticalWidth;
        // 得到这个View的测量高度
        measuredHeight += optical ? opticalHeight : -opticalHeight;
    }
    // 调用setMeasuredDimensionRaw(int measuredWidth, int measuredHeight)方法
    setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}

看下setMeasuredDimensionRaw(int measuredWidth, int measuredHeight)方法,源码如下所示:

// View.java
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
    mMeasuredWidth = measuredWidth;
    mMeasuredHeight = measuredHeight;

    mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}

ViewgetMeasuredWidth()方法使用到成员变量mMeasuredWidth的值,它的作用是返回原始的测量宽度ViewgetMeasuredHeight()方法使用到成员变量mMeasuredHeight的值,它的作用是返回原始的测量高度,源码如下所示:

// View.java
public final int getMeasuredWidth() {
    return mMeasuredWidth & MEASURED_SIZE_MASK;
}

public final int getMeasuredHeight() {
    return mMeasuredHeight & MEASURED_SIZE_MASK;
}

前面看的是Viewmeasure过程,接下来看下ViewGroupmeasure过程,看下ViewGroup类的measureChildren(int widthMeasureSpec, int heightMeasureSpec)方法,源码如下所示:

// View.java
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
    final int size = mChildrenCount;
    final View[] children = mChildren;
    // 循环执行
    for (int i = 0; i < size; ++i) {
        // 得到ViewGroup中的子元素
        final View child = children[i];
        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
            // 如果子元素的状态不是隐藏状态,也就是可见状态(VISIBLE)或者不可见状态(INVISIBLE),就测量子元素,调用measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)方法
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

看下measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)方法,源码如下所示:

// View.java
protected void measureChild(View child, int parentWidthMeasureSpec,
        int parentHeightMeasureSpec) {
    final LayoutParams lp = child.getLayoutParams();

    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
            mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
            mPaddingTop + mPaddingBottom, lp.height);

    // 调用View的measure(int widthMeasureSpec, int heightMeasureSpec)方法
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

例子

最后看下一个例子:LinearLayout,它重写onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,源码如下所示:

// LinearLayout.java
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (mOrientation == VERTICAL) {
        // 如果是垂直方向,就调用measureVertical(int widthMeasureSpec, int heightMeasureSpec)方法
        measureVertical(widthMeasureSpec, heightMeasureSpec);
    } else {
        // 如果是水平方向,就调用measureHorizontal(int widthMeasureSpec, int heightMeasureSpec)方法
        measureHorizontal(widthMeasureSpec, heightMeasureSpec);
    }
}

这里只看垂直方向的这种情况,看下measureVertical(int widthMeasureSpec, int heightMeasureSpec)方法,源码如下所示:

// LinearLayout.java
void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
    // mTotalLength的值是所有子元素的高度加paddingTop和paddingBottom,要注意的是,它和LinearLayout本身的高度不同
    mTotalLength = 0;
    // 所有子元素的最大宽度
    int maxWidth = 0;
    int childState = 0;
    // 所有layout_weight属性的值小于等于0的子元素中宽度的最大值
    int alternativeMaxWidth = 0;
    // 所有layout_weight属性的值大于0的子元素中宽度的最大值
    int weightedMaxWidth = 0;
    boolean allFillParent = true;
    // 所有子元素的weight之和
    float totalWeight = 0;

    final int count = getVirtualChildCount();

    // 得到宽度的测量模式
    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    // 得到高度的测量模式
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

    boolean matchWidth = false;
    boolean skippedMeasure = false;

    final int baselineChildIndex = mBaselineAlignedChildIndex;
    final boolean useLargestChild = mUseLargestChild;

    int largestChildHeight = Integer.MIN_VALUE;
    int consumedExcessSpace = 0;

    int nonSkippedChildCount = 0;

    // 循环执行
    for (int i = 0; i < count; ++i) {
        // 得到子元素
        final View child = getVirtualChildAt(i);
        if (child == null) {
            mTotalLength += measureNullChild(i);
            continue;
        }

        if (child.getVisibility() == View.GONE) {
           i += getChildrenSkipCount(child, i);
           continue;
        }

        nonSkippedChildCount++;
        if (hasDividerBeforeChildAt(i)) {
            mTotalLength += mDividerHeight;
        }

        final LayoutParams lp = (LayoutParams) child.getLayoutParams();

        totalWeight += lp.weight;

        final boolean useExcessSpace = lp.height == 0 && lp.weight > 0;
        if (heightMode == MeasureSpec.EXACTLY && useExcessSpace) {
            final int totalLength = mTotalLength;
            mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
            skippedMeasure = true;
        } else {
            // 省略部分代码
            final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
            // 调用measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight)方法
            measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
                    heightMeasureSpec, usedHeight);

            // 省略部分代码
        }

        // 省略部分代码
    }

    // 省略部分代码

    // mTotalLength再加上paddingTop和paddingBottom之和
    mTotalLength += mPaddingTop + mPaddingBottom;

    int heightSize = mTotalLength;

    // 取高度大小和建议最小高度的最大值
    heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

    // 省略部分代码

    if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
        maxWidth = alternativeMaxWidth;
    }

    // maxWidth再加上paddingLeft和paddingRight之和
    maxWidth += mPaddingLeft + mPaddingRight;

    // 取最大宽度和建议最小宽度的最大值
    maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());

    // 调用setMeasuredDimension(int measuredWidth, int measuredHeight)方法
    setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
            heightSizeAndState);

    if (matchWidth) {
        forceUniformWidth(count, heightMeasureSpec);
    }
}

measureChildBeforeLayout(View child, int childIndex, int widthMeasureSpec, int totalWidth, int heightMeasureSpec, int totalHeight)方法的作用是测量LinearLayout中子元素,让这些子元素执行measure流程,同时系统通过成员变量mTotalLength来存储LinearLayout垂直方向初步高度,每测量一个子元素,都会使成员变量mTotalLength加上子元素的高度子元素在垂直方向上的margin属性和padding属性

layout流程

layout流程ViewRootImpl类的performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight)方法开始,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
                           int desiredWindowHeight) {
    // 省略部分代码

    final View host = mView;
    if (host == null) {
        return;
    }
    if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {
        Log.v(mTag, "Laying out " + host + " to (" +
                host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");
    }

    Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
    try {
        // 调用View的layout(int l, int t, int r, int b)方法
        host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());

        // 省略部分代码
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
    mInLayout = false;
}

调用layout(int l, int t, int r, int b)方法,并且left的位置传入0top的位置传入0right的位置传入测量宽度bottom的位置传入测量高度,源码如下所示:

// View.java
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
    // 省略部分代码

    int oldL = mLeft;
    int oldT = mTop;
    int oldB = mBottom;
    int oldR = mRight;

    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);

    if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
        // 调用onLayout(boolean changed, int left, int top, int right, int bottom)方法
        onLayout(changed, l, t, r, b);

        // 省略部分代码
    }

    // 省略部分代码
}

看下isLayoutModeOptical(Object o)方法,它的作用是如果是传进来是一个使用光学边界布局的ViewGroup就返回true否则就返回false,源码如下所示:

// View.java
public static boolean isLayoutModeOptical(Object o) {
    return o instanceof ViewGroup && ((ViewGroup) o).isLayoutModeOptical();
}

先看下setOpticalFrame(int left, int top, int right, int bottom)方法,源码如下所示:

// View.java
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
    Insets parentInsets = mParent instanceof View ?
            ((View) mParent).getOpticalInsets() : Insets.NONE;
    Insets childInsets = getOpticalInsets();
    // 调用setFrame(int left, int top, int right, int bottom)方法
    return setFrame(
            left   + parentInsets.left - childInsets.left,
            top    + parentInsets.top  - childInsets.top,
            right  + parentInsets.left + childInsets.right,
            bottom + parentInsets.top  + childInsets.bottom);
}

然后看下setFrame(int left, int top, int right, int bottom)方法,这个方法的作用是确定View的四个顶点位置也就是确定了子元素在父元素中的位置,源码如下所示:

// View.java
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected boolean setFrame(int left, int top, int right, int bottom) {
    boolean changed = false;

    if (DBG) {
        Log.d(VIEW_LOG_TAG, this + " View.setFrame(" + left + "," + top + ","
                + right + "," + bottom + ")");
    }

    if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
        changed = true;

        int drawn = mPrivateFlags & PFLAG_DRAWN;

        int oldWidth = mRight - mLeft;
        int oldHeight = mBottom - mTop;
        int newWidth = right - left;
        int newHeight = bottom - top;
        boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);

        // 使旧的位置无效
        invalidate(sizeChanged);

        // 将left赋值给成员变量mLeft
        mLeft = left;
        // 将top赋值给成员变量mTop
        mTop = top;
        // 将right赋值给成员变量mRight
        mRight = right;
        // 将bottom赋值给成员变量mBottom
        mBottom = bottom;
        mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);

        mPrivateFlags |= PFLAG_HAS_BOUNDS;


        // 省略部分代码
    }
    return changed;
}

ViewgetWidth()的值就是使用mRight的值减mLeft的值ViewgetHeight()的值就是使用mBottom的值减mTop的值,源码如下所示:

// View.java
@ViewDebug.ExportedProperty(category = "layout")
public final int getWidth() {
    return mRight - mLeft;
}

@ViewDebug.ExportedProperty(category = "layout")
public final int getHeight() {
    return mBottom - mTop;
}

接着上面,看下onLayout(boolean changed, int left, int top, int right, int bottom)方法,源码如下所示:

// View.java
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}

开发者可以重写这个方法来改变布局View的逻辑。

例子

最后看下一个例子:LinearLayout,它重写onLayout(boolean changed, int l, int t, int r, int b)方法,源码如下所示:

// LinearLayout.java
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (mOrientation == VERTICAL) {
        // 如果是垂直方向,就调用layoutVertical(int left, int top, int right, int bottom)方法
        layoutVertical(l, t, r, b);
    } else {
        // 如果是垂直方向,就调用layoutHorizontal(int left, int top, int right, int bottom)方法
        layoutHorizontal(l, t, r, b);
    }
}

这里只看垂直方向的这种情况,看下layoutVertical(int left, int top, int right, int bottom)方法,源码如下所示:

// LinearLayout.java
void layoutVertical(int left, int top, int right, int bottom) {
    final int paddingLeft = mPaddingLeft;

    int childTop;
    int childLeft;

    // 父元素的默认宽度
    final int width = right - left;
    // 子元素默认的right的值
    int childRight = width - mPaddingRight;

    // 子元素的可用空间
    int childSpace = width - paddingLeft - mPaddingRight;

    // 子元素的数量
    final int count = getVirtualChildCount();

    final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
    final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;

    // 根据设置的gravity属性,设置第一个子元素的top的值
    switch (majorGravity) {
       case Gravity.BOTTOM:
           childTop = mPaddingTop + bottom - top - mTotalLength;
           break;

       case Gravity.CENTER_VERTICAL:
           childTop = mPaddingTop + (bottom - top - mTotalLength) / 2;
           break;

       case Gravity.TOP:
       default:
           childTop = mPaddingTop;
           break;
    }

    // 循环执行
    for (int i = 0; i < count; i++) {
        // 得到子元素
        final View child = getVirtualChildAt(i);
        if (child == null) {
            childTop += measureNullChild(i);
        } else if (child.getVisibility() != GONE) {
            // 得到子元素的测量宽度
            final int childWidth = child.getMeasuredWidth();
            // 得到子元素的测量高度
            final int childHeight = child.getMeasuredHeight();

            // 得到子元素的LayoutParams
            final LinearLayout.LayoutParams lp =
                    (LinearLayout.LayoutParams) child.getLayoutParams();

            int gravity = lp.gravity;
            if (gravity < 0) {
                gravity = minorGravity;
            }
            final int layoutDirection = getLayoutDirection();
            final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
            // 根据子元素的gravity属性设置left的值
            switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                case Gravity.CENTER_HORIZONTAL:
                    childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                            + lp.leftMargin - lp.rightMargin;
                    break;

                case Gravity.RIGHT:
                    childLeft = childRight - childWidth - lp.rightMargin;
                    break;

                case Gravity.LEFT:
                default:
                    childLeft = paddingLeft + lp.leftMargin;
                    break;
            }

            if (hasDividerBeforeChildAt(i)) {
                // 如果有分割线,就添加分割线的高度
                childTop += mDividerHeight;
            }

            // 子元素的top的值加上marginTop的值
            childTop += lp.topMargin;
            // 调用setChildFrame(View child, int left, int top, int width, int height)方法设置子元素在父元素的布局位置
            setChildFrame(child, childLeft, childTop + getLocationOffset(child),
                    childWidth, childHeight);
            childTop += childHeight + lp.bottomMargin + getNextLocationOffset(child);

            i += getChildrenSkipCount(child, i);
        }
    }
}

setChildFrame(View child, int left, int top, int width, int height)方法的作用是设置子元素在父元素的布局位置,源码如下所示:

// LinearLayout.java
private void setChildFrame(View child, int left, int top, int width, int height) {
    child.layout(left, top, left + width, top + height);
}

这个方法调用了子元素layout(int l, int t, int r, int b)方法,使子元素执行layout流程

draw流程

draw流程ViewRootImpl类的performDraw()方法开始,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
private void performDraw() {
    // 省略部分代码

    try {
        // 调用draw(Canvas canvas)方法
        boolean canUseAsync = draw(fullRedrawNeeded);
        if (usingAsyncReport && !canUseAsync) {
            mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
            usingAsyncReport = false;
        }
    } finally {
        mIsDrawing = false;
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }

    // 省略部分代码
}

看下draw(Canvas canvas)方法,源码如下所示:

// frameworks/base/core/java/android/view/ViewRootImpl.java
@CallSuper
public void draw(Canvas canvas) {
    final int privateFlags = mPrivateFlags;
    mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;

    int saveCount;

    // 第一步:如果有必要,就画背景
    drawBackground(canvas);

    // 一般情况下,如果可能的话会跳过第二步和第五步
    final int viewFlags = mViewFlags;
    boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
    boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
    if (!verticalEdges && !horizontalEdges) {
        // 第三步:画View的内容
        onDraw(canvas);

        // 第四步:画子元素
        dispatchDraw(canvas);

        drawAutofilledHighlight(canvas);

        // 覆盖(overlay)是内容的一部分,它在前景下面绘制
        if (mOverlay != null && !mOverlay.isEmpty()) {
            mOverlay.getOverlayView().dispatchDraw(canvas);
        }

        // 第六步:绘制装饰(前景、滚动条)
        onDrawForeground(canvas);

        // 第七步:绘制默认焦点突出显示
        drawDefaultFocusHighlight(canvas);

        if (debugDraw()) {
            debugDrawFocus(canvas);
        }

        // 执行到这里代表整个绘制流程就执行完成了
        return;
    }

    boolean drawTop = false;
    boolean drawBottom = false;
    boolean drawLeft = false;
    boolean drawRight = false;

    float topFadeStrength = 0.0f;
    float bottomFadeStrength = 0.0f;
    float leftFadeStrength = 0.0f;
    float rightFadeStrength = 0.0f;

    // 第二步:如果有必要,就保存画布的图层,准备褪色(fading)
    int paddingLeft = mPaddingLeft;

    final boolean offsetRequired = isPaddingOffsetRequired();
    if (offsetRequired) {
        paddingLeft += getLeftPaddingOffset();
    }

    int left = mScrollX + paddingLeft;
    int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
    int top = mScrollY + getFadeTop(offsetRequired);
    int bottom = top + getFadeHeight(offsetRequired);

    if (offsetRequired) {
        right += getRightPaddingOffset();
        bottom += getBottomPaddingOffset();
    }

    final ScrollabilityCache scrollabilityCache = mScrollCache;
    final float fadeHeight = scrollabilityCache.fadingEdgeLength;
    int length = (int) fadeHeight;

    // 剪辑(clip)褪色长度,如果顶部和底部褪色重叠将会产生奇怪的工件
    if (verticalEdges && (top + length > bottom - length)) {
        length = (bottom - top) / 2;
    }

    // 如果有必要,还可以剪辑(clip)水平渐变
    if (horizontalEdges && (left + length > right - length)) {
        length = (right - left) / 2;
    }

    if (verticalEdges) {
        topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
        drawTop = topFadeStrength * fadeHeight > 1.0f;
        bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
        drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
    }

    if (horizontalEdges) {
        leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
        drawLeft = leftFadeStrength * fadeHeight > 1.0f;
        rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
        drawRight = rightFadeStrength * fadeHeight > 1.0f;
    }

    saveCount = canvas.getSaveCount();
    int topSaveCount = -1;
    int bottomSaveCount = -1;
    int leftSaveCount = -1;
    int rightSaveCount = -1;

    int solidColor = getSolidColor();
    if (solidColor == 0) {
        if (drawTop) {
            topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);
        }

        if (drawBottom) {
            bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);
        }

        if (drawLeft) {
            leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);
        }

        if (drawRight) {
            rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);
        }
    } else {
        scrollabilityCache.setFadeColor(solidColor);
    }

    // 第三步:画View的内容
    onDraw(canvas);

    // 第四步:画子元素
    dispatchDraw(canvas);

    // 第五步:如果有必要,就画褪色的边缘(fading edges)和恢复图层(restore layers)
    final Paint p = scrollabilityCache.paint;
    final Matrix matrix = scrollabilityCache.matrix;
    final Shader fade = scrollabilityCache.shader;

    // 必须按照保存的顺序进行恢复
    if (drawRight) {
        matrix.setScale(1, fadeHeight * rightFadeStrength);
        matrix.postRotate(90);
        matrix.postTranslate(right, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        if (solidColor == 0) {
            canvas.restoreUnclippedLayer(rightSaveCount, p);

        } else {
            canvas.drawRect(right - length, top, right, bottom, p);
        }
    }

    if (drawLeft) {
        matrix.setScale(1, fadeHeight * leftFadeStrength);
        matrix.postRotate(-90);
        matrix.postTranslate(left, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        if (solidColor == 0) {
            canvas.restoreUnclippedLayer(leftSaveCount, p);
        } else {
            canvas.drawRect(left, top, left + length, bottom, p);
        }
    }

    if (drawBottom) {
        matrix.setScale(1, fadeHeight * bottomFadeStrength);
        matrix.postRotate(180);
        matrix.postTranslate(left, bottom);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        if (solidColor == 0) {
            canvas.restoreUnclippedLayer(bottomSaveCount, p);
        } else {
            canvas.drawRect(left, bottom - length, right, bottom, p);
        }
    }

    if (drawTop) {
        matrix.setScale(1, fadeHeight * topFadeStrength);
        matrix.postTranslate(left, top);
        fade.setLocalMatrix(matrix);
        p.setShader(fade);
        if (solidColor == 0) {
            canvas.restoreUnclippedLayer(topSaveCount, p);
        } else {
            canvas.drawRect(left, top, right, top + length, p);
        }
    }

    canvas.restoreToCount(saveCount);

    drawAutofilledHighlight(canvas);

    // 覆盖(overlay)是内容的一部分,它在前景下面绘制
    if (mOverlay != null && !mOverlay.isEmpty()) {
        mOverlay.getOverlayView().dispatchDraw(canvas);
    }

    // 第六步:绘制装饰(前景和滚动条)
    onDrawForeground(canvas);

    if (debugDraw()) {
        debugDrawFocus(canvas);
    }
}

看下View类的dispatchDraw(Canvas canvas)方法,源码如下所示:

// View.java
protected void dispatchDraw(Canvas canvas) {

}

这个方法没有任何逻辑处理ViewGroup类继承View类,看下ViewGroup重写了这个方法,源码如下所示:

// ViewGroup.java
@Override
protected void dispatchDraw(Canvas canvas) {
    boolean usingRenderNodeProperties = canvas.isRecordingFor(mRenderNode);
    // 得到子元素的数量
    final int childrenCount = mChildrenCount;
    // 得到子元素的数组,也就是View数组
    final View[] children = mChildren;
    int flags = mGroupFlags;

    if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
        final boolean buildCache = !isHardwareAccelerated();
        // 循环执行
        for (int i = 0; i < childrenCount; i++) {
            // 得到子元素
            final View child = children[i];
            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
                // 如果这个View是可见状态,就处理其动画
                final LayoutParams params = child.getLayoutParams();
                attachLayoutAnimationParameters(child, params, i, childrenCount);
                bindLayoutAnimation(child);
            }
        }

        final LayoutAnimationController controller = mLayoutAnimationController;
        if (controller.willOverlap()) {
            mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
        }

        controller.start();

        mGroupFlags &= ~FLAG_RUN_ANIMATION;
        mGroupFlags &= ~FLAG_ANIMATION_DONE;

        if (mAnimationListener != null) {
            mAnimationListener.onAnimationStart(controller.getAnimation());
        }
    }

    // 省略部分代码
    final ArrayList<View> preorderedList = usingRenderNodeProperties
            ? null : buildOrderedChildList();
    final boolean customOrder = preorderedList == null
            && isChildrenDrawingOrderEnabled();
    for (int i = 0; i < childrenCount; i++) {
        // 省略部分代码
        if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
            // 调用drawChild(Canvas canvas, View child, long drawingTime)方法
            more |= drawChild(canvas, child, drawingTime);
        }
    }
    // 省略部分代码
}

看下drawChild(Canvas canvas, View child, long drawingTime)方法,源码如下所示:

// ViewGroup.java
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
    return child.draw(canvas, this, drawingTime);
}

这个方法调用Viewdraw(Canvas canvas)方法。

总结一下,draw流程分为以下七步

  1. 画背景。
  2. 如果有必要,就保存画布的图层,准备褪色(fading)。
  3. 画View的内容。
  4. 画子元素。
  5. 如果有必要,画褪色的边缘(fading edges)和恢复图层(restore layers)。
  6. 绘制装饰(前景和滚动条)。
  7. 绘制默认焦点突出显示。

例子

最后看下一个例子:LinearLayout,它重写onDraw(Canvas canvas)方法,源码如下所示:

// LinearLayout.java
@Override
protected void onDraw(Canvas canvas) {
    if (mDivider == null) {
        // 如果没有分割线,就结束方法
        return;
    }

    if (mOrientation == VERTICAL) {
        // 如果是垂直方向,就调用drawDividersVertical(Canvas canvas)方法
        drawDividersVertical(canvas);
    } else {
        // 如果是水平方向,就调用drawDividersHorizontal(Canvas canvas)方法
        drawDividersHorizontal(canvas);
    }
}

这里只看垂直方向的这种情况,看下drawDividersVertical(Canvas canvas)方法,源码如下所示:

// LinearLayout.java
void drawDividersVertical(Canvas canvas) {
    // 得到子元素的虚拟数量
    final int count = getVirtualChildCount();
    // 循环执行
    for (int i = 0; i < count; i++) {
        // 得到子元素
        final View child = getVirtualChildAt(i);
        if (child != null && child.getVisibility() != GONE) {
            if (hasDividerBeforeChildAt(i)) {
                // 如果子元素的状态不是隐藏状态,也就是可见状态(VISIBLE)或者不可见状态(INVISIBLE),同时这个View有分割线,就画水平的分割线
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                final int top = child.getTop() - lp.topMargin - mDividerHeight;
                drawHorizontalDivider(canvas, top);
            }
        }
    }

    if (hasDividerBeforeChildAt(count)) {
        // 得到最后一个不是隐藏状态,也就是可见状态(VISIBLE)或者不可见状态(INVISIBLE)的View
        final View child = getLastNonGoneChild();
        int bottom = 0;
        if (child == null) {
            bottom = getHeight() - getPaddingBottom() - mDividerHeight;
        } else {
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            bottom = child.getBottom() + lp.bottomMargin;
        }
        // 画水平的分割线
        drawHorizontalDivider(canvas, bottom);
    }
}

这个方法的作用是画水平的分割线

在子线程更新UI的问题

先看下如下第一个例子,代码如下所示:

package com.tanjiajun.viewdemo

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

/**
 * Created by TanJiaJun on 2020/10/8.
 */
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 创建子线程,并且启动它
        Thread {
            // 在子线程更新UI,设置id为tv_content的TextView的文本为谭嘉俊
            findViewById<TextView>(R.id.tv_content).text = "谭嘉俊"
        }.start()
    }

}

这段代码是在子线程更新UI,结果很顺利地执行完毕,并且符合预期。

我修改下第一个例子,得到第二个例子,代码如下所示:

package com.tanjiajun.viewdemo

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

/**
 * Created by TanJiaJun on 2020/10/8.
 */
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 创建子线程,并且启动它
        Thread {
            // 让子线程睡眠一秒
            Thread.sleep(1000)
            // 在子线程更新UI,设置id为tv_content的TextView的文本为谭嘉俊
            findViewById<TextView>(R.id.tv_content).text = "谭嘉俊"
        }.start()
    }

}

这段代码也是在子线程更新UI并且让子线程睡眠一秒,结果就抛出了如下异常:

2020-10-08 17:02:25.544 8619-8665/com.tanjiajun.viewdemo E/AndroidRuntime: FATAL EXCEPTION: Thread-2
    Process: com.tanjiajun.viewdemo, PID: 8619
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7753)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1225)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at android.view.View.requestLayout(View.java:23093)
        at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
        at android.view.View.requestLayout(View.java:23093)
        at android.widget.TextView.checkForRelayout(TextView.java:8908)
        at android.widget.TextView.setText(TextView.java:5730)
        at android.widget.TextView.setText(TextView.java:5571)
        at android.widget.TextView.setText(TextView.java:5528)
        at com.tanjiajun.viewdemo.MainActivity$onCreate$1.run(MainActivity.kt:20)
        at java.lang.Thread.run(Thread.java:764)

抛出了CalledFromWrongThreadException异常,在前面讲解checkThread()方法的时候也提及过,这个方法的作用是检查当前线程是不是创建ViewRootImpl所在的线程如果是就通知View执行绘制流程否则就抛出CalledFromWrongThreadException异常,示例代码的ViewRootImpl是在主线程创建的,也就是判断是否为主线程第一个例子没有抛出CalledFromWrongThreadException异常的原因是,因为调用setText(CharSequence text)方法的时候ViewRootImpl还没创建,View的绘制流程会在Activity的onResume方法之后执行,也就是ViewRootImpl是在onResume方法之后创建的,所以checkThread()方法还没调用,因此这个时候通知UI刷新就不会抛出CalledFromWrongThreadException异常,第二个例子抛出CalledFromWrongThreadException异常的原因是,因为让线程睡眠一秒,可能这个时候onResume方法已经执行了,并且已经在主线程 创建ViewRootImpl,这个时候调用setText(CharSequence text)通知UI刷新就会调用checkThread()方法,然后得到当前线程是子线程,和主线程不是同一个线程,因此就抛出CalledFromWrongThreadException异常。

其实Google是如下说法:

The Android UI toolkit is not thread-safe.

Google的意思是Android的UI toolkit不是线程安全的,Google也没说不允许在工作线程(非主线程)中更新UI。

题外话

介绍一个Android开源项目(AOSP)的代码搜索工具,网址如下:

Android开源项目(AOSP)的代码搜索工具

我的GitHub:TanJiaJunBeyond

Android通用框架:Android通用框架

我的掘金:谭嘉俊

我的简书:谭嘉俊

我的CSDN:谭嘉俊

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270