Android源碼解析Window系列第(三)篇---WindowManager基本原理

前面的兩篇博客,總結了一下Window的基本知識,我們知道Window是一個抽象的概念,每一個Window都對應着一個View,Window的呈現方式是View,View要依賴Window,View和Window最終要關聯在一起。Activity在android中所起的作用主要是處理一些邏輯問題,比如生命週期的管理、建立窗口等。Window的層次關係就如下面畫的一樣。詳細參考前面兩篇博客。

Window層次
Android源碼解析Window系列第(一)篇—Window的基本認識和Activity的加載流程

Android源碼解析Window系列第(二)篇—Dialog加載繪製流程

這篇博客寫一下WindowManager,在android中,窗口的管理還是比較重要的一塊,因爲他直接負責把內容展示給用戶,並和用戶進行交互。響應用戶的輸入等。WindowManager可以說是外部訪問Window的入口,利用WindowManager可以向Window中添加View,刪除View,更新View。其實我們已經用過很多次WindowManager的addView方法了,因爲ViewGroup就繼承了ViewManager,WindowManager也是繼承了ViewManager。再比如360手機助手有一個懸浮窗,用來顯示內存的實時信息,這個也要通過WindowManager來處理。

1、WindowManager基本認識

  • WindowManager可以通過下面兩種方式進行獲取。
WindowManager   mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 
WindowManager mWindowManager=(WindowManager) getWindowManger(); 
  • 設置WindowManager.LayoutParams參數
WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams(); 

// 設置window type 
wmParams.type = LayoutParams.TYPE_PHONE; 

// 設置圖片格式,效果爲背景透明
wmParams.format = PixelFormat.RGBA_8888; 

 // 調整懸浮窗口至右側中間 
wmParams.gravity = Gravity.RIGHT| Gravity. CENTER_VERTICAL;

// 以屏幕左上角爲原點,設置xy初始值 
wmParams.x = 0;
wmParams.y = 0;
// 設置懸浮窗口長寬數據 
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height =WindowManager.LayoutParams.WRAP_CONTENT;
  • WindowManager是一個接口,並且繼承了ViewManager
public interface WindowManager extends ViewManager 
  • WindowManager.LayoutParams內部包含了Window的一些參數,比如Window的寬高,對齊方式,flag等。

  • WindowManager是外界訪問Window的入口,Window的具體實現是WindowManagerService,WindowManager和WindowManagerService的交互是一個IPC過程。

  • Android中所有的視圖都是通過Window呈現的,不管是Activity、Dialog還是Toast,他們的視圖實際上都是附加在Window上的,因此Window實際是View的直接管理者。

2、Window中添加View

既然WindowManager是外部訪問Window的入口,對於Activity的Window來說,WindowManager肯定是可以訪問Activity的Window的,想往Activity中的DecorView添加View,Activity應改持有了WindowManager的引用,在第一篇博客中,有分析過,在ActivityThread中的performLaunchActivity內部用反射的方式創建了Activity對象之後,調用了Activity對象的attach方法。

 final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        ....

        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(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();
        mCurrentConfig = config;
    }

attach內部創建了PhoneWindow之後,以(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)的方式獲取了WindowManager對象,並且設置給了Window中的mWindowManage成員變量。

  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);
        }
        //返回的是WindowManagerImpl
        mWindowManager =((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        //可以判斷出WindowManagerImpl是WindowManager的實現類
        return new WindowManagerImpl(mContext, parentWindow);
    }

然後以mWindow.getWindowManager()方式給Activity的mWindowManager賦值,到此Activity持有了mWindowManager對象,mWindow對象,Window也持有了mWindowManager對象,Activity類的Window類型成員變量mWindow及WindowManager類型成員變量mWindowManager都被賦值,現在可以操作Activity的Window了。

現在分析一下WindowManager是如何操作Window中的View的,WindowManager繼承了ViewManager,ViewManager定義了三個操作View的方法。addView/updateViewLayoutView/removeView,所以ViewManager像是定義了一套外部操作Activity的Window中的View的接口。

public interface ViewManager{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

上面說了,WindowManager的真正實現類是WindowManagerImpl

public final class WindowManagerImpl implements WindowManager {

    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ......

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

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

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

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

    @Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    ......
} 

WindowManagerImpl雖然帶着Impl的後綴,卻把活都交給了mGlobal,這樣就要去看看WindowManagerGlobal的addView了。

 public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
        //參數校驗省略
        ......

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        if (parentWindow != null) {
            //如果是子View,需要調整佈局參數
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            // If there's no parent and we're running on L or above (or in the
            // system context), assume we want hardware acceleration.
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        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.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
    }

上面的代碼主要有五個步驟
- 1、構建ViewRootImpl,暫時不管ViewRootImpl是什麼東西
- 2、給所添加的View設置參數
- 3、保存View到mViews的列表中
- 4、保存ViewRootImpl對象root到mRoots的列表中
- 5、調用ViewRootImpl對象root的setView方法,將View顯示到手機上。setView方法比較複雜,已經深入到Native層了,不分析了。

3、WindowManager獲取方式的分析

WindowManager w1 = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

WindowManager w2 = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);

上面的w1與w2有什麼區別呢?

w1獲取是通過Context的getSystemService方法,這個方法的實現在Activity中,返回的是Activity的mWindowManager。

 @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)) {
            //此處返回的是Activity的mWindowManager
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

此處返回的Activity的mWindowManager是通過createLocalWindowManager來創建的,注意傳入給WindowManagerImpl中參數parentWindow,就是當前Window對象。

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

因爲Activity有多個,所以每個Activity的WindowManager都不一樣。而w2的獲取的WindowManager,最終WindowManagerImpl時傳入給WindowManagerImpl中parentWindow是null值,這獲取的WindowManager對每個APP來說是全局單例的。

4、 Window在事件分發中的應用

當一個點擊事件產生時,它首先傳遞到Activity、在Activity的dispatchToucheEvent的方法中進行分發、然後傳遞到Window、最後纔到頂級view。

    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

查看Window子類PhoneWindow的源碼。

    @Override
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }

而這裏的mDecor就是頂級View,就這樣事件就從window傳遞到了頂級View了。

Please accept mybest wishes for your happiness and success!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章