Android Framework 源码之旅 —— WindowManagerService初窥

前言

  • Android Framework 源码之旅 —— Activity启动流程
  • Android Framework 源码之旅 —— 进程的启动

应用始终是用来和用户进行交互的,而呈现到用户眼前的就是一个个页面窗体,很早以前,就想知道,这些窗体是怎么来的,做应用层的时候,总会无意间看到什么PhoneWindowDecorView之类的陌生类,但从名称上却又惹人遐想
随着源码的研究,甚至想弄清楚窗体是怎么显示到屏幕上的,随着源码的研究和资料的查阅,顿时有点怀疑人生了,这其中设计到的知识面已经超过了现阶段的认知,饭还是得一口一口的吃,现阶段只需要研究Framework层的代码就够了

应用启动窗口添加

直接模拟器断点调试,首先点击一个应用图标,而后就断点到了ActivityStack#startActivityLocked

package com.android.server.am;
......
class ActivityStack<T extends StackWindowController> extends ConfigurationContainer
        implements StackWindowListener {
	......
    void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
                             boolean newTask, boolean keepCurTransition, ActivityOptions options) {
		......
                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
            }
        } else {
            // If this is the first activity, don't do any fancy animations,
            // because there is nothing for it to animate on top of.
            ActivityOptions.abort(options);
        }
	}
	......
}

断点到这里会调用到ActivityRecord#showStartingWindow

package com.android.server.am;
......
final class ActivityRecord extends ConfigurationContainer implements AppWindowContainerListener {
	......
    AppWindowContainerController mWindowContainerController;
	......
    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
        showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */);
    }

    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch,
            boolean fromRecents) {
        if (mWindowContainerController == null) {
            return;
        }
        if (mTaskOverlay) {
            // We don't show starting window for overlay activities.
            return;
        }

        final CompatibilityInfo compatInfo =
                service.compatibilityInfoForPackageLocked(info.applicationInfo);
        final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
                compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(),
                allowTaskSnapshot(),
                mState.ordinal() >= RESUMED.ordinal() && mState.ordinal() <= STOPPED.ordinal(),
                fromRecents);
        if (shown) {
            mStartingWindowState = STARTING_WINDOW_SHOWN;
        }
    }
	......
}

这里面就跳转到了AppWindowContainerController#addStartingWindow

package com.android.server.wm;
......
public class AppWindowContainerController
        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
	......
    public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
            CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning,
            boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) {
	......
            mContainer.startingData = new SplashScreenStartingData(mService, pkg, theme,
                    compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
                    mContainer.getMergedOverrideConfiguration());
            scheduleAddStartingWindow();
        }
        return true;
	}
	......
}

这里的mContainer是一个AppWindowToken对象

package com.android.server.wm;
......
public class AppWindowContainerController
        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
    final WindowManagerService mService;
	......
    void scheduleAddStartingWindow() {
        // Note: we really want to do sendMessageAtFrontOfQueue() because we
        // want to process the message ASAP, before any other queued
        // messages.
        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
        }
    }
	......
}

代码很好理解,就是将一个任务添加到Handler队列之首,也可以理解为立即执行,mAddStartingWindow是一个Runnable对象

package com.android.server.wm;
......
public class AppWindowContainerController
        extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
	......
    private final Runnable mAddStartingWindow = new Runnable() {
        @Override
        public void run() {
            final StartingData startingData;
            final AppWindowToken container;

            synchronized (mWindowMap) {
                if (mContainer == null) {
                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
                            + " add starting window");
                    return;
                }

                // There can only be one adding request, silly caller!
                mService.mAnimationHandler.removeCallbacks(this);

                startingData = mContainer.startingData;
                container = mContainer;
            }

            if (startingData == null) {
                // Animation has been canceled... do nothing.
                if (DEBUG_STARTING_WINDOW)
                    Slog.v(TAG_WM, "startingData was nulled out before handling"
                            + " mAddStartingWindow: " + mContainer);
                return;
            }

            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
                    + AppWindowContainerController.this + ": startingData="
                    + container.startingData);

            StartingSurface surface = null;
            try {
                surface = startingData.createStartingSurface(container);
            } catch (Exception e) {
                Slog.w(TAG_WM, "Exception when adding starting window", e);
            }
            ......
        }
    };
	......
}

重点代码surface = startingData.createStartingSurface(container);,也就是执行到SplashScreenStartingData#createStartingSurface

package com.android.server.wm;
......
class SplashScreenStartingData extends StartingData {
    ......
    @Override
    StartingSurface createStartingSurface(AppWindowToken atoken) {
        return mService.mPolicy.addSplashScreen(atoken.token, mPkg, mTheme, mCompatInfo,
                mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
                mMergedOverrideConfiguration, atoken.getDisplayContent().getDisplayId());
    }
}

mService.mPolicy是一个PhoneWindowManager对象

