之前在分析Activity的时候有很多和WMS相关的,我们这里再简单总结下和WMS相关的那部分。
在博客http://blog.csdn.net/kc58236582/article/details/52413871中在APPWindowToken创建过程这节中,我们分析到在AMS调用startActivityLocked的时候,会调用WMS的addAppToken来创建一个APPWindowToken。
final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) { TaskRecord rTask = r.task; final int taskId = rTask.taskId; // mLaunchTaskBehind tasks get placed at the back of the task stack. if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) { // Last activity in task had been removed or ActivityManagerService is reusing task. // Insert or replace. // Might not even be in. insertTaskAtTop(rTask, r); mWindowManager.moveTaskToTop(taskId); } TaskRecord task = null; if (!newTask) { // If starting in an existing task, find where that is... boolean startIt = true; for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { task = mTaskHistory.get(taskNdx); if (task.getTopActivity() == null) { // All activities in task are finishing. continue; } if (task == r.task) { // Here it is! Now, if this is not yet visible to the // user, then just add it without starting; it will // get started when the user navigates back to it. if (!startIt) { if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task " + task, new RuntimeException("here").fillInStackTrace()); task.addActivityToTop(r); r.putInHistory(); mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind);我们再来看看WMS的addAppToken函数,在WMS中创建了APPWindowToken然后保存在mTokenMap中。
public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId, int requestedOrientation, boolean fullscreen, boolean showForAllUsers, int userId, int configChanges, boolean voiceInteraction, boolean launchTaskBehind) { ...... synchronized(mWindowMap) { AppWindowToken atoken = findAppWindowToken(token.asBinder());//如果已经有了直接退出 if (atoken != null) { Slog.w(TAG, "Attempted to add existing app token: " + token); return; } atoken = new AppWindowToken(this, token, voiceInteraction);//新建一个APPWindowToken atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos; atoken.appFullscreen = fullscreen; atoken.showForAllUsers = showForAllUsers; atoken.requestedOrientation = requestedOrientation; atoken.layoutConfigChanges = (configChanges & (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0; atoken.mLaunchTaskBehind = launchTaskBehind; if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken + " to stack=" + stackId + " task=" + taskId + " at " + addPos); Task task = mTaskIdToTask.get(taskId); if (task == null) { task = createTaskLocked(taskId, stackId, userId, atoken); } task.addAppToken(addPos, atoken); mTokenMap.put(token.asBinder(), atoken);//保存在mTokenMap中 token为key(Activity的binder对象) // Application tokens start out hidden. atoken.hidden = true; atoken.hiddenRequested = true; } }
再来看看AppWindowToken的构造函数,它是WindowToken的子类,再调用父类构造函数的时候说明了自己是TYPE_APPLICATION类型的。参数_token指向的是一个ActivityRecord对象的IBinder接口,因此,AppWindowToken类的成员变量appToken描述的就是一个ActivityRecord对象。
AppWindowToken(WindowManagerService _service, IApplicationToken _token, boolean _voiceInteraction) { super(_service, _token.asBinder(), WindowManager.LayoutParams.TYPE_APPLICATION, true); appWindowToken = this; appToken = _token; voiceInteraction = _voiceInteraction; mInputApplicationHandle = new InputApplicationHandle(this); mAnimator = service.mAnimator; mAppAnimator = new AppWindowAnimator(this); }
首先在博客http://blog.csdn.net/kc58236582/article/details/52397657中主要分析了Activity的创建 初始化。
handleLaunchActivity函数先调用performLaunchActivity函数,再调用handleResumeActivity函数。
在performLaunchActivity函数先新建Activity,然后调用其attach函数,最后调用了Activity的onCreate函数。
在Activity的attach函数中,主要代码如下:
...... mWindow = new PhoneWindow(this);//新建PhoneWindow对象 mWindow.setCallback(this);//这window中设置回调,在按键事件分发的时候中用到。如果有这个回调,就调用Activity的dispatchKeyEvent mWindow.setOnWindowDismissedCallback(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();
我们继续分析,在http://blog.csdn.net/kc58236582/article/details/52411791博客,我们分析到了ViewRootImpl和DecorView的创建。
一般在Activity的onCreate函数中会调用setContentView。而在setContentView会创建DecorView对象。
下面我们再从ActivityThread的handleResumeActivity函数看,先调用了performResumeActivity函数来查找这个Activity,后面主要调用了WindowManager的addView函数。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume) { // If we are getting ready to gc after going to the background, well // we are back active so skip it. unscheduleGcIdler(); mSomeActivitiesChanged = true; // TODO Push resumeArgs into the activity for consideration ActivityClientRecord r = performResumeActivity(token, clearHide); if (r != null) { final Activity a = r.activity; if (localLOGV) Slog.v( TAG, "Resume " + r + " started activity: " + a.mStartedActivity + ", hideForNow: " + r.hideForNow + ", finished: " + a.mFinished); final int forwardBit = isForward ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // If the window hasn't yet been added to the window manager, // and this guy didn't finish itself or start another activity, // then go ahead and add the window. boolean willBeVisible = !a.mStartedActivity; if (!willBeVisible) { try { willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible( a.getActivityToken()); } catch (RemoteException e) { } } 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; wm.addView(decor, l); }
我们来看WindowManagerImpl的addView函数,其实就是调用了WindowManagerGlobal的addView函数
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mDisplay, mParentWindow); }
之前也分析过WindowManagerGlobal,它有3个重要的成员变量:
private final ArrayList<View> mViews = new ArrayList<View>();//所有的DecorView对象 private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();//所有的ViewRootImpl对象 private final ArrayList<WindowManager.LayoutParams> mParams =//所有顶层View的layout参数 new ArrayList<WindowManager.LayoutParams>();
们再来看WindowManagerGlobal的addView函数,这个函数先是查找是否已经在WindowManagerGlobal中已经有这个view,如果有的话就调用其ViewRootImpl的doDie函数中主要是调用WindowManagerGlobal函数去除这个ViewRootImpl对象,在这个主要是创建了ViewRootImpl,并且把DecorView,RootViewRootImpl,layout参数都保存起来了。然后调用了ViewRootImpl的setView函数。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { ...... int index = findViewLocked(view, false);//查找是否有该view,获取其index 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();//调用ViewRootImpl的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. } ...... root = new ViewRootImpl(view.getContext(), display);//新建ViewRootImpl对象 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 { root.setView(view, wparams, panelParentView);//调用ViewRootImpl的setView函数 } catch (RuntimeException e) { // BadTokenException or InvalidDisplayException, clean up. synchronized (mLock) { final int index = findViewLocked(view, false); if (index >= 0) { removeViewLocked(index, true); } } throw e; } }
上面在创建完ViewRootImpl之后会调用其setView函数,setView函数主要可以分成两个部分。
在setView中先会调用requestLayout函数,这个函数在http://blog.csdn.net/kc58236582/article/details/52421683博客中有详细分析,主要调用了WMS的relayoutWindow函数,然后通过SurfaceFlinger来创建一个Surface
另一部分就是如下代码,最终会调用WMS的addWindow函数,增加窗口。在http://blog.csdn.net/kc58236582/article/details/52413871博客中WindowState的创建过程那节中有详细分析。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mOutsets, mInputChannel);