1. 程式人生 > >WindowManagerService新增View流程

WindowManagerService新增View流程

我們都知道在android中所有的介面顯示相關的,都是通過WindowManager.addView方法來將當前需要顯示的View新增到window中。

Window與WindowManager之間的關係

WindowManager的實現類就是WindowManagerImpl:

@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        // 每一個window都又一個唯一標識的token,這裡如果沒有,則設定系統預設的
        applyDefaultToken(params
); // mGlobal是WindowManagerGlobal型別 mGlobal.addView(view, params, mDisplay, mParentWindow); }

然後通過,ViewRootImpl進一步實現當前需要顯示的View的繪製,具體可以參考setContentView那些事
可以看到,在framework中Window和PhoneWindow構成了視窗的抽象部分,其中Window為抽象介面,PhoneWindow為具體實現,同樣的WindowManager是實現部分的父類

WindowManagerImpl為具體實現邏輯,在WindowManagerImpl中使用WindowManagerGlobal通過IWindowManager介面與WindowManagerService進行互動,並由WMS完成具體的視窗管理工作

public final class WindowManagerGlobal {

    private static IWindowManager sWindowManagerService;

    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"
)); try { sWindowManagerService = getWindowManagerService(); ValueAnimator.setDurationScale(sWindowManagerService.getCurrentAnimatorScale()); } catch (RemoteException e) { Log.e(TAG, "Failed to get WindowManagerService, cannot set animator scale", e); } } return sWindowManagerService; } } }

Window與WindowManager建立連線

在Window中維護了一個mWindowManager屬性,可以通過 方法設定一個mWindowManager,來和WindowManager建立連線

public abstract class Window {
    private WindowManager mWindowManager;

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
        setWindowManager(wm, appToken, appName, false);
    }

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
    ....
}

關於WindowManagerService

WindowManagerService(WMS),和其他系統服務一樣也是在SystemServer中啟動的。

public final class SystemServer {
    private void startOtherServices() {
       ....
        // 通過WindowManagerService的靜態main方法獲取一個WindowManagerService例項
        wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore);
        // 將WMS新增到ServiceManager中
        ServiceManager.addService(Context.WINDOW_SERVICE, wm);
        ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

        mActivityManagerService.setWindowManager(wm);
    }

}

在startOtherServices中,獲取WindowManagerService例項,然後新增到ServiceManager中,之後我們就可以通過ServiceManager#getService獲取WMS了.

WindowManagerService.main方法

通過WindowManagerService.main方法獲取WMS例項,其實就是在main方法內部通過非同步方法new了一個WindowManagerService例項。

public static WindowManagerService main(final Context context,
            final InputManagerService im,
            final boolean haveInputMethods, final boolean showBootMsgs,
            final boolean onlyCore) {
        final WindowManagerService[] holder = new WindowManagerService[1];
        DisplayThread.getHandler().runWithScissors(new Runnable() {
            @Override
            public void run() {
                // 通過非同步方法建立一個WindowManagerService例項
                holder[0] = new WindowManagerService(context, im,
                        haveInputMethods, showBootMsgs, onlyCore);
            }
        }, 0);
        return holder[0];
}

WindowManagerService構造方法

private WindowManagerService(Context context, InputManagerService inputManager,
                                 boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
        // 完成一些初始化工作
        mContext = context;
        mHaveInputMethods = haveInputMethods;
        mAllowBootMessages = showBootMsgs;
        mOnlyCore = onlyCore;
        // 省略程式碼
        // 獲取顯示服務
        mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
        // 為每一個display分配一個content
        mDisplays = mDisplayManager.getDisplays();
        for (Display display : mDisplays) {
            createDisplayContentLocked(display);
        }

        mKeyguardDisableHandler = new KeyguardDisableHandler(mContext, mPolicy);

        // 獲取PowerManager服務,並且註冊LowPowerModeObserver
        mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
        mPowerManagerInternal.registerLowPowerModeObserver(
                new PowerManagerInternal.LowPowerModeListener() {
                    @Override
                    public void onLowPowerModeChanged(boolean enabled) {
                        synchronized (mWindowMap) {
                            if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                                mAnimationsDisabled = enabled;
                                dispatchNewAnimatorScaleLocked(null);
                            }
                        }
                    }
                });
        // 省略程式碼
        // 獲取IActivityManager
        mActivityManager = ActivityManagerNative.getDefault();
        mBatteryStats = BatteryStatsService.getService();
        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
        AppOpsManager.OnOpChangedInternalListener opListener =
                new AppOpsManager.OnOpChangedInternalListener() {
                    @Override public void onOpChanged(int op, String packageName) {
                        updateAppOpsState();
                    }
                };
        .....

        // 構建視窗動畫
        mAnimator = new WindowAnimator(this);
        // 初始化視窗管理策略
        initPolicy();
        // 開啟繪製SurfaceView事務
        SurfaceControl.openTransaction();
        ....
}