package com.android.server.policy;
......
public class PhoneWindowManager implements WindowManagerPolicy {
	......
    @Override
    public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
                                           CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
                                           int logo, int windowFlags, Configuration overrideConfig, int displayId) {
		......
        WindowManager wm = null;
        View view = null;

        try {
        	......
            final PhoneWindow win = new PhoneWindow(context);
            win.setIsStartingWindow(true);
            ......
            wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
            view = win.getDecorView();

            if (DEBUG_SPLASH_SCREEN) Slog.d(TAG, "Adding splash screen window for "
                    + packageName + " / " + appToken + ": " + (view.getParent() != null ? view : null));

            wm.addView(view, params);

            // Only return the view if it was successfully added to the
            // window manager... which we can tell by it having a parent.
            return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
        } catch (WindowManager.BadTokenException e) {
            // ignore
            Log.w(TAG, appToken + " already running, starting window not displayed. " +
                    e.getMessage());
        } catch (RuntimeException e) {
            // don't crash if something else bad happens, for example a
            // failure loading resources because we are loading from an app
            // on external storage that has been unmounted.
            Log.w(TAG, appToken + " failed creating starting window", e);
        } finally {
            if (view != null && view.getParent() == null) {
                Log.w(TAG, "view not successfully added to wm, removing view");
                wm.removeViewImmediate(view);
            }
        }

        return null;
	}
	......
}

重点在于wm.addView(view, params);这一行代码,其中的view参数是一个DecorView对象,以应用层的知识,尽管对它有点陌生,但还是有些眼熟,以目前的认知,到了这一步,就算是添加了一个窗口了,就没必要再过度深究了,毕竟之前深究的后果就是怀疑人生

窗体添加

上面的流程中,最终调用到了addView,接下来就分析这个添加窗体的动作是怎么运作的,不过在此之前,先看下上面的wm是怎么来的,首先从wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);入手,而context是一个ContextImpl对象

package android.app;
......
class ContextImpl extends Context {
	......
    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }
    ......
}
package android.app;
......
final class SystemServiceRegistry {
	......
    private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS =
            new HashMap<String, ServiceFetcher<?>>();
	......
	static {
		......
        registerService(Context.WINDOW_SERVICE, WindowManager.class,
                new CachedServiceFetcher<WindowManager>() {
                    @Override
                    public WindowManager createService(ContextImpl ctx) {
                        return new WindowManagerImpl(ctx);
                    }
                });
		......
	}
	......
    public static Object getSystemService(ContextImpl ctx, String name) {
        ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        return fetcher != null ? fetcher.getService(ctx) : null;
    }
    ......
}

可见这里的wm是一个WindowManagerImpl对象,回归正题

package android.view;
......
public final class WindowManagerImpl implements WindowManager {
    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);
    }
    ......
}

这里有两个陌生的类:WindowManagerWindowManagerGlobal,其中的WindowManagerGlobal采用了单例模式

package android.view;
......
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
	......
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
		......
	}
}

可见WindowManager是一个接口,里面定义了一大堆的重要参数,至于其中更多细节,目前暂不深究,先看看其继承的ViewManager

package android.view;

/**
 * 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.
     * 

Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. *

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); }

从接口名可以大胆猜测其就是用来实现添加/移除/更新窗体的
WindowManager也算是粗略认识了,接下来就该看看WindowManagerGlobal

package android.view;
......
public final class WindowManagerGlobal {
	......
    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;
        ......
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
        	......
            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 {
                root.setView(view, wparams, panelParentView);
            } catch (RuntimeException e) {
                // BadTokenException or InvalidDisplayException, clean up.
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
                throw e;
            }
		}
	}
	......
}
package android.view;
......
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
	......
    final IWindowSession mWindowSession;
	......
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ......
                requestLayout();
                ......
                try {
                    ......
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
                } catch (RemoteException e) {
                    ......
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
                ......
                view.assignParent(this);
                ......
            }
        }
    }
    ......
}

上面的requestLayout留作后续课题,而mWindowSession是一个Session对象

package android.view;
......
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
	......
    final IWindowSession mWindowSession;
    ......
    public ViewRootImpl(Context context, Display display) {
        mContext = context;
        mWindowSession = WindowManagerGlobal.getWindowSession();
        ......
	}
	......
}
package android.view;
......
public final class WindowManagerGlobal {
	......
    private static IWindowManager sWindowManagerService;
    private static IWindowSession sWindowSession;
	......
    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;
        }
    }

    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    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;
        }
    }
    ......
}

显然,windowManager就是WindowManagerService

package com.android.server.wm;
......
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
	......
    @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;
    }
    ......
}

看吧,返回的就是一个Session实例,回到主题mWindowSession.addToDisplay

package com.android.server.wm;
......
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
    final WindowManagerService mService;
	......
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
                            int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,
                            Rect outStableInsets, Rect outOutsets,
                            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,
                outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
    }
    ......
}
package com.android.server.wm;
......
public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
	......
    public int addWindow(Session session, IWindow client, int seq,
                         LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
                         Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
                         DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel) {
		......
        synchronized (mWindowMap) {
        	......
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId);
            ......
            AppWindowToken atoken = null;
            final boolean hasParent = parentWindow != null;
            // Use existing parent window token for child windows since they go in the same token
            // as there parent window so we can apply the same policy on them.
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
        	......
            final WindowState win = new WindowState(this, session, client, token, parentWindow,
                    appOp[0], seq, attrs, viewVisibility, session.mUid,
                    session.mCanAddInternalSystemWindow);
			......
            win.attach();
            ......
		}
		......
	}
}

从函数名看,以为是这里面做添加窗体操作,其实一看源码,大致就是做一些成员的初始化以及关联的操作而已,算是为添加窗体做准备

尾声

这篇算是通过添加窗体初步认识了WindowManagerService,但添加窗体后怎么显示到屏幕上的,这个作为后续课题吧,而至于一些没介绍的重要成员,比如WindowState等,深入研究WMS时,再做记录

你可能感兴趣的:(Android,Framework)