Android开发Android开发Android技术知识

探秘Android之WindowManager

2018-06-04  本文已影响3人  shhp

在Android的世界里,我们可以通过WindowManager将一个视图添加到屏幕上。下面就是实现此需求的两条关键语句:

WindowManager wm = (WindowManager) contex.getSystemService(Context.WINDOW_SERVICE);

...

wm.addView(view, layoutParam);

这篇文章将以这两条语句作为切入点,探究一下与WindowManager相关的源码(基于5.1.1系统)。

WindowManager是什么

根据WindowManager的定义

public interface WindowManager extends ViewManager

可以看出WindowManager是一个继承自ViewManager的接口。所以WindowManager继承了ViewManager中的两个主要函数:

public void addView(View view, ViewGroup.LayoutParams params);
public void removeView(View view);

从函数名便可推断这两个函数的作用分别是往屏幕添加视图以及移除之前添加的视图。

既然WindowManager只是一个接口,那必然有类实现了这个接口。让我们回到这条语句

WindowManager wm = (WindowManager) contex.getSystemService(Context.WINDOW_SERVICE);

去探究一下通过contex.getSystemService(Context.WINDOW_SERVICE)拿到的WindowManager到底是什么。

因为与UI紧密相关的ContextActivity,所以假定这里的context是一个Activity

首先看一下ActivitygetSystemService的实现:

@Override
5033    public Object getSystemService(@ServiceName @NonNull String name) {
5034        if (getBaseContext() == null) {
5035            throw new IllegalStateException(
5036                    "System services not available to Activities before onCreate()");
5037        }
5038
5039        if (WINDOW_SERVICE.equals(name)) {
5040            return mWindowManager;
5041        } else if (SEARCH_SERVICE.equals(name)) {
5042            ensureSearchManager();
5043            return mSearchManager;
5044        }
5045        return super.getSystemService(name);
5046    }

通过5039行的if语句可以看出,如果参数nameWINDOW_SERVICE则直接返回Activity的成员变量mWindowManager. 接下来需要找到mWindowManager被初始化的地方。它的初始化紧随Activity的初始化。而Activity的初始化在ActivityThread.performLaunchActivity中进行。这里的ActivityThread就是我们通常所说的主线程或者UI线程。摘取ActivityThread.performLaunchActivity中的几条关键语句:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
       Activity activity = null;
...
        activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
...
        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被创建出来后其attach方法即被调用。

5922    final void attach(Context context, ActivityThread aThread,
5923            Instrumentation instr, IBinder token, int ident,
5924            Application application, Intent intent, ActivityInfo info,
5925            CharSequence title, Activity parent, String id,
5926            NonConfigurationInstances lastNonConfigurationInstances,
5927            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {

           ...      

5932        mWindow = PolicyManager.makeNewWindow(this);

           ...

5966        mWindow.setWindowManager(
5967                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
5968                mToken, mComponent.flattenToString(),
5969                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
5970        if (mParent != null) {
5971            mWindow.setContainer(mParent.getWindow());
5972        }
5973        mWindowManager = mWindow.getWindowManager();
5974        mCurrentConfig = config;
5975    }

在第5932行,Activity的成员变量mWindow是一个Window类对象。Window是对Activity或者Dialog的视图的一种上层抽象。Window是一个抽象类,而PhoneWindow继承了Window并且实现了其中的一些关键方法。PhoneWindow中有一个类型为DecorView的成员变量mDecor,表示的是Activity对应的视图的最外层容器。PolicyManager.makeNewWindow(this)正好返回了一个PhoneWindow对象。

接下来在5966行,Window.setWindowManager被调用。该函数需要四个参数。首先我们看第一个参数(WindowManager)context.getSystemService(Context.WINDOW_SERVICE). 在这里context.getSystemService(Context.WINDOW_SERVICE)又一次被调用并且返回一个WindowManager对象。不过这里的context不是某个Activity对象,而是一个ContextImpl对象。接下来看一下ContextImpl.getSystemService的实现。

@Override
public Object getSystemService(String name) {
    ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
    return fetcher == null ? null : fetcher.getService(this);
}

可以看到getSystemService会根据参数nameSYSTEM_SERVICE_MAP中拿到对应的ServiceFetcher对象,然后通过ServiceFetcher.getService返回具体的对象。在ContextImpl中有一个函数的作用是往SYSTEM_SERVICE_MAP中放入(name, ServiceFetcher)映射。

private static void registerService(String serviceName, ServiceFetcher fetcher) {
    if (!(fetcher instanceof StaticServiceFetcher)) {
        fetcher.mContextCacheIndex = sNextPerContextServiceCacheIndex++;
    }
    SYSTEM_SERVICE_MAP.put(serviceName, fetcher);
}

另外在ContextImpl中有一个static语句块,里面通过多次调用registerService方法将所有可能的(name, ServiceFetcher)映射放入了SYSTEM_SERVICE_MAP. 这里我们特别关注一下WINDOW_SERVICE:

634     registerService(WINDOW_SERVICE, new ServiceFetcher() {
635             Display mDefaultDisplay;
636             public Object getService(ContextImpl ctx) {
637                 Display display = ctx.mDisplay;
638                 if (display == null) {
639                     if (mDefaultDisplay == null) {
640                         DisplayManager dm = (DisplayManager)ctx.getOuterContext().getSystemService(Context.DISPLAY_SERVICE);
642                         mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
643                     }
644                     display = mDefaultDisplay;
645                 }
646                 return new WindowManagerImpl(display);
647             }});

可以看到getService返回了一个WindowManagerImpl对象。之前提到WindowManager只是一个接口,而这里的WindowManagerImpl正好实现了WindowManager.

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;
    private final Window mParentWindow;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Display display) {
        this(display, null);
    }

    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }

    ...
}

