上一篇文章由setContentView()方法引起的思考看到了setContentView()对安卓源码的一些思考,也揭示了Window,PhoneWindow,DecorView他们几个的关系。但我们从上篇文章里留下了一些疑问WindowManager,ViewRoot,ViewRootImpl,PhoneWindow,WindowManagerService他们究竟是什么,他们之间有什么样的关系呢?看了网上很多文章,终于对他们有所了解,下面我就从Activity启动一步一步分析到底我们看到的View是怎么创建出来的。
关于Activity的启动流程相当复杂,我还没时间去深究,但是从网上资料看出,Activity的启动最后会到ActivityThread类的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,
...
}
}
一来就是就有WindowManagerGlobal这个没见过的对象,但从名字来看应该跟WindowManager有一定的关系。这里先不管,往下看performLaunchActivity(r, customIntent)方法:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
try { //Activity通过ClassLoader创建出来
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} ...
try {
//创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
if (activity != null) {
//创建Activity所需的Context
Context appContext = createBaseContextForActivity(r, activity);
...
//将Context与Activity进行绑定
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);
...
//调用activity.oncreate
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
...
//调用Activity的onstart方法
activity.performStart();
//调用activitu的OnRestoreInstanceState方法进行Window数据恢复
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
...
return activity;
}
我们先看到mInstrumentation.newActivity(cl,component.getClassName(), r.intent)方法
public Activity newActivity(ClassLoader cl, String className,Intent intent)throws InstantiationException,IllegalAccessException,ClassNotFoundException {
return (Activity)cl.loadClass(className).newInstance();
}
是通过ClassLoader把activity创建出来,接着通过makeApplication(false, mInstrumentation)方法创建Application,接下来我们看看我们熟悉的attach()方法:
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) {
//ContextImpl的绑定
attachBaseContext(context);
//在当前Activity创建Window
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
//为Window设置WindowManager
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());
}
//创建完后通过getWindowManager就可以得到WindowManager实例
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
我们可以看到一个我们想要看到的东西出来了PhoneWindow和WindowManager,首先这里创建了一个PhoneWindow对象,然后通过setWindowManager创建出一个WindowManager对象,然后将他们绑定在一起。然后我们点进去看看WindowManager:
突然感觉到又撞板了,WindowManager只是一个接口,并不是具体的实体类。那我们只好看看setWindowManager方法里面究竟怎么操作的:
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
好吧,这下放心了,WindowManagerImpl就是WindowManager的实现类。
接下来会到performLaunchActivity():
mInstrumentation.callActivityOnCreate(activity, r.state ...);
该方法则是调用activity.oncreate方法的
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
final void performCreate(Bundle icicle) {
onCreate(icicle);
mActivityTransitionState.readState(icicle);
performCreateCommon();
}
接下来就是onstart()
activity.performStart();
接下来是我们熟悉的:
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,r.persistentState);
public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,PersistableBundle persistentState) {
activity.performRestoreInstanceState(savedInstanceState, persistentState);
}
里面通过Bundle来保存恢复Window窗口信息的,接下来回到handleLaunchActivity()方法:
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//上面分析完了
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
...
}
}
奇怪,怎么过了onStart()方法都还没有出现View的绘制的三大流程呢,按我以前的理解。别着急,好戏在后头,我们看看handleResumeActivity()方法的实现吧:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
//获取DecorView
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 (r.mPreserveWindow) {
...
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//把当前的DecorView与WindowManager绑定一起
wm.addView(decor, l);
}
}
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.newConfig != null) {
//然后调用这个方法回调,表示屏幕参数发生了改变
performConfigurationChangedForActivity(r, r.newConfig);
...
r.newConfig = null;
}
r.activity.mVisibleFromServer = true;
mNumVisibleActivities++;
if (r.activity.mVisibleFromClient) {
//由于前面设置了INVASIBLE,所以现在要把DecorView显示出来了
r.activity.makeVisible();
}
}
// Tell the activity manager we have resumed.
if (reallyResume) {
try {
//通知ActivityManagerService,Activity完成Resumed ActivityManager.getService().activityResumed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
}
handleResumeActivity方法一开始就调用了activity = performResumeActivity()方法,但看过里面的实现,并没有我们想要的东西,所以代码就不贴出来的。重点在下面,
(1)通过r.window.getDecorView()方法获取了DecorView;
(2)然后把DecorView设置为INVISIBLE;
(3)然后获取ViewManager
(4)通过addView的方法把DecorView绑定到ViewManager上;
(5)然后调用这个方法回调,表示屏幕参数发生了改变;
(6)通过makeVisible()的方法,把之前INVISIBLE的DecorView显示出来;
(7)通知ActivityManagerService,Activity完成Resumed。
我们看到handleResumeActivity()方法做了很多事,我们最关注的是第(4)点。看到这个,突然间灵光一现,记起来上篇文章摘录艺术探索的时候有一句话:
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带
到这里我们都没看到ViewRoot的身影,只在上面看到了ViewRootImpl,既然他们之间有什么关系,那么我们就看看wm.addView(decor, l)这个方法里面弄个明白吧。
好吧,点下去是一个ViewManager,ViewManager有addView(),updateViewLayout(),removeView()三个方法,而且我们知道WindowManager是继承ViewManger的接口,而且WindowManager的WindowManagerImpl实现类是WindowManagerImpl,所以下面我们去看看WindowManagerImpl的addView是怎么实现的:
mGlobal对象是不是有点熟悉,没错,他就是文章开头写的WindowManagerGlobal类。我们看看WindowManagerImpl类,发现addView(),updateViewLayout(),removeView()三个方法的实现都不是他自己完成的,而是交由WindowManagerGlobal去实现。那么接下来我们就看看WindowManagerGlobal的addView()方法又是怎么样的:
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//ViewRootImpl开始绘制view
root.setView(view, wparams, panelParentView);
...
}
噢,我们终于见到ViewRootImpl的身影了,我们查资料发现,原来ViewRoot其实就是ViewRoot,那么我们总算找到我们想要的东西了。我们看看root.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);
}
}
在setView方法中,
首先会调用到requestLayout(),表示添加Window之前先完成第一次layout布局过程。requestLayout最终会调用performTraversals方法来完成View的绘制。
@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.");
}
}
这个方法其实是判断你是否在子线程更新UI线程的,所以说当你在onCreate()方法:
你们觉得会报错吗,答案是不会的,因为判断你是否在子线程更新UI线程是在onResume()方法里面判断的。
接着我们往下看scheduleTraversals()方法:
void scheduleTraversals() {
if (!mTraversalScheduled) {
...
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
scheduleTraversals中会通过handler去异步调用mTraversalRunnable接口
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
...
performTraversals();
...
}
接着看performTraversals()方法:
private void performTraversals() {
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
}
......
}
看到这里大家应该都懂了,绘制的三大流程(measure,layout,draw)就是在这里。那我们得出的结果是:View的绘制是ViewRootImpl完成的,而且WindowManager的addView()方法实际上最终的实现也在ViewRootImpl里面。
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带。
那这句话的意思大家也终于明白了吧。
接着我们返回setView()方法,addToDisplay()方法会通过WindowSession最终来完成Window的添加过程。在下面的代码中mWindowSession类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是说这其实是一次IPC过程,远程调用了Session中的addToDisPlay方法。
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
这里的mService就是WindowManagerService,也就是说Window的添加请求,最终是通过WindowManagerService来添加的。这里我们终于见到WindowManagerService了。
然而还有一个问题,为什么我们在开发的时候,在onCreate方法中调用view.getMeasureHeight() = 0呢?原因很简单,那是因为activity绘制的三大流程发生在activity.handleResumeActivity()方法中。那有人继续问,那在onResume()方法调用为什么view.getMeasureHeight()还是会等于0,原因也很简单,因为ViewRootImpl绘制View是异步进行的,所以为了让我们拿到这个测量结果的数值,在viewRootImpl的performTraversals的测量布局之后添加了:
if (triggerGlobalLayoutListener) {
mAttachInfo.mRecomputeGlobalAttributes = false;
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
}
...
performDraw();
也就是我们平常用的:
view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
}
});
利用观察者模式在onCreate()方法里面设置监听,我们就能就能得到想要的测量结果了。
而View是怎么跟ViewRootImpl绑定的呢?
这篇文章讲到很详细:
Android窗口机制(四)ViewRootImpl与View和WindowManager
在ViewRootImpl的构造方法中:
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession();
...
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
...
}
AttachInfo(IWindowSession session, IWindow window, Display display,ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
mSession = session;
mWindow = window;
mWindowToken = window.asBinder();
mDisplay = display;
mViewRootImpl = viewRootImpl;
mHandler = handler;
mRootCallbacks = effectPlayer;
}
而ViewRootImpl是在WindowManagerGlobal的addView()中创建的:
root = new ViewRootImpl(view.getContext(), display);
这就很清楚了吧。下面小弟画了个图总结了一下,如果有错的话请大家来纠正一下,谢谢~
参考文献:
《Android开发艺术探索》
Android窗口机制(四)ViewRootImpl与View和WindowManager
我的掘金:
https://juejin.im/user/594e8e9a5188250d7b4cd875/posts
我的:
https://www.jianshu.com/u/b538ca57f640