【Android】Android Window

1,Window和View的关系

View必须依托于Window这个抽象该类存在,通过Window实现View的变化,

Activity的启动过程,View和Window的联系发生在ActivityThread的handleResumeActivity函数中,

通过makeVisible,使得界面对用户可见

if (r.activity.mVisibleFromClient) {

    r.activity.makeVisible();

}


可以看到,wm在此处才添加mDecor(顶层View)

void makeVisible() {

if (!mWindowAdded) {

ViewManager wm = getWindowManager();

wm.addView(mDecor, getWindow().getAttributes());

mWindowAdded = true;

}

mDecor.setVisibility(View.VISIBLE);

}


简而言之: Activity ---- Window ---- View

---------------------------------------------------------------------------------------------------------------------------------

2,WindowManager.addView()分析

在Activity中,通过Context可以获取到WindowManager,

val wm: WindowManager = this.getSystemService(Context.WINDOW_SERVICE) as WindowManager

wm提供三种核心方法,即View的add、update、delete,继承ViewManager,

public interface ViewManager

{

    /**

     * Assign the passed LayoutParams to the passed View and add the view to the window.

     * <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming

     * errors, such as adding a second view to a window without removing the first view.

     * <p>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);

}


而具体实现类是WindowManagerImpl,现在可以先分析下addView方法,注意WindowManager只有一个实现类WindowManagerImpl,

@Override

public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {

    applyDefaultToken(params);

    mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,

            mContext.getUserId());

}


委托给WindowManagerGlobal实现,

public void addView(View view, ViewGroup.LayoutParams params,

        Display display, Window parentWindow, int userId) {

    if (view == null) {

        throw new IllegalArgumentException("view must not be null");

    }

    if (display == null) {

        throw new IllegalArgumentException("display must not be null");

    }

    if (!(params instanceof WindowManager.LayoutParams)) {

        throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");

    }

    final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;

    if (parentWindow != null) {

         //对子window,调整参数

        parentWindow.adjustLayoutParamsForSubWindow(wparams);

    else {

        // If there's no parent, then hardware acceleration for this view is

        // set from the application's hardware acceleration setting.

        final Context context = view.getContext();

        if (context != null

                && (context.getApplicationInfo().flags

                        & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {

            wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;

        }

    }

    ViewRootImpl root;

    View panelParentView = null;

    synchronized (mLock) {

        // Start watching for system property changes.

        if (mSystemPropertyUpdater == null) {

            mSystemPropertyUpdater = new Runnable() {

                @Override public void run() {

                    synchronized (mLock) {

                        for (int i = mRoots.size() - 1; i >= 0; --i) {

                            mRoots.get(i).loadSystemProperties();

                        }

                    }

                }

            };

            SystemProperties.addChangeCallback(mSystemPropertyUpdater);

        }

        int index = findViewLocked(view, false);

        if (index >= 0) {

            if (mDyingViews.contains(view)) {

                // Don't wait for MSG_DIE to make it's way through root's queue.

                mRoots.get(index).doDie();

            else {

                throw new IllegalStateException("View " + view

                        " has already been added to the window manager.");

            }

            // The previous removeView() had not completed executing. Now it has.

        }

        // If this is a panel window, then find the window it is being

        // attached to for future reference.

        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&

                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {

            final int count = mViews.size();

            for (int i = 0; i < count; i++) {

                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {

                    panelParentView = mViews.get(i);

                }

            }

        }

        root = new ViewRootImpl(view.getContext(), display);

        view.setLayoutParams(wparams);

        // 这是存储了所有Window的参数,严格按照顺序

        mViews.add(view);

        mRoots.add(root);

        mParams.add(wparams);

        // do this last because it fires off messages to start doing things

        try {

            //再次委托给ViewRootImpl完成界面添加过程

            root.setView(view, wparams, panelParentView, userId);

        catch (RuntimeException e) {

            // BadTokenException or InvalidDisplayException, clean up.

            if (index >= 0) {

                removeViewLocked(index, true);

            }

            throw e;

        }

    }

}


调用ViewRootImpl的setView方法继续该过程,

该方法内部调用requestLayout,进入三大过程,在此不展开分析,

@Override

public void requestLayout() {

    if (!mHandlingLayoutInLayoutRequest) {

        checkThread();

        mLayoutRequested = true;

        scheduleTraversals();

    }

}

最终通过mWindowSession(IWindowSession)完成添加过程,这是一次IPC过程。

res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,

        getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,

        mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,

        mAttachInfo.mDisplayCutout, inputChannel,

        mTempInsets, mTempControls);

/**

 * IWindowSession implementation. Currently this class doesn't need to support for multi-user.

 */

@Override

public int addToDisplayAsUser(IWindow window, int seq, WindowManager.LayoutParams attrs,

        int viewVisibility, int displayId, int userId, Rect outFrame,

        Rect outContentInsets, Rect outStableInsets,

        DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,

        InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {

    return addToDisplay(window, seq, attrs, viewVisibility, displayId,

            outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel,

            outInsetsState, outActiveControls);

}​

这样,Session内部通过WindowManagerService来实现了Window的添加。
WindowManagerService会为每个应用保存一个单独的Session。

---------------------------------------------------------------------------------------

3,WindowManager的使用

通过自定义一个Activity来使用wm提供的api,如下所示:

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main2)