有一点需要注意:这里用到了WindowManagerImpl的只需一个参数的构造函数。通过上面的类的定义可以看到,WindowManagerImpl还有一个需要两个参数的构造函数,这个构造函数的调用接下来就会看到。还有我们可以看到WindowManagerImpl中有一个类型为WindowManagerGlobal的成员变量mGlobal,使用了单例模式,它的作用将在下节说明。

现在回到Activity.attach方法的5966行,我们已经知道第一个参数是一个WindowManagerImpl对象。接着就看一下Window.setWindowManager的具体实现。

539     public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
540             boolean hardwareAccelerated) {
541         mAppToken = appToken;
542         mAppName = appName;
543         mHardwareAccelerated = hardwareAccelerated
544                 || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
545         if (wm == null) {
546             wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
547         }
548         mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
549     }

这里有两个比较关键的点:

再次回到Activity.attach方法。第5973行将Window的成员变量mWindowManager指向的WindowManagerImpl对象赋给了Activity的成员变量mWindowManager. 至此,我们就知道了通过Activity.getSystemService(Context.WINDOW_SERVICE);拿到的是一个WindowManager的实现类WindowManagerImpl的对象。对于非ActivityContext,调用它们的getSystemService方法实际上会调用ContextImpl.getSystemService. 这个方法返回的也是一个WindowManagerImpl的对象。只不过这个WindowManagerImpl的对象的成员变量mParentWindownull,也就是没有关联任何Window对象。

WindowManager.addView做了什么

接下来探究一下WindowManager.addView到底做了什么。

通过上面的分析,我们已经知道实现WindowManager这个接口的是WindowManagerImpl类,因此直接看一下WindowManagerImpl.addView的实现:

@Override
public void addView(@NonNull View view, 
@NonNull ViewGroup.LayoutParams params) {
    applyDefaultToken(params);
    mGlobal.addView(view, params, mDisplay, mParentWindow);
}

函数就只有两句话,我们看关键的第二句。这里的mGlobal就是之前提到过的WindowManagerGlobal对象。它是一个单例,也就意味着在一个应用进程里,虽然每一个Activity会对应不同的WindowManagerImpl对象,但是它们的视图是由唯一的一个WindowManagerGlobal对象统一管理。

在看WindowManagerGlobal.addView的实现之前,有必要先说明一下WindowManagerGlobal中有三个比较重要的ArrayList:

private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();

这三个ArrayList分别保存了View, ViewRootImplWindowManager.LayoutParams.

接下来就具体分析一下WindowManagerGlobal.addView.

