Activity窗口机制源码解析下(8.0)
这个小节我们接着ActivityThread 类中的handleLaunchActivity方法继续分析,接下来就到了3 处的handleResumeActivity方法,我们跟进去看下(为方便分析,代码有所删减):
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
//1.完成 Activity onResume生命周期函数回调
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
//获取到当前activity对象
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
//获取到当前activity对应的phoneWindow对象,并赋值给r.window
r.window = r.activity.getWindow();
//获取到当前activity对应的decorview对象
View decor = r.window.getDecorView();
//将decorview暂时设置为不可见,注:decorview本质为FrameLayout
decor.setVisibility(View.INVISIBLE);
//获得ViewManager实例,实质为WindowManagerImpl对象
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) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//2.调用WindowManagerImpl对象的addView方法,将decorview与Window进行绑定,底层通过IPC机制
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);
}
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(
TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
if (r.activity.mVisibleFromClient) {
//3.将decorview设置为可见
r.activity.makeVisible();
}
}
...
}
}
上述代码中重要的部分已经做了标注,1 处 performResumeActivity方法,我们在分析Activity启动流程的时候有讲过,该方法中完成了 Activity onResume生命周期函数的回调,在这里就不赘述了。我们看下 2 处 wm.addView方法,wm实质为WindowManagerImpl对象,所以就来到了WindowManagerImpl的addView方法,我们跟进去看下:
#WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
可以看到,WindowManagerImpl的addView方法直接调用了mGlobal.addView方法。mGlobal是什么呢?我们看下它的定义:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
可以看到mGlobal实质为WindowManagerGlobal实例对象,同时我们也看到mGlobal是通过单例的模式来进行获取的。我们跟进去WindowManagerGlobal的addView方法去看下:
#WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//1.调用到findViewLocked方法,判断当前decorview是否已经被添加过
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
//2.创建ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
//3.调用 ViewRootImpl 对象的 setView方法
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
注意在该方法中参数 view 为当前decorview对象,parentWindow 为当前 phonewindow 对象,具体细节赋值操作,在这里就不展开分析了,有兴趣的可以跟进源码看下。在分析 1 处findViewLocked方法之前,我们先来看下 WindowManagerGlobal中几个比较重要的成员变量:
//1.
private final ArrayList<View> mViews = new ArrayList<View>();
//2.
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
//3.
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
//4.
private final ArraySet<View> mDyingViews = new ArraySet<View>();
我们刚才也看到,WindowManagerGlobal对象是通过单例的方式来获取的,所以我们的应用程序中只存在一个WindowManagerGlobal单例对象。在上面的声明中,mViews存储的是所有Window所对应的View,mRoots存储的是所有ViewRootImpl实例对象,mParams存储的是所有Window所对应的布局参数,而mDyingViews中存储的是将要被删除的view对象。
好了,我们接着之前的分析,首先我们看下 1 处的 findViewLocked方法,注意 1 处调用findViewLocked方法所传的第二个参数为false:
private int findViewLocked(View view, boolean required) {
final int index = mViews.indexOf(view);
if (required && index < 0) {
throw new IllegalArgumentException("View=" + view + " not attached to window manager");
}
return index;
}
我们上面提到,mViews中存储的是所有phonewindow所对应的view对象,可以看到方法中调用到mViews.indexOf方法,用来判断当前decorview是否已经被添加过,由于我们传入的required参数值为false,所以findViewLocked接着直接将index返回掉,如果index的值大于等于0,就说明当前decorview已经被添加过,如果index的值为-1就说明当前decorview未被添加过。我们回到addView方法中可以看到,后续操作对index的值进行了判断,当index的值大于等于0时,表明当前decorview已经被添加过,接着进一步调用mDyingViews.contains(view)方法,如果该方法return true的话,会调用mRoots.get(index).doDie();方法,将已经添加的decorview移除掉,否则直接抛出异常。
好了,我们接着看下 2 处创建ViewRootImpl对象,在这里你可能会说,一个对象的创建有什么好看的,不就是一个 new 关键字吗,其实在这里不然,里面暗藏玄机,我们跟进去看下ViewRootImpl对象的构造方法就知道了:
public ViewRootImpl(Context context, Display display) {
//重点!!!
mWindowSession = WindowManagerGlobal.getWindowSession();
...
}
可以看到mWindowSession就是在构造方法中进行赋值的,我们先看下mWindowSession的类型:
final IWindowSession mWindowSession;
有没有似曾相识的感觉,对的,IWindowSession就是AIDL类型的接口,decorview被WindowManager正式添加到Window中底层是通过IPC机制来实现的,其中客户端进程对应的就是mWindowSession,而SystemServer进程中对应的Binder实现就是Session类。
好了,我们看到mWindowSession的赋值是通过调用WindowManagerGlobal.getWindowSession();方法来实现的,我们跟进去看下:
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
//1.
IWindowManager windowManager = getWindowManagerService();
//2.
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
方法中首先调用到getWindowManagerService方法,获取到IWindowManager对象,等等,IWindowManager?该不会也是一个AIDL接口吧?对的,没毛病,其实IWindowManager在这里代表Binder连接池对应的AIDL接口,SystemServer进程中对应的Binder实现类为 WindowManagerService 。我们可以看到 2 处通过调用windowManager.openSession方法来获取到sWindowSession实例对象,其实windowManager.openSession方法就涉及到了IPC操作,远程调用到WindowManagerService的openSession方法。我们先分析下 1 处IWindowManager对象的获取,可以看到它是通过调用getWindowManagerService方法来实现的,我们自然是跟进去看下:
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
实际上在这里调用getWindowManagerService方法,sWindowManagerService已经进行过赋值操作了,方法中会直接返回sWindowManagerService。在这里你可能会疑惑,sWindowManagerService的赋值操作是什么时候进行的呢?我们就来揭开神秘的面纱,上个小节的handleLaunchActivity少年你可还记得?好,那handleLaunchActivity方法中 1 处的调用代码 WindowManagerGlobal.initialize(); 你可还记得?sWindowManagerService的赋值操作就是这行代码进行操作的,怎么你不信,我们回到WindowManagerGlobal.initialize()方法中去看下:
public static void initialize() {
getWindowManagerService();
}
可以看到 initialize 方法中直接调用到getWindowManagerService方法,对 sWindowManagerService 完成了赋值操作,有没有豁然开朗柳暗花明的感觉哈哈。
好了我们接着回到getWindowSession 方法的 2 处,刚我们提到过, 2 处windowManager.openSession方法实际上远程调用到WindowManagerService的openSession方法,我们跟进去看下:
#WindowManagerService
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
可以看到WindowManagerService的openSession方法中直接 new 了一个Session对象,然后 return掉。根据我们的分析,Session为IWindowSession
AIDL接口在SystemServer进程中对应的Binder实现类,到底是不是这样子呢,我们看下Session类的定义:
public class Session extends IWindowSession.Stub
implements IBinder.DeathRecipient {
果然如此哈哈。
好了,ViewRootImpl对象的创建操作,这里我们就分析完毕了,总结一句话就是ViewRootImpl对象创建的时候,在它的构造方法中获取到了IWindowSession 实例对象,为后续decorview被WindowManager正式添加到Window中这一IPC操作做准备。我们接着往下看,3 处调用到 ViewRootImpl 对象的 setView 方法,我们跟进去看下,方法中的代码量有点大,我做了删减操作:
#ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
//1.重点,view测量绘制操作的源头就是这里
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//2.远程调用到 SystemServer进程中 Session类 的addToDisplay方法,完成decorview被WindowManager正式添加到Window操作
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
}
...
}
}
首先我们先看下 1 处的requestLayout方法,代码中标注了该方法就是view测量绘制的源头。你也许会说,view的测量绘制不是由ViewRootImpl类的 performTraversals 方法开始的吗?不知道你有没有想过 performTraversals 方法是怎么被调用的呢?其实就是由 requestLayout 方法调用到的,不信我们跟进去代码看下:
#ViewRootImpl
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
可以看到 requestLayout 方法中首先调用到 checkThread方法,根据方法名字我们就可以猜到,这个方法是用来检验当前线程是否为UI线程,我们跟进去看下:
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
果然如我们所料,相信方法中的异常大家在刚刚学习Android知识的时候肯定遇到过,当我们在工作线程中进行更新UI操作时,就会抛出该异常。其实我们在对控件进行UI操作时,底层都会调用到requestLayout方法,重新进行测量绘制操作。好了,我们回到requestLayout方法接着往下看,可以看到方法中调用到scheduleTraversals方法,跟进去看下:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//重点
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
scheduleTraversals方法中调用到 mChoreographer.postCallback方法,关于mChoreographer定义以及postCallback的细节由于篇幅原因这里就不深入分析了,有兴趣的可以翻阅源码看下,postCallback方法中传入的第二个参数 mTraversalRunnable 实质为 Runnable对象,我们看下它的定义:
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
所谓的异步绘制操作就是从这里开始的,还等什么,接下来肯定是看下 TraversalRunnable 的 run 方法啦:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
run方法中直接调用了doTraversal 方法,我们接着跟:
#ViewRootImpl
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
//重点!!!
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
在 doTraversal 方法中我们终于找到了 performTraversals 方法的调用。performTraversals方法中是如何进行测量绘制操作的,在这里就不展开分析了,后续笔者会专门开一小节来进行分析。
好了好了,我们回到 ViewRootImpl 的 setView 方法中接着往下看,requestLayout方法调用完毕后就来到 2 处,mWindowSession.addToDisplay方法通过IPC机制远程调用到 SystemServer进程中 Session类 的addToDisplay方法,我们跟进去看下:
#Session
@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);
}
可以看到,Session 类的 addToDisplay 方法直接调用到 mService.addWindow 方法,我们看下 mService 的定义:
final WindowManagerService mService;
mService其实就是 WindowManagerService 实例对象,所以说 Window的添加操作最终还是通过 WindowManagerService 的addWindow方法来实现的。后续代码我们就不再分析了。
Window添加完毕后,最后我们回到 handleResumeActivity 方法的 3 处,调用到 r.activity.makeVisible();方法,将decorview设置为可见,我们跟进去Activity的makeVisible方法看下:
#Activity
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
//重点
mDecor.setVisibility(View.VISIBLE);
}
很简单,Activity的 makeVisible方法中直接将 mDecor设置为VISIBLE,这样我们就能看到Activity 界面了。
到这里,Activity窗口机制源码解析这一部分就告一段落了,欢迎大家一起交流。