Activity启动流程源码讲解(上)
https://blog.youkuaiyun.com/weixin_37730482/article/details/69569670
通过上一章节,我们知道了以下内容
1.ActivityThread类的performLaunchActivity方法是启动Activity的入口。
2.performLaunchActivity方法中,核心功能是 初始化上下文对象,获取ClassLoader,利用获取的ClassLoader创建Activity,创建Application执行其onCreate方法,执行Activity源码类的attach方法,最后执行Activity的onCreate方法。
3.Activity的attach方法中,使用PhoneWindow对象创建Window对象。然后再PhoneWindow的构造方法中初始化DecorView。
4.onCreate方法中 执行setContentView()方法。
<4.1> 自己的Activity通过 Activity类的setContentView方法 利用PhoneWindow类的setContentView方法 创建DecorView 然后通过DecorView生成一个id是com.android.internal.R.id.content的ViewGroup。并初始化各种系统的View。比如导航栏等等。
<4.2> 再通过 AppCompatActivity的setContentView方法 将PhoneWindow类的setContentView方法生成的id是com.android.internal.R.id.content的ViewGroup为父布局 解析自己的布局文件。
这里 仅仅执行到Activity的onCreate方法。所以仅仅是 由xml布局文件 显示出一个View结构。下面我们看一下 Activity的下一个生命周期方法 onResume。
一.Activity启动流程之onResume方法
源码讲解之 ActivityThread类
handleResumeActivity方法源码
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {
// TODO Push resumeArgs into the activity for consideration
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
if (r == null) {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
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);
//获取ViewManger
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//ViewManager添加View
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {
Looper.myQueue().addIdleHandler(new Idler());
}
由上可知,handleResumeActivity方法的核心功能是
<1> 执行本类的performResumeActivity方法。生成一个ActivityClientRecord对象。稍后详解。
<2> 获取ViewManager对象,调用ViewManager对象的addView方法。
performResumeActivity方法源码
public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,String reason) {
final ActivityClientRecord r = mActivities.get(token);
if (r.getLifecycleState() == ON_RESUME) {
if (finalStateRequest) {
r.hideForNow = false;
r.activity.mStartedActivity = false;
}
try {
r.activity.onStateNotSaved();
r.activity.mFragments.noteStateNotSaved();
checkAndBlockForNetworkAccess();
if (r.pendingIntents != null) {
deliverNewIntents(r, r.pendingIntents);
r.pendingIntents = null;
}
if (r.pendingResults != null) {
deliverResults(r, r.pendingResults, reason);
r.pendingResults = null;
}
r.activity.performResume(r.startsNotResumed, reason);
r.state = null;
r.persistentState = null;
r.setState(ON_RESUME);
} catch (Exception e) {
}
return r;
}
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
该方法的核心就是 通过Binder获取ActivityClientRecord 对象。
因为 上述 获取ViewManager对象,调用ViewManager对象的addView方法。 所以我们看一下ViewManager
/** Interface to let you add and remove child views to an Activity. To get an instance
* of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*/
public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}
ViewManager是一个接口,提供了 添加View 更新View 删除View 方法。
WindowManager接口 继承了 ViewManager接口。
public interface WindowManager extends ViewManager
WindowManagerImpl类 实现了 WindowManager接口。
public final class WindowManagerImpl implements WindowManager {
}
所以 ViewManager对象的addView方法 其实执行的是 WindowManagerImpl类的addView方法。
WindowManagerImpl类的addView方法源码
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
也就是说,该方法的核心代码是 执行WindowManagerGlobal类的addView方法。
WindowManagerGlobal类的addView方法源码
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//核心代码1 创建ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
//核心代码2 将view和root存入ArrayList集合中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//核心代码3 调用ViewRootImpl的setView方法
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
}
}
}
也就是说,WindowManagerGlobal类的addView方法 核心代码就是创建ViewRootImpl对象,并且调用ViewRootImpl的setView方法。
ViewRootImpl类的构造方法源码
public ViewRootImpl(Context context, Display display) {
//获取上下文对象
mContext = context;
//是一个Binder对象,用于进程间通信
mWindowSession = WindowManagerGlobal.getWindowSession();
//获取当前线程 requestLayout时,检测是否主线程更新UI
mThread = Thread.currentThread();
}
ViewRootImpl类的setView方法源码
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
requestLayout();
if (res < WindowManagerGlobal.ADD_OKAY) {
switch (res) {
case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not valid; is your activity running?");
case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
throw new WindowManager.BadTokenException(
"Unable to add window -- token " + attrs.token
+ " is not for an application");
case WindowManagerGlobal.ADD_APP_EXITING:
throw new WindowManager.BadTokenException(
"Unable to add window -- app for token " + attrs.token
}
}
}
}
也就是说,ViewRootImpl类的setView方法 核心代码就是执行requestLayout()方法,并且根据WindowManagerGlobal的状态 抛出不同的window异常。
requestLayout()方法源码
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
checkThread()方法源码
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
二.总结
1.总结
<1> 通过 ActivityThread类 的 handleResumeActivity方法 执行到Activity的onResume方法。
<2> 该方法 核心功能有 通过Binder获取ActivityClientRecord对象,通过ActivityClientRecord对象获取Activity对象,通过Activity对象获取ViewManager对象,最后执行ViewManager的addView方法。
<3> ViewManager是接口,WindowManager也是接口并且继承ViewManager,WindowManagerImpl类 实现了 WindowManager接口。WindowManagerImpl类的addView方法调用的是WindowManagerGlobal类的addView方法。
<4> WindowManagerGlobal类的addView方法中 初始化了ViewRootImpl对象。
root = new ViewRootImpl(view.getContext(), display);
2.Activity、PhoneWindow、DecorView、ViewRootImpl 这几个关键的知识点
<1> Activity:Android四大组件之一。在ActivityThread类中的performLaunchActivity方法中,利用Instrumentation类的newActivity方法,传参ClassLoader对象 创建Activity。
<2> PhoneWindow:Window类的继承类,(包名:com.android.internal.policy),是Activity和View交互的中间层,主要的操作是:创建DecorView,处理输入法等等。在Activity类的attach方法中初始化。
<3> DecorView:窗体的顶级视图,继承FrameLayout类,在PhoneWindow类的构造方法中和setContentView方法执行installDecor()方法时初始化。主要用来生成一个id是com.android.internal.R.id.content的ViewGroup。
<4> ViewRootImpl:创建时机是 在ActivityThread类中的handleResumeActivity方法中,准备执行Activity的onResume方法时,使用ViewManager对象添加View。最终走到WindowManagerGlobal类的addView方法,在此方法中创建ViewRootImpl对象。ViewRootImpl主要用来绘制View。
3.子线程不能更新UI问题
ViewRootImpl类中有一个checkThread方法。而checkThread()方法调用地方比较多,比如本类的requestLayout()方法就会调用。也就是说绘制View的时候,可能会执行到这个方法。所以子线程不能更新UI。但是某些特殊的情况下子线程更新UI也是没有问题的。
具体详情:https://blog.youkuaiyun.com/weixin_37730482/article/details/72851014
4.UI优化
了解了Activity的启动过程,从onCreate方法到setContentView方法再到onResume方法。我们可以总结出几点UI优化的内容。
<1> 我们的页面在滑动或者首次打开的时候,最主要是加载布局,加载布局的耗时点主要在inflate布局,而inflate布局主要耗时分为两部分:读取、解析layout xml文件耗时。反射创建View耗时。
<2> 所以为了减少加载布局的耗时,我们可以优化一下我们的布局,主要是减少布局嵌套。
详情:https://blog.youkuaiyun.com/weixin_37730482/article/details/76157085
<3> UI的卡顿,主要发生在主线程,所以一些耗时的操作我们最好在子线程中操作。
详情:https://blog.youkuaiyun.com/weixin_37730482/article/details/72781767