    val button: Button = findViewById(R.id.button_2)

    // 这里初始化layoutParams,注意必须是WindowManager.LayoutParams

    mLayoutParams = WindowManager.LayoutParams(

        ViewGroup.LayoutParams.WRAP_CONTENT,

        ViewGroup.LayoutParams.WRAP_CONTENT,

        WindowManager.LayoutParams.TYPE_APPLICATION, //窗口类型,z-order的依据

        0,  //flag

        PixelFormat.TRANSPARENT

    )

    // 这些flag定义了窗口的特性

    mLayoutParams.flags =

        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or

                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or

                WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED

    //组件对齐方式

    mLayoutParams.gravity = Gravity.LEFT or Gravity.TOP

    // 组件位置

    mLayoutParams.x = 100

    mLayoutParams.y = 300

    val wm: WindowManager = this.getSystemService(Context.WINDOW_SERVICE) as WindowManager

    mTextView = TextView(this)

    mTextView.text = "I am TextView"

    // 直接addView

    wm.addView(mTextView, mLayoutParams)

    

    mTextView.setOnTouchListener(this)

}

 
---------------------------------------------------------------------------------------

4,Window的创建过程

对于Activity,在其attach()方法中,

mWindow = new PhoneWindow(this, window, activityConfigCallback);

mWindow.setWindowControllerCallback(mWindowControllerCallback);

mWindow.setCallback(this);

mWindow.setOnWindowDismissedCallback(this);

mWindow.getLayoutInflater().setPrivateFactory(this);


这里直接创建了一个PhoneWindow,如何设置一系列回调,如下:

private final WindowControllerCallback mWindowControllerCallback =

        new WindowControllerCallback(){

/**

 * Moves the activity between {@link WindowConfiguration#WINDOWING_MODE_FREEFORM} windowing

 * mode and {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}.

 *

 * @hide

 */

 @Override

 public void toggleFreeformWindowingMode() throws RemoteException {

ActivityTaskManager.getService().toggleFreeformWindowingMode(mToken);

 }

/**

 * Puts the activity in picture-in-picture mode if the activity supports.

 * @see android.R.attr#supportsPictureInPicture

 * @hide

 */

 @Override

 public void enterPictureInPictureModeIfPossible() {

if (mActivityInfo.supportsPictureInPicture()) {

enterPictureInPictureMode();

 }

}

@Override

 public boolean isTaskRoot() {

try {

return ActivityTaskManager.getService().getTaskForActivity(mToken, true) >= 0;

 catch (RemoteException e) {

return false;

 }

}

/**

 * Update the forced status bar color.

 * @hide

 */

 @Override

 public void updateStatusBarColor(int color) {

mTaskDescription.setStatusBarColor(color);

 setTaskDescription(mTaskDescription);

 }

/**

 * Update the forced navigation bar color.

 * @hide

 */

 @Override

 public void updateNavigationBarColor(int color) {

mTaskDescription.setNavigationBarColor(color);

 setTaskDescription(mTaskDescription);

 }

}

对于Dialog和Toast,创建过程类似,

比如Dialog的构造函数,

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {

    if (createContextThemeWrapper) {

        if (themeResId == Resources.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;

    }

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

    final Window w = new PhoneWindow(mContext);

    mWindow = w;

    w.setCallback(this);

    w.setOnWindowDismissedCallback(this);

    w.setOnWindowSwipeDismissedCallback(() -> {

        if (mCancelable) {

            cancel();

        }

    });

    w.setWindowManager(mWindowManager, nullnull);

    w.setGravity(Gravity.CENTER);

    mListenersHandler = new ListenersHandler(this);

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值