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

}

【Android】Android Window相关推荐

  1. 【OkHttp】Android 项目导入 OkHttp ( 配置依赖 | 配置 networkSecurityConfig | 配置 ViewBinding | 代码示例 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  2. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  3. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  4. 【Android】Android 集成商米内置打印机打印票据

    文章目录 [Android]Android 集成商米内置打印机打印票据 1.集成商米打印依赖 2.规范接口接口 3.使用到的相关对象以及工具类 4.MainActivity初始化接口 5.Uniapp ...

  5. 【Android】Android 集成佳博80打印机打印票据

    文章目录 [Android]Android 集成佳博80打印机打印票据 1.集成佳博80打印机依赖 2.规范调用接口 3.使用到的相关对象以及工具类 4.MainActivity初始化接口 5.Uni ...

  6. 【Android】Android 集成商米钱箱

    文章目录 [Android]Android 集成商米钱箱 1.集成商米打印依赖 2.规范调用接口 3.MainActivity初始化接口 4.Uniapp调用方法 技术分享区 [Android]And ...

  7. 【翻译】Android Support Library Features(二)

    原文地址:http://developer.android.com/tools/support-library/features.html 在Android Support Library包中,包含了 ...

  8. 【朝花夕拾】Android性能篇之(二)Java内存分配

    前言       原文:[朝花夕拾]Android性能篇之(二)Java内存分配        在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给 ...

  9. 【译】Android系统简介—— Activity

    续上一篇,继续介绍Android系统.上一篇: [译]Android系统简介 本文主要介绍构建Android应用的一些主要概念: Activity Activity是应用程序中一个单独的有UI的页面( ...

  10. 【Android】Android 设置Activity窗体 不显示标题和全屏显示

    [一]Android 设置Activity窗体 不显示标题 android:theme="@android:style/Theme.NoTitleBar" 1 <activi ...

最新文章

  1. 二维指针删除单向链表
  2. 机器学习--线性回归、逻辑回归
  3. SAP为什么并购sybase
  4. php在数据流(内存)中操纵远程数据
  5. 定积分在计算机中的应用,计算机模拟定积分的定义
  6. 体验QQ2011Beta3带来的变化和惊喜
  7. 数据标注——VoTT的学习笔记
  8. 大数据技术 - 学习之路(一)
  9. 袋鼠云数智之旅·上海站|探索“十四五”智慧校园新图景
  10. 使用TraceView+实际案列实战性能分析 找出android app UI卡顿原因
  11. 好用的android剪辑软件,最好用的视频剪辑app软件有哪些?自媒体人都在用的六款app软件...
  12. 《上海悠悠接口自动化平台》-3.流程性用例,有关联的接口如何写?
  13. java pgm_用Java读取pgm文件
  14. 最全的100个Python精选库,建议收藏!
  15. 论文阅读《Direct Sparse Odometry》2
  16. FileExistsError: [WinError 183] 当文件已存在时,无法创建该文件。: 'D:/tjn/tra
  17. 诺贝尔物理奖候选人张首晟:区块链技术是互联网世界新的分合转折点
  18. 【陈工笔记】# latex中如何将图片并排处理 #
  19. How to modify comment and attribute ClearCase
  20. JS图片灯箱(lightBox)效果基本原理和demo

热门文章

  1. html5 css3在线工具,HTML5/CSS3开发辅助工具(TopStyle)
  2. 家里装电线时,为啥说“走顶”比“走地”好
  3. java 线程与线程池详解
  4. 全美航班停飞原因曝光/ 米哈游辟谣年终奖108薪/ 苹果正开发触摸屏Mac…今日更多新鲜事在此...
  5. bedgraph文件转bigwig文件
  6. 计算机处理器的CPU主频与指令条数
  7. unity给定两个点和中间点的其中一个坐标比如z坐标,求该点的坐标
  8. Axure9的使用经验分享:如何高效的实现弹窗交互模型
  9. B. Luxurious Houses
  10. 酷狗音乐能拉进计算机里面吗,手机上的酷狗音乐怎么传到电脑上|苹果手机酷狗音乐怎么传到电脑上|酷狗音乐电脑和手机怎么同步...