Activity创建流程
我们的activity通过调用phonewindow的setcontentview的方法来实现布局
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
*
* @param layoutResID Resource ID to be inflated.
*
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
phonewindow是作为view与activity之间的一个管理工具,创建是在activity的attach方法中,作为window的一个实现类。
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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,
IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
//.........
因为需要管理View,创建出了 PhoneWindow; 因为需要根据主题显示不同的布局结构,创建出了根View DecorView; 因为需要处理View的各种事件,包括绘制、事件分发,创建出了ViewRootImpl。大家各忙各的,并听命于Activity。
viewRootImpl的创建时机
View 的三大流程都是通过 RootViewImpl 来完成的,在 ActivityThread 中,当 Activity 对象被创建完毕后,在 onResume 后,就会通过 WindowManager 将 DecorView 添加到窗口上,在这个过程中会创建 ViewRootImpl:
ActivityThread.handleResumeActivity
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// .....
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
//设置窗口类型为应用类型
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//添加 decor 到 window 中
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
}
//....
}
WindowManagerImpl.addView
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal.addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//检查参数是否合法
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//.....
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//创建 ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//将 Window 所对应的 View,ViewRootImp,params 顺序添加到列表中,这一步是为了方便更新和删除 View
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//把 Window 对应的 View 设置给创建的 ViewRootImpl
//通过 ViewRootImpl 来更新界面并添加到 WIndow中。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
Activity、PhoneWindow、DecorView、ViewRootImpl 之间的关系?
PhoneWindow:是Activity和View交互的中间层,帮助Activity管理View。
DecorView:是所有View的最顶层View,是所有View的parent。
ViewRootImpl:用于处理View相关的事件,比如绘制,事件分发,也是DecorView的parent。但是他并不是一个真正的 View,只是继承了 ViewParent 接口,用来掌管 View 的各种事件,包括 requestLayout、invalidate、dispatchInputEvent 等等。
四者的创建时机?
Activity创建于performLaunchActivity方法中,在startActivity时候触发。
PhoneWindow,同样创建于performLaunchActivity方法中,再具体点就是Activity的attach方法。
DecorView,创建于setContentView->PhoneWindow.installDecor。
ViewRootImpl,创建于handleResumeActivity方法中,最后通过addView被创建。
为什么在onResume中拿不到控件宽高
ActivityThrad在handleResumeActivity里调用了performResumeActivity,执行了onResume()方法,这个时候还没有绘制好页面,所以拿不到测量控件的宽高,
线程更新UI导致崩溃的原因?
在触发绘制方法requestLayout中,有个checkThread方法:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
其中对mThread和当前线程进行了比较。而mThread是在ViewRootImpl实例化的时候赋值的。
所以崩溃的原因就是 view被绘制到界面时候的线程(也就是ViewRootImpl被创建时候的线程)和进行UI更新时候的线程不是同一个线程。
ActivityThread 是什么,它是一个线程吗,如何被启动的?
它不是一个线程,它是运行在 App 进程中的主线程中的一个方法中。当 App 进程创建时会执行 ActivityThread.main(),ActivityThread.main() 首先会创建 Looper 执行 Looper.prepareMainLooper();然后创建 ActivityThread 并调用 ActivityThread.attach() 方法告诉 ActivityManagerService 我们创建了一个应用 并将 ApplicationThread 传给 ActivityManagerService;最后调用 Looper.loop()。
ViewRootImpl 和 DecorView 的关系是什么?
是在 PhoneWindow.installDecor -> generateLayout 中设置的。在 ViewRootImpl.setView 里,通过 DecorView.assignParent 把 ViewRootImpl 设置为 DecorView 的 parent。
所以 ViewRootImpl 和 DecorView 的关系就是 ViewRootImpl 是 DecorView 的 parent。因为 DecorView 是我们布局的顶层,现在我们就知道层层调用 requestLayout 等方法是怎么调用到 ViewRootImpl 里的了。
View的第一次绘制发生在什么时候?
既然开始说到了 View 的绘制流程,那整个流程是什么时候触发的呢?
答案是在 ActivityThread.handleResumeActivity 里触发的。
ActivityThread.handleResumeActivity 里会调用 wm.addView 来添加 DecorView,wm 是 WindowManagerImpl;
最终通过 WindowManagerImpl.addView -> WindowManagerGlobal.addView -> ViewRootImpl.setView -> ViewRootImpl.requestLayout 就触发了第一次 View 的布局、测量、绘制的流程。
ViewRootImpl 创建的时机?
从上面流程里可以看到,ViewRootImpl 也是在 ActivityThread.handleResumeActivity 里创建的。具体是在 WindowManagerGlobal.addView 中创建的。这时候主要是为了把 DecorView 添加到页面中(ViewRootImpl.setView )。