深入理解WindowManagerService

WMS主要用來管理當前視窗和對事件的管理和分發,在IWindowManager.aidl檔案中定義了大部分WMS的功能方法,另外作為視窗的管理者,WMS裡也定義了各種不同的視窗

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    // 已經啟動完成的應用
    final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<>();
    // 尺寸正在改變的視窗
    final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
    // 動畫結束的視窗
    final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
    // 即將釋放Surface的視窗
    final ArrayList<WindowState> mDestroySurface = new ArrayList<>();
    // 失去焦點的視窗
    ArrayList<WindowState> mLosingFocus = new ArrayList<>();
    // 為了釋放記憶體,需要強制關閉的視窗
    final ArrayList<WindowState> mForceRemoves = new ArrayList<>();
    // 等待繪製的視窗
    ArrayList<WindowState> mWaitingForDrawn = new ArrayList<>();
    // 正在開啟的應用
    final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<>();
    // 正在關閉的應用
    final ArraySet<AppWindowToken> mClosingApps = new ArraySet<>();
    // 當前獲得焦點的視窗
    WindowState mCurrentFocus = null;
    // 上一個獲得焦點的視窗
    WindowState mLastFocus = null;
    // 輸入發視窗下方的視窗
    WindowState mInputMethodTarget = null;
    // 輸入法視窗
    WindowState mInputMethodWindow = null;
    // 得到焦點的應用
    AppWindowToken mFocusedApp = null;

}

可以看到在WMS中維護的成員變數大都用到了線性表,不同視窗或者同一個視窗在不同階段可能位於不同的線性表中,對於視窗,主要分為應用視窗和系統視窗

  • 應用視窗

應用視窗中,我們常見的activity所處的視窗,應用對話視窗,應用彈出視窗都屬於該類,與應用視窗相關的主要是Window和PhoneWindow類
PhoneWindow繼承自Window,應用視窗的新增主要通過WindowManager.addView方法將一個DecorView新增到WindowManager中,具體可以參考setContentView那些事

  • 系統視窗

我們平時常見的狀態列,導航欄等都是系統視窗,對於系統視窗,不像activity那樣使用setContentView來設定佈局,它沒有專門的封裝類,而是直接使用WindowManager.addView方法
將一個View新增到WindowManager中,下面看下PhoneStatusBar的顯示過程。

PhoneStatusBar的顯示

對於PhoneStatusBar,其主要的是在addStatusBarWindow中添加當前statusbar到WindowManager中的.

private void addStatusBarWindow() {
    // 載入並建立StatusBarWindowView,StatusBarWindowView繼承自FrameLayout
    makeStatusBarView();
    mStatusBarWindowManager = new StatusBarWindowManager(mContext);
    // 將StatusBarWindowView新增到WindowManager中
    mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}

載入並建立StatusBarWindowView

protected PhoneStatusBarView makeStatusBarView() {
        final Context context = mContext;

        Resources res = context.getResources();

        updateDisplaySize(); // populates mDisplayMetrics
        updateResources();
        // 載入佈局檔案,並初始化mStatusBarWindow物件
        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
        ....
        return mStatusBarView;
}

將StatusBarWindowView新增到WindowManager

在StatusBarWindowManager中將StatusBarWindowView新增到WindowManager中的:

public void add(View statusBarView, int barHeight) {
        mLp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                barHeight,
                WindowManager.LayoutParams.TYPE_STATUS_BAR,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                        | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                PixelFormat.TRANSLUCENT);
        mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
        mLp.gravity = Gravity.TOP;
        mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
        mLp.setTitle("StatusBar");
        mLp.packageName = mContext.getPackageName();
        mStatusBarView = statusBarView;
        mBarHeight = barHeight;
        mWindowManager.addView(mStatusBarView, mLp);
        mLpChanged = new WindowManager.LayoutParams();
        mLpChanged.copyFrom(mLp);
}

WindowManager.addView流程分析

上述程式碼通過WindowManager.addView將當前View顯示到螢幕,那麼當前View具體是怎麼被顯示到螢幕的,下面就是我們要討論的:
我們知道WindowManager是一個介面,其具體的實現類是WindowManagerImpl

public interface WindowManager extends ViewManager

看下WindowManagerImpl#addView方法:

public final class WindowManagerImpl implements WindowManager {

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        android.util.SeempLog.record_vg_layout(383,params);
        applyDefaultToken(params);
        // mGlobal是WindowManagerGlobal類的例項
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}

