@TryLoveCatch
2022-05-05T10:27:42.000000Z
字数 18127
阅读 574
Android知识体系
Activity启动流程 可以参考上面的,我们直接说ApplicationThread.scheduleLaunchActivity(),发送消息给H,然后H调用handleLaunchActivity
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
// Initialize before creating the activity
WindowManagerGlobal.initialize();
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
...
}
再来看下performLaunchActivity
ActivityThread.performLaunchActivity
ActivityThread.performLaunchActivity {
//类似Application的创建过程,通过classLoader加载到activity.
activity = mInstrumentation.newActivity(classLoader,
component.getClassName(), r.intent);
//因为Activity有界面,所以其Context是ContextThemeWrapper类型,但实现类仍是ContextImpl.
Context appContext = createBaseContextForActivity(r, activity);
activity.attach(context,mInstrumentation,application,...);
//调用activity.oncreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
...
//调用Activity的onstart方法
activity.performStart();
//调用activitu的OnRestoreInstanceState方法进行Window数据恢复
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
}
由上面这些源码我们可以知道
handleLaunchActivity
- mInstrumentation.newActivity
- activity.attach
- mInstrumentation.callActivityOnCreate
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}- activity.performStart()
- mInstrumentation.callActivityOnRestoreInstanceState
handleResumeActivity
- activity.performResume()
所以,handleLaunchActivity里面会调用onCreate,onStart,onRestoreInstanceState,onResume;onCreate里面又回调用setContextView。还有就是,一个 Activity 界面的绘制,其实是在 onResume() 之后才开始的
在ActivityThread.performLaunchActivity中,创建Activity的实例,接着会调用Activity.attach()来初始化一些内容,而Window对象就是在attach里进行创建初始化赋值的。
Activity.attach
final void attach(...) {
...
mWindow = new PhoneWindow(this);
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
...
}
从上面两个代码可以看出来,Activity在onCreate之前,先创建了一个Window,即PhoneWindow,并设置了WindowManager,因此一个Activity对应着一个Window也就是应用窗口。
由上面的代码可知Window类的成员变量mWindowManager和Activity的成员变量mWindowManager都是指向同一个WindowManager对象,而WindowManager对象是调用如下代码获取:
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)
而我们知道Activity是继承Context类的,ContextImpl类中实现了WindowManager服务的注册,代码如下:
class ContextImpl extends Context {
static {
registerService(WINDOW_SERVICE, new ServiceFetcher() {
Display mDefaultDisplay;
public Object getService(ContextImpl ctx) {
Display display = ctx.mDisplay;
if (display == null) {
if (mDefaultDisplay == null) {
DisplayManager dm = (DisplayManager)ctx.getOuterContext().
getSystemService(Context.DISPLAY_SERVICE);
mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
}
display = mDefaultDisplay;
}
return new WindowManagerImpl(display);
}});
.......
}
}
有上面代码可知,在ContextImpl类中注册服务是一个静态代码块,也就是说值执行main方法之前就已经完成了WindowManager服务的注册。在整个应用第一次创建Context对象的时候就已经创建WindowManagerImpl对象了,所以,不管一个应用程序中有多少个Activity,但只有一个WindowManagerImpl对象。
每个Activity都会在onCreate里面调用setContextView方法来加载布局视图,而这其实就是视图View添加到Activity窗口上的一个过程。加载布局视图代码如下Activity#setContentView:
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow#setContentView
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//给窗口安装装饰视图DecorView
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//加载xml布局视图内容到视图容器mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
调用installDecor方法给窗口安装视图装饰,所谓视图装饰指的就是界面上看到的标题栏,导航栏Actionbar,也就是窗口的标题栏。
installDecor
private void installDecor() {
if (mDecor == null) {
//
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
........
}
该方法中主要做了两件事:
generateDecor()
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
generateLayout()
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource; //窗口修饰布局文件
if() {
layoutResource = com.android.internal.R.layout.dialog_title_icons;
} else if() {
layoutResource = com.android.internal.R.layout.screen_title_icons;
} else if() {
layoutResource = com.android.internal.R.layout.screen_progress;
} else if() {
}
....
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}
窗口布局文件,R.layout.screen_title
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
全屏的窗口布局文件 R.layout.screen_simple:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/content"
android:fitsSystemWindows="true"
android:foregroundInsidePadding="false"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
然后,回过头来,在installDecor里面调用inflate方法将布局视图内容加载到刚刚获得的内容容器mContentParent上。
现在总结一下他们之间的关系:一个Activity中有一个Window对象用于描述当前应用的窗口,而Window是抽象类,实际上指向PhoneWindow类。PhoneWindow类中有一个内部类DecorView用于描述窗口的顶层视图,该类实际上是ViewGroup类型。它里面有一个@android:id/content指向的mContentParent,也是一个ViewGroup,我们setContextView里面设置进去的xml,会成为它的子 view。
我们知道Activity中的PhoneWindow对象帮我们创建了一个PhoneWindow内部类DecorView(父类为FrameLayout)窗口顶层视图,然后通过LayoutInflater将xml内容布局解析成View树形结构添加到DecorView顶层视图中id为content的FrameLayout父容器上面。到此,我们已经知道Activity的content内容布局最终会添加到DecorView窗口顶层视图上面
那么,窗口顶层视图DecorView是怎么绘制到我们的手机屏幕上的呢?
上面我们说到
handleLaunchActivity里面会调用onCreate,onStart,onRestoreInstanceState,onResume;onCreate里面又回调用setContextView
所以接下来,我们看下onResume的调用。
我们上面说过,在handleLaunchActivity里面,performLaunchActivity之后,会调用handleResumeActivity:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume){
//调用activity.onResume,把activity数据记录更新到ActivityClientRecord
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
//activity.mStartedActivity是用来标记启动Activity,有没有带返回值,一般我们startActivity(intent)是否默认是startActivityForResult(intent,-1),默认值是-1,所以这里mStartedActivity = false
boolean willBeVisible = !a.mStartedActivity;
...
//mFinished标记Activity有没有结束,而r.window一开始activity并未赋值给ActivityClientRecord,所以这里为null
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;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//把当前的DecorView与WindowManager绑定一起
wm.addView(decor, l);
}
...
performResumeActivity注释已经很清楚了,里面会回调onResume方法,这里就不详细说明了。一个 Activity 界面的绘制,其实是在 onResume() 之后才开始的。
我们主要看这句:
wm.addView(decor, l);
很明显,这句就是把Activity的顶层视图DecorView添加到窗口视图上,我们来看addView方法
下面是WindowManagerImpl的源码
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
private IBinder mDefaultToken;
public WindowManagerImpl(Display display) {
this(display, null);
}
private WindowManagerImpl(Display display, Window parentWindow) {
mDisplay = display;
mParentWindow = parentWindow;
}
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mDisplay, parentWindow);
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
@Override
public Display getDefaultDisplay() {
return mDisplay;
}
}
我们发现,其实都是调用了WindowManagerGlobal类相对应的方法:
public final class WindowManagerGlobal {
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 static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
........
//窗口的添加过程
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
final Context context = view.getContext();
if (context != null
&& context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
if (mSystemPropertyUpdater == null) {
mSystemPropertyUpdater = new Runnable() {
@Override public void run() {
synchronized (mLock) {
for (int i = mRoots.size() - 1; i >= 0; --i) {
mRoots.get(i).loadSystemProperties();
}
}
}
};
}
//不能重复添加窗口
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
}
//判断当前窗口是否为子窗口,如果是则获得其父窗口并保存在panelParentView变量中
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
//每一个窗口对应着一个ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
//给当前窗口视图设置参数
view.setLayoutParams(wparams);
//保存三个数组
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
try {
//真正执行窗口的视图View绘制工作的方法
root.setView(view, wparams, panelParentView);
}
}
//主要更新当前窗口的参数LayoutParams
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
view.setLayoutParams(wparams);
synchronized (mLock) {
int index = findViewLocked(view, true);
ViewRootImpl root = mRoots.get(index);
mParams.remove(index);
mParams.add(index, wparams);
root.setLayoutParams(wparams, false);
}
}
//从三个数组里面分别移除DecorView对象,ViewRootIpl对象,WindowManager.LayoutParams对象
public void removeView(View view, boolean immediate) {
synchronized (mLock) {
int index = findViewLocked(view, true);
View curView = mRoots.get(index).getView();
removeViewLocked(index, immediate);
if (curView == view) {
return;
}
}
}
在WindowManagerGlobal类中维系着三个数组分别是:
- mViews:保存着当前应用所有窗口的顶层视图DecorView对象
- mRoots:保存着当前应用所有窗口的视图绘制类ViewRootImpl
- mParams:保存着当前应用所有窗口的参数 WindowManager.LayoutParams
另外,上面我们说过:
一个应用中不管有多少个Activity,都共用一个WindowManagerImpl对象,
因此:
而在WindowManagerImpl类中是单例模式获得WindowManagerGlobal对象,因此:一个应用中也就只有一个WindowManagerGlobal对象了。而在该对象中通过维护以上三个数组来维护一个应用中所有窗口的管理。
我们来看下addView方法:
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {
...
ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
...
}
这个过程创建一个ViewRootImpl,并将之前创建的DecoView作为参数传入,以后DecoView的事件都由ViewRootImpl来管理了,比如DecoView上添加View,删除View。
ViewRootImpl实现了ViewParent这个接口,这个接口最常见的一个方法是requestLayout()。
ViewRootImpl是个ViewParent,在DecoView添加的View时,就会将View中的ViewParent设为DecoView所在的ViewRootImpl,View的ViewParent相同时,理解为这些View在一个View链上。
所以每当调用View的requestLayout()时,其实是调用到ViewRootImpl,ViewRootImpl会控制整个事件的流程。可以看出一个ViewRootImpl对添加到DecoView的所有View进行事件管理。
我们接着看ViewRootImpl的setView方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
...
try {
...
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(),mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mInputChannel);
}
}。
这时候,我们在看下这种图,就会很清晰了
我们在看下requestLayout()这个方法
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
checkThread判断线程一致行的,注意其实并不是判断是否时UI线程
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
这里就涉及到一个问题,子线程是否可以更新UI,具体情况看这里 子线程是否可以更新UI
我们主要来看scheduleTraversals()
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
..............
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
...............
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
try {
performTraversals();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
}
............
跟踪代码,最后DecorView的绘制会进入到ViewRootImpl类中的performTraversals()成员方法:
private void performTraversals() {
// cache mView since it is used so much below...
//我们在Step3知道,mView就是DecorView根布局
final View host = mView;
//在Step3 成员变量mAdded赋值为true,因此条件不成立
if (host == null || !mAdded)
return;
//是否正在遍历
mIsInTraversal = true;
//是否马上绘制View
mWillDrawSoon = true;
.............
//顶层视图DecorView所需要窗口的宽度和高度
int desiredWindowWidth;
int desiredWindowHeight;
.....................
//在构造方法中mFirst已经设置为true,表示是否是第一次绘制DecorView
if (mFirst) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
//如果窗口的类型是有状态栏的,那么顶层视图DecorView所需要窗口的宽度和高度就是除了状态栏
if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
|| lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {//否则顶层视图DecorView所需要窗口的宽度和高度就是整个屏幕的宽高
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
}
............
//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
//执行测量操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
........................
//执行布局操作
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
.......................
//回调OnGlobalLayoutListener,在meausre、layout之后,draw之前
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
.......................
//执行绘制操作
performDraw();
}
performTraversals方法会经过measure、layout和draw三个过程才能将一个View绘制出来,所以View的绘制是ViewRootImpl完成的,另外当手动调用invalidate,postInvalidate,requestInvalidate也会最终调用performTraversals,来重新绘制View。
这里面还有一个问题,我们通过上面的分析可以知道,在onCreate和onResume里面调用getMeasureHeight() = 0是没有问题,平时我们获取的时候,可以使用监听:
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
}
});
上面的代码已经很清楚了:
//执行测量操作
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
........................
//执行布局操作
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
.......................
//回调OnGlobalLayoutListener,在meausre、layout之后,draw之前
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
.......................
//执行绘制操作
performDraw();
OnGlobalLayoutListener,是在meausre、layout之后,draw之前回调的。
- ActivityThread.performLaunchActivity()
- Activity.attach()
- Activity.onCreate()
- Activity.setContentView()
- PhoneWindow.setContentView()
- PhoneWindow.installDecor()
- PhoneWindow.generateDecor():创建DecorView对象,继承FrameLayout
- PhoneWindow.generateLayout():根据不通的窗口风格,选择不通的layout文件,并添加到DecorView,返回id为content的FrameLayout,作为
- ActivityThread.performResumeActivity()