Android源码 windowManager

前言

嗯嗯 windowManager是用来干嘛的?
从名字上可以看出windowManger是窗口管理的意思, 主要

window

/**
 * Abstract base class for a top-level window look and behavior policy.  An
 * instance of this class should be used as the top-level view added to the
 * window manager. It provides standard UI policies such as a background,   title
 * area, default key processing, etc.
 *
 * 

The only existing implementation of this abstract class is * android.view.PhoneWindow, which you should instantiate when needing a * Window. */ public abstract class Window { /** Flag for the "options panel" feature. This is enabled by default. */ public static final int FEATURE_OPTIONS_PANEL = 0; /** Flag for the "no title" feature, turning off the title at the top * of the screen. */ public static final int FEATURE_NO_TITLE = 1; //.... }

看下类的解释 :一个顶级窗口和行为策略的抽象基类, 实例作为顶级view添加到windowManager,提供了标准UI,唯一实现类是PhoneWindow .

哪些地方用到了windowManager
windowManager什么时候创建的

  • IWindowSession mWindowSession;
image.png

Activity中的windowManager的创建
在dialog中调用

 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);


在Activity中

@Override
public Object getSystemService(@ServiceName @NonNull String name) {
    if (getBaseContext() == null) {
        throw new IllegalStateException(
                "System services not available to Activities before onCreate()");
    }

    if (WINDOW_SERVICE.equals(name)) {
        return mWindowManager;
    } else if (SEARCH_SERVICE.equals(name)) {
        ensureSearchManager();
        return mSearchManager;
    }
    return super.getSystemService(name);
}

attach时

 final void attach(...) {
     //....
    "1.创建window"
    mWindow = new PhoneWindow(this, window, activityConfigCallback);
    "2.设置window回调"
    mWindow.setWindowControllerCallback(this);
    mWindow.setCallback(this);
    mWindow.setOnWindowDismissedCallback(this);
    mWindow.getLayoutInflater().setPrivateFactory(this);
    //....
    "4.创建windowManger并关联window和windowManger"
    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();
    mCurrentConfig = config;

  //...
}

在服务中获取windowManager
下图是输入法InputMethodService创建输入法dialog的过程
softInputWindow实际就是输入法的视图区


image.png

断点继续走到dialog的创建过程


用户

此时用户获取windowManger的上下文是service

image.png
image.png
image.png

image.png

image.png

=========================================================

可以看到最终是通过了 SystemServiceRegistry 这个类获取windowManager

 "管理所有可以通过Context的getSystemService()获取的系统服务"
 /**
 * Manages all of the system services that can be returned by {@link         Context#getSystemService}.
 * Used by {@link ContextImpl}.
 * 
 */