可以看到,上述最終實質上是通過WindowManagerGlobal#addView實現具體的邏輯

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ....        
        try {
            // root是ViewRootImpl的例項
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
           ....
        }
}

繼續分析ViewRootImpl#setView的邏輯實現:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ....
                int res; /* = WindowManagerImpl.ADD_OKAY; */
                // 實現具體的繪製操作
                requestLayout();
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();
                    // 通過addToDisplay方法向WMS發起一個Session請求,這裡最終會呼叫Session中對應的方法
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    ....
                    throw new RuntimeException("Adding window failed", e);
                } finally {
                    if (restore) {
                        attrs.restore();
                    }
                }
            }
        }
}

上面的方法主要做了下面的操作:
1. requestLayout(); // 進行具體的繪製操作
2. 呼叫了Session.addToDisplay方法:

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

最終還是回到了WMS中與其建立連線,並且上述addToDisplay呼叫最終返回WMS中的addWindow的返回結果。

final WindowManagerPolicy mPolicy = new PhoneWindowManager();
public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        int[] appOp = new int[1];
        // mPolicy實際上是一個PhoneWindowManager型別,在checkAddPermission方法中,首先判斷視窗型別是否是系統級別的,
        // 如果不是系統級別的視窗,則返回一個ADD_OKAY,否則需要SYSTEM_ALERT_WINDOW或者INTERNAL_SYSTEM_WINDOW許可權
        int res = mPolicy.checkAddPermission(attrs, appOp);
        if (res != WindowManagerGlobal.ADD_OKAY) {
            return res;
        }

        ....

        synchronized(mWindowMap) {
            ....
            boolean addToken = false;
            WindowToken token = mTokenMap.get(attrs.token);
            if (token == null) {
                // 如果視窗是子視窗
                if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                // 如果是輸入法視窗
                if (type == TYPE_INPUT_METHOD) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                // 如果是牆紙視窗
                if (type == TYPE_WALLPAPER) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                // 如果是DayDream視窗,即互動屏保
                if (type == TYPE_DREAM) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                // 構造WindowToken物件
                token = new WindowToken(this, attrs.token, -1, false);
                addToken = true;
            } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
                // 獲取應用的AppWindowToken
                AppWindowToken atoken = token.appWindowToken;
                if (atoken == null) {
                    return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
                } else if (atoken.removed) {
                    return WindowManagerGlobal.ADD_APP_EXITING;
                }
                if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
                    // No need for this guy!
                    if (localLOGV) Slog.v(
                            TAG, "**** NO NEED TO START: " + attrs.getTitle());
                    return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;
                }
            } else if (type == TYPE_INPUT_METHOD) {
                if (token.windowType != TYPE_INPUT_METHOD) {
                      // 如果是輸入法視窗,token的windowType必須是ADD_BAD_APP_TOKEN型別
                      return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
            } 
            ....

           // 在視窗的有效性檢查完成之後,為當前視窗建立一個WindowState物件,來維護視窗的狀態以及根據適當的機制來調整視窗的狀態
           WindowState win = new WindowState(this, session, client, token,
                    attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
           // 如果客戶端已經被銷燬
            if (win.mDeathRecipient == null) {
                return WindowManagerGlobal.ADD_APP_EXITING;
            }
           
            
            if (outInputChannel != null && (attrs.inputFeatures
                    & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                // 如果輸出Channel的讀通道為空,則建立通道
                String name = win.makeInputChannelName();
                InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
                win.setInputChannel(inputChannels[0]);
                inputChannels[1].transferTo(outInputChannel);
                // 向InputManager中註冊通道,以便當前視窗可以接收到事件
                mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
            }

           .....
        }
}

到現在為止,使用WindowManager.addView方法顯示對應的View解析就完成了,重點總結一下:
1. WindowManager#addView—>WindowManagerGlobal#addView—>ViewRootImpl#setView
2. 在ViewRootImpl#setView中的requestLayout();實現具體的繪製操作
3. 在ViewRootImpl#setView中呼叫Session#addToDisplay
4. 在Session#addToDisplay中最終還是回到了WMS中與其建立連線,並且最終呼叫WMS的addWindow
5. 在WMS的addWindow方法中,主要做了下面幾件事:

  • 檢查當前視窗的許可權,如果不是系統級別的視窗,則返回一個ADD_OKAY,否則需要SYSTEM_ALERT_WINDOW或者INTERNAL_SYSTEM_WINDOW許可權

  • 根據當前視窗型別,返回對應的token值

  • 當前視窗建立一個WindowState物件,來維護視窗的狀態以及根據適當的機制來調整視窗的狀態,並且通過registerInputChannel,以便當前視窗可以接收輸入事件