204    public void addView(View view, ViewGroup.LayoutParams params,
205            Display display, Window parentWindow) {
206        if (view == null) {
207            throw new IllegalArgumentException("view must not be null");
208        }
209        if (display == null) {
210            throw new IllegalArgumentException("display must not be null");
211        }
212        if (!(params instanceof WindowManager.LayoutParams)) {
213            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
214        }
215
216        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
217        if (parentWindow != null) {
218            parentWindow.adjustLayoutParamsForSubWindow(wparams);
219        } else {
220            // If there's no parent and we're running on L or above (or in the
221            // system context), assume we want hardware acceleration.
222            final Context context = view.getContext();
223            if (context != null
224                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
225                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
226            }
227        }
228
229        ViewRootImpl root;
230        View panelParentView = null;
231
232        synchronized (mLock) {
233            // Start watching for system property changes.
234            if (mSystemPropertyUpdater == null) {
235                mSystemPropertyUpdater = new Runnable() {
236                    @Override public void More ...run() {
237                        synchronized (mLock) {
238                            for (int i = mRoots.size() - 1; i >= 0; --i) {
239                                mRoots.get(i).loadSystemProperties();
240                            }
241                        }
242                    }
243                };
244                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
245            }
246
247            int index = findViewLocked(view, false);
248            if (index >= 0) {
249                if (mDyingViews.contains(view)) {
250                    // Don't wait for MSG_DIE to make it's way through root's queue.
251                    mRoots.get(index).doDie();
252                } else {
253                    throw new IllegalStateException("View " + view
254                            + " has already been added to the window manager.");
255                }
256                // The previous removeView() had not completed executing. Now it has.
257            }
258
259            // If this is a panel window, then find the window it is being
260            // attached to for future reference.
261            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
262                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
263                final int count = mViews.size();
264                for (int i = 0; i < count; i++) {
265                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
266                        panelParentView = mViews.get(i);
267                    }
268                }
269            }
270
271            root = new ViewRootImpl(view.getContext(), display);
272
273            view.setLayoutParams(wparams);
274
275            mViews.add(view);
276            mRoots.add(root);
277            mParams.add(wparams);
278        }
279
280        // do this last because it fires off messages to start doing things
281        try {
282            root.setView(view, wparams, panelParentView);
283        } catch (RuntimeException e) {
284            // BadTokenException or InvalidDisplayException, clean up.
285            synchronized (mLock) {
286                final int index = findViewLocked(view, false);
287                if (index >= 0) {
288                    removeViewLocked(index, true);
289                }
290            }
291            throw e;
292        }
293    }

206-214行是对参数的正确性检查。

然后217行有一个if语句:如果parentWindow不为null则调用其adjustLayoutParamsForSubWindow方法。这里的parentWindow实际上指向的是WindowManagerImpl的成员变量mParentWindow. 如果我们仍然假定当前的Context是一个Activity, 那么parentWindow就非空。因此Window.adjustLayoutParamsForSubWindow被调用:

551     void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
552         CharSequence curTitle = wp.getTitle();
553         if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
554             wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                ...
581         } else {
582             if (wp.token == null) {
583                 wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
584             }
585             if ((curTitle == null || curTitle.length() == 0)
586                     && mAppName != null) {
587                 wp.setTitle(mAppName);
588             }
589         }
            ...
596     }

这里最关键的是第583行,将Window里保存的Activity的标识mAppToken赋给了传进来的WindowManager.LayoutParamstoken. 由于这个WindowManager.LayoutParams最后会传给WindowManagerService, 因此WindowManagerService可以通过这个token知道是哪个Activity要添加视图。

回到WindowManagerGlobal.addView, 248-257行会对要添加的view进行重复添加的检查。如果发现是重复添加则抛出异常。

如果当前要添加的view从属于某个之前已经添加的view,261-269行就会去找出那个已经添加的view.

接着在271行,一个ViewRootImpl对象被创建出来。ViewRootImpl在应用进程这边管理视图的过程中担任了重要的角色。像Activity对应的视图的measure, layout, draw等过程都是从ViewRootImpl开始。另外ViewRootImpl还负责应用进程和WindowManagerService进程之间的通信。

275-277行将view, rootwparams分别加入各自对应的ArrayList. View, ViewRootImplWindowManager.LayoutParams这三者是通过ArrayList的下标一一对应的。

最后282行调用了ViewRootImpl.setView, 在这个方法中ViewRootImpl会去通知WindowManagerService将新的视图添加到屏幕上。

总结

这篇文章所讲的内容可以浓缩成下面这张图。

WindowManager

一个Activity会有一个成员变量指向一个WindowManager的具体实现类WindowManagerImpl对象。该WindowManagerImpl对象会保存该Activity对应的一个PhoneWindow对象的引用。一个PhoneWindow又会引用一个DecorView对象。在一个应用进程中,会有一个WindowManagerGlobal的单例管理应用中所有Activity对应的视图。WindowManagerGlobal中有三个关键的ArrayList,分别保存了View, ViewRootImplWindowManager.LayoutParams.

上文所讲的这些内容都是发生在应用进程里的事情。在系统里真正负责视图管理的是WindowManagerService进程。其中,ViewRootImpl担任了沟通应用进程和WindowManagerService进程的角色。

上一篇 下一篇

猜你喜欢

热点阅读