final class SystemServiceRegistry {
private static final String TAG = "SystemServiceRegistry";

// Service registry information.
// This information is never changed once static initialization has completed.
"保存了所有的系统服务的名字"
private static final HashMap, String> SYSTEM_SERVICE_NAMES =
        new HashMap, String>();
"保存了所有的系统服务的ServiceFetcher  "
"getSystemService时 是通过服务的名字先获取ServiceFetcher ,然后通过fetcher.getService(ctx) 获取注册了的服务"
private static final HashMap> SYSTEM_SERVICE_FETCHERS =
        new HashMap>();
private static int sServiceCacheSize;

// Not instantiable.
private SystemServiceRegistry() { }

//类加载时注册所有的服务
static {
    registerService(Context.ACCESSIBILITY_SERVICE, AccessibilityManager.class,
            new CachedServiceFetcher() {
        @Override
        public AccessibilityManager createService(ContextImpl ctx) {
            return AccessibilityManager.getInstance(ctx);
        }});
    //太多服务了  这里先忽略..........
    //WINDOW_SERVICE 在这里注册的
    registerService(Context.WINDOW_SERVICE, WindowManager.class,
            new CachedServiceFetcher() {
        @Override
        public WindowManager createService(ContextImpl ctx) {
            return new WindowManagerImpl(ctx);
        }});

   //太多服务了  这里先忽略..........

}

/**
 * Creates an array which is used to cache per-Context service instances.
 */
public static Object[] createServiceCache() {
    return new Object[sServiceCacheSize];
}
 "here  通过contextImpl 和Context中的服务名(如Context.WINDOW_SERVICE) 获取服务"
"getSystemService时 是通过服务的名字先获取ServiceFetcher ,然后通过fetcher.getService(ctx) 获取注册了的服务"
/**
 * Gets a system service from a given context.
 */
public static Object getSystemService(ContextImpl ctx, String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
    return fetcher != null ? fetcher.getService(ctx) : null;
}

/**
 * Gets the name of the system-level service that is represented by the specified class.
 */
public static String getSystemServiceName(Class serviceClass) {
    return SYSTEM_SERVICE_NAMES.get(serviceClass);
}
  "类加载时调用 注册系统服务"
/**
 * Statically registers a system service with the context.
 * This method must be called during static initialization only
 */
private static  void registerService(String serviceName, Class serviceClass,
        ServiceFetcher serviceFetcher) {
    SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
    SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
"系统服务获取的接口"
/**
 * Base interface for classes that fetch services.
 * These objects must only be created during static initialization.
 */
static abstract interface ServiceFetcher {
    T getService(ContextImpl ctx);
}
"当system service需要 context时使用这个"
/**
 * Override this class when the system service constructor needs a
 * ContextImpl and should be cached and retained by that context.
 */
static abstract class CachedServiceFetcher implements ServiceFetcher {
    private final int mCacheIndex;

    public CachedServiceFetcher() {
        mCacheIndex = sServiceCacheSize++;
    }

    @Override
    @SuppressWarnings("unchecked")
    public final T getService(ContextImpl ctx) {
        final Object[] cache = ctx.mServiceCache;
        synchronized (cache) {
            // Fetch or create the service.
            Object service = cache[mCacheIndex];
            "如果服务为空通过 ContextImpl create 并保存到缓存中"
            if (service == null) {
                try {
                    service = createService(ctx);
                    "把ServiceFetcher create的服务加到缓存中,下次可以直接从缓存中获取"
                    cache[mCacheIndex] = service;
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return (T)service;
        }
    }

    public abstract T createService(ContextImpl ctx) throws ServiceNotFoundException;
}
   "当system service不需要 context时使用这个"
/**
 * Override this class when the system service does not need a ContextImpl
 * and should be cached and retained process-wide.
 */
static abstract class StaticServiceFetcher implements ServiceFetcher {
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl ctx) {
        synchronized (StaticServiceFetcher.this) {
            if (mCachedInstance == null) {
                try {
                    mCachedInstance = createService();
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return mCachedInstance;
        }
    }

    public abstract T createService() throws ServiceNotFoundException;
}
"每个进程只有一个实例的system service使用这个StaticApplicationContextServiceFetcher注册   ==>只有ConnectivityManager用到  "
/**
 * Like StaticServiceFetcher, creates only one instance of the service per application, but when
 * creating the service for the first time, passes it the application context of the creating
 * application.
 *
 * TODO: Delete this once its only user (ConnectivityManager) is known to work well in the
 * case where multiple application components each have their own ConnectivityManager object.
 */
static abstract class StaticApplicationContextServiceFetcher implements ServiceFetcher {
    private T mCachedInstance;

    @Override
    public final T getService(ContextImpl ctx) {
        synchronized (StaticApplicationContextServiceFetcher.this) {
            if (mCachedInstance == null) {
                Context appContext = ctx.getApplicationContext();
                // If the application context is null, we're either in the system process or
                // it's the application context very early in app initialization. In both these
                // cases, the passed-in ContextImpl will not be freed, so it's safe to pass it
                // to the service. http://b/27532714 .
                try {
                    mCachedInstance = createService(appContext != null ? appContext : ctx);
                } catch (ServiceNotFoundException e) {
                    onServiceNotFound(e);
                }
            }
            return mCachedInstance;
        }
    }

    public abstract T createService(Context applicationContext) throws ServiceNotFoundException;
}

public static void onServiceNotFound(ServiceNotFoundException e) {
    // We're mostly interested in tracking down long-lived core system
    // components that might stumble if they obtain bad references; just
    // emit a tidy log message for normal apps
    if (android.os.Process.myUid() < android.os.Process.FIRST_APPLICATION_UID) {
        Log.wtf(TAG, e.getMessage(), e);
    } else {
        Log.w(TAG, e.getMessage());
    }
}
}

========================================================

image.png
  /**
   * Context for decor views which can be seeded with pure application 
  context and not depend on the
 * activity, but still provide some of the facilities that Activity has,
 * e.g. themes, activity-based resources, etc.
 *
 * @hide
 */
class DecorContext extends ContextThemeWrapper {
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
private Resources mActivityResources;

public DecorContext(Context context, Resources activityResources) {
    super(context, null);
    mActivityResources = activityResources;
}

void setPhoneWindow(PhoneWindow phoneWindow) {
    mPhoneWindow = phoneWindow;
    mWindowManager = null;
}

@Override
public Object getSystemService(String name) {
    if (Context.WINDOW_SERVICE.equals(name)) {
        if (mWindowManager == null) {
            WindowManagerImpl wm =
                    (WindowManagerImpl) super.getSystemService(Context.WINDOW_SERVICE);
            mWindowManager = wm.createLocalWindowManager(mPhoneWindow);
        }
        return mWindowManager;
    }
    return super.getSystemService(name);
}

@Override
public Resources getResources() {
    return mActivityResources;
}

@Override
public AssetManager getAssets() {
    return mActivityResources.getAssets();
}

}

Dialog

public class Dialog implements DialogInterface, Window.Callback,
    KeyEvent.Callback, OnCreateContextMenuListener, Window.OnWindowDismissedCallback {
 //...
"qtip:  look here!!!   dialog 中的windowManger "
private final WindowManager mWindowManager;

final Context mContext;
"qtip:  look here!!!   dialog 中的Window "
final Window mWindow;

View mDecor;

 //....

/**
 * Creates a dialog window that uses the default dialog theme.
 * 

* The supplied {@code context} is used to obtain the window manager and * base theme used to present the dialog. * * @param context the context in which the dialog should run * @see android.R.styleable#Theme_dialogTheme */ public Dialog(@NonNull Context context) { this(context, 0, true); } //....... 以下略 } //dialog的创建 Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (themeResId == ResourceId.ID_NULL) { final TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); themeResId = outValue.resourceId; } mContext = new ContextThemeWrapper(context, themeResId); } else { mContext = context; } "1.获取windowManager " mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); "2 创建window" final Window w = new PhoneWindow(mContext); mWindow = w; "3.设置window回调" w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setOnWindowSwipeDismissedCallback(() -> { if (mCancelable) { cancel(); } }); " 4 给window设置windowManger " w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); mListenersHandler = new ListenersHandler(this); }

PopupWindow

public class PopupWindow {


 //...

private Context mContext;
" PopupWindow 中的windowManager ..."
private WindowManager mWindowManager;

//...

/** View that handles event dispatch and content transitions. */
private PopupDecorView mDecorView;

// ...... 以下 略


}

popupWindow的显示

/**
 * Display the content view in a popup window at the specified location.
 *
 * @param token Window token to use for creating the new window
 * @param gravity the gravity which controls the placement of the popup window
 * @param x the popup's x location offset
 * @param y the popup's y location offset
 *
 * @hide Internal use only. Applications should use
 *       {@link #showAtLocation(View, int, int, int)} instead.
 */
    public void showAtLocation(IBinder token, int gravity, int x, int y) {
    if (isShowing() || mContentView == null) {
        return;
    }

    TransitionManager.endTransitions(mDecorView);

    detachFromAnchor();

    mIsShowing = true;
    mIsDropdown = false;
    mGravity = gravity;

    final WindowManager.LayoutParams p = createPopupLayoutParams(token);
    preparePopup(p);

    p.x = x;
    p.y = y;

    invokePopup(p);
}
 "继续看invokePopup()"

    private void invokePopup(WindowManager.LayoutParams p) {
    if (mContext != null) {
        p.packageName = mContext.getPackageName();
    }

    final PopupDecorView decorView = mDecorView;
    decorView.setFitsSystemWindows(mLayoutInsetDecor);

    setLayoutDirectionFromAnchor();
    "向windowManger中添加View 显示popupWindow "
    mWindowManager.addView(decorView, p);

    if (mEnterTransition != null) {
        decorView.requestEnterTransition(mEnterTransition);
    }
}

"popupWindow的隐藏"
/**
 * Disposes of the popup window. This method can be invoked only after
 * {@link #showAsDropDown(android.view.View)} has been executed. Failing
 * that, calling this method will have no effect.
 *
 * @see #showAsDropDown(android.view.View)
 */
public void dismiss() {
    //如果没有显示或者正在消失 return
    if (!isShowing() || isTransitioningToDismiss()) {
        return;
    }

    final PopupDecorView decorView = mDecorView;
    final View contentView = mContentView;

    final ViewGroup contentHolder;
    final ViewParent contentParent = contentView.getParent();
    if (contentParent instanceof ViewGroup) {
        contentHolder = ((ViewGroup) contentParent);
    } else {
        contentHolder = null;
    }

    // Ensure any ongoing or pending transitions are canceled.
    decorView.cancelTransitions();

    mIsShowing = false;
    mIsTransitioningToDismiss = true;

    // This method may be called as part of window detachment, in which
    // case the anchor view (and its root) will still return true from
    // isAttachedToWindow() during execution of this method; however, we
    // can expect the OnAttachStateChangeListener to have been called prior
    // to executing this method, so we can rely on that instead.
    final Transition exitTransition = mExitTransition;
    if (exitTransition != null && decorView.isLaidOut()
            && (mIsAnchorRootAttached || mAnchorRoot == null)) {
        // The decor view is non-interactive and non-IME-focusable during exit transitions.
        final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
        p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
        p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
        p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
        mWindowManager.updateViewLayout(decorView, p);

        final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null;
        final Rect epicenter = getTransitionEpicenter();

        // Once we start dismissing the decor view, all state (including
        // the anchor root) needs to be moved to the decor view since we
        // may open another popup while it's busy exiting.
        decorView.startExitTransition(exitTransition, anchorRoot, epicenter,
                new TransitionListenerAdapter() {
                    @Override
                    public void onTransitionEnd(Transition transition) {
                        dismissImmediate(decorView, contentHolder, contentView);
                    }
                });
    } else {
    "立即消失   ==============="
        dismissImmediate(decorView, contentHolder, contentView);
    }

    // Clears the anchor view.
    detachFromAnchor();

    if (mOnDismissListener != null) {
        mOnDismissListener.onDismiss();
    }
}


 "再来看看是怎么立即消失的"
/**
 * Removes the popup from the window manager and tears down the supporting
 * view hierarchy, if necessary.
 */
private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
    // If this method gets called and the decor view doesn't have a parent,
    // then it was either never added or was already removed. That should
    // never happen, but it's worth checking to avoid potential crashes.
    if (decorView.getParent() != null) {

        //qtip: 移除decorView 
        mWindowManager.removeViewImmediate(decorView);

    }

    if (contentHolder != null) {
        contentHolder.removeView(contentView);
    }

    // This needs to stay until after all transitions have ended since we
    // need the reference to cancel transitions in preparePopup().
    mDecorView = null;
    mBackgroundView = null;
    mIsTransitioningToDismiss = false;
}

看看decorView是啥
从windowManager中移除的decorView就是PopupWindow中的一个成员变量 :mDecorView
popupWindow 调用showAtLocation()时会先调用preparePopup(),在preparePopup()中完成decorView 和 mBackgroundView的创建

private void preparePopup(WindowManager.LayoutParams p) {
    if (mContentView == null || mContext == null || mWindowManager == null) {
        throw new IllegalStateException("You must specify a valid content view by "
                + "calling setContentView() before attempting to show the popup.");
    }

    if (p.accessibilityTitle == null) {
        p.accessibilityTitle = mContext.getString(R.string.popup_window_default_title);
    }

    // The old decor view may be transitioning out. Make sure it finishes
    // and cleans up before we try to create another one.
    if (mDecorView != null) {
        mDecorView.cancelTransitions();
    }

    // When a background is available, we embed the content view within
    // another view that owns the background drawable.
    if (mBackground != null) {
     "qtip: 通过popupWindow设置的contentView创建一个背景PopupBackgroundView,PopupBackgroundView继承于FramLayout"
        mBackgroundView = createBackgroundView(mContentView);
        mBackgroundView.setBackground(mBackground);
    } else {
        mBackgroundView = mContentView;
    }
    "qtip: 再通过背景创建一个装饰视图 mDecorView"
    mDecorView = createDecorView(mBackgroundView);

    // The background owner should be elevated so that it casts a shadow.
    mBackgroundView.setElevation(mElevation);

    // We may wrap that in another view, so we'll need to manually specify
    // the surface insets.
    p.setSurfaceInsets(mBackgroundView, true /*manual*/, true /*preservePrevious*/);

    mPopupViewInitialLayoutDirectionInherited =
            (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
}


/**
 * Wraps a content view in a FrameLayout.
 *
 * @param contentView the content view to wrap
 * @return a FrameLayout that wraps the content view
 */
private PopupDecorView createDecorView(View contentView) {
    final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
    final int height;
    if (layoutParams != null && layoutParams.height == WRAP_CONTENT) {
        height = WRAP_CONTENT;
    } else {
        height = MATCH_PARENT;
    }

    final PopupDecorView decorView = new PopupDecorView(mContext);
    decorView.addView(contentView, MATCH_PARENT, height);
    decorView.setClipChildren(false);
    decorView.setClipToPadding(false);

    return decorView;
}

PopupDecorView是popupWindow的一个内部类 继承于FramLayout ,重新处理了点击和按键等事件以控制popupWindow返回键和touch消失

是不是对比PopupWindow 和dialog 好像有些不一样

image.png
image.png

Toast

public class Toast {
static final String TAG = "Toast";
static final boolean localLOGV = false;

/** @hide */
@IntDef({LENGTH_SHORT, LENGTH_LONG})
@Retention(RetentionPolicy.SOURCE)
public @interface Duration {}

/**
 * Show the view or text notification for a short period of time.  This time
 * could be user-definable.  This is the default.
 * @see #setDuration
 */
public static final int LENGTH_SHORT = 0;

/**
 * Show the view or text notification for a long period of time.  This time
 * could be user-definable.
 * @see #setDuration
 */
public static final int LENGTH_LONG = 1;

final Context mContext;
" look here !!   TN是什么 "
final TN mTN;
int mDuration;
View mNextView;

//...... 以下略
}



private static class TN extends ITransientNotification.Stub {
    private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();

    private static final int SHOW = 0;
    private static final int HIDE = 1;
    private static final int CANCEL = 2;
    final Handler mHandler;

    int mGravity;
    int mX, mY;
    float mHorizontalMargin;
    float mVerticalMargin;


    View mView;
    View mNextView;
    int mDuration;
    "look here !!!   windowManager  "
    WindowManager mWM;

    String mPackageName;

    static final long SHORT_DURATION_TIMEOUT = 4000;
    static final long LONG_DURATION_TIMEOUT = 7000;

    TN(String packageName, @Nullable Looper looper) {
        // XXX This should be changed to use a Dialog, with a Theme.Toast
        // defined that sets up the layout params appropriately.
        final WindowManager.LayoutParams params = mParams;
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.format = PixelFormat.TRANSLUCENT;
        params.windowAnimations = com.android.internal.R.style.Animation_Toast;
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.setTitle("Toast");
        params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;

        mPackageName = packageName;

        if (looper == null) {
            // Use Looper.myLooper() if looper is not specified.
            looper = Looper.myLooper();
            if (looper == null) {
                throw new RuntimeException(
                        "Can't toast on a thread that has not called Looper.prepare()");
            }
        }
        mHandler = new Handler(looper, null) {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case SHOW: {
                        IBinder token = (IBinder) msg.obj;
                        handleShow(token);
                        break;
                    }
                    case HIDE: {
                        handleHide();
                        // Don't do this in handleHide() because it is also invoked by
                        // handleShow()
                        mNextView = null;
                        break;
                    }
                    case CANCEL: {
                        handleHide();
                        // Don't do this in handleHide() because it is also invoked by
                        // handleShow()
                        mNextView = null;
                        try {
                            getService().cancelToast(mPackageName, TN.this);
                        } catch (RemoteException e) {
                        }
                        break;
                    }
                }
            }
        };
    }

你可能感兴趣的:(Android源码 windowManager)