IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android窗口机制:七、Activity的Token和Dialog的关系。(源码版本SDK31) -> 正文阅读

[移动开发]Android窗口机制:七、Activity的Token和Dialog的关系。(源码版本SDK31)

Android 窗口机制 SDK31源码分析 总目录

前面几个章节已经搞清楚Activity的窗口加载过程,那么其他窗口呢?今天我们分析一下Dialog,搞清楚以下几个问题:

  1. token是什么?
  2. Dialog为什么一定需要Activity作为Context
  3. Dialog弹出后对于Activity生命周期有何影响
  4. 如何正确的设置到Dialog的宽高

前言

首先大家需要了解一下LayoutParams,当然属性很多,简单了解即可:

 		...
        //窗口类型
        //有3种主要类型如下:
        //ApplicationWindows取值在FIRST_APPLICATION_WINDOW与LAST_APPLICATION_WINDOW之间,是常用的顶层应用程序窗口,须将token设置成Activity的token;
        //SubWindows取值在FIRST_SUB_WINDOW和LAST_SUB_WINDOW之间,与顶层窗口相关联,需将token设置成它所附着宿主窗口的token;
        //SystemWindows取值在FIRST_SYSTEM_WINDOW和LAST_SYSTEM_WINDOW之间,不能用于应用程序,使用时需要有特殊权限,它是特定的系统功能才能使用;
        public int type;

        //WindowType:开始应用程序窗口
        public static final int FIRST_APPLICATION_WINDOW = 1;
        //WindowType:所有程序窗口的base窗口,其他应用程序窗口都显示在它上面
        public static final int TYPE_BASE_APPLICATION   = 1;
        //WindowType:普通应用程序窗口,token必须设置为Activity的token来指定窗口属于谁
        public static final int TYPE_APPLICATION        = 2;
        //WindowType:应用程序启动时所显示的窗口,应用自己不要使用这种类型,它被系统用来显示一些信息,直到应用程序可以开启自己的窗口为止
        public static final int TYPE_APPLICATION_STARTING = 3;
        //WindowType:结束应用程序窗口
        public static final int LAST_APPLICATION_WINDOW = 99;

        //WindowType:SubWindows子窗口,子窗口的Z序和坐标空间都依赖于他们的宿主窗口
        public static final int FIRST_SUB_WINDOW        = 1000;
        //WindowType: 面板窗口,显示于宿主窗口的上层
        public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;
        //WindowType:媒体窗口(例如视频),显示于宿主窗口下层
        public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;
        //WindowType:应用程序窗口的子面板,显示于所有面板窗口的上层
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
        //WindowType:对话框,类似于面板窗口,绘制类似于顶层窗口,而不是宿主的子窗口
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
        //WindowType:媒体信息,显示在媒体层和程序窗口之间,需要实现半透明效果
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4;
        //WindowType:子窗口结束
        public static final int LAST_SUB_WINDOW         = 1999;

        //WindowType:系统窗口,非应用程序创建
        public static final int FIRST_SYSTEM_WINDOW     = 2000;
        //WindowType:状态栏,只能有一个状态栏,位于屏幕顶端,其他窗口都位于它下方
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
        //WindowType:搜索栏,只能有一个搜索栏,位于屏幕上方
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
        //WindowType:电话窗口,它用于电话交互(特别是呼入),置于所有应用程序之上,状态栏之下
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
        //WindowType:系统提示,出现在应用程序窗口之上
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
        //WindowType:锁屏窗口
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
        //WindowType:信息窗口,用于显示Toast
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
        //WindowType:系统顶层窗口,显示在其他一切内容之上,此窗口不能获得输入焦点,否则影响锁屏
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
        //WindowType:电话优先,当锁屏时显示,此窗口不能获得输入焦点,否则影响锁屏
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
        //WindowType:系统对话框
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
        //WindowType:锁屏时显示的对话框
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
        //WindowType:系统内部错误提示,显示于所有内容之上
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
        //WindowType:内部输入法窗口,显示于普通UI之上,应用程序可重新布局以免被此窗口覆盖
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
        //WindowType:内部输入法对话框,显示于当前输入法窗口之上
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
        //WindowType:墙纸窗口
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
        //WindowType:状态栏的滑动面板
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
        //WindowType:安全系统覆盖窗口,这些窗户必须不带输入焦点,否则会干扰键盘
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
        //WindowType:拖放伪窗口,只有一个阻力层(最多),它被放置在所有其他窗口上面
        public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;
        //WindowType:状态栏下拉面板
        public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;
        //WindowType:鼠标指针
        public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;
        //WindowType:导航栏(有别于状态栏时)
        public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;
        //WindowType:音量级别的覆盖对话框,显示当用户更改系统音量大小
        public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
        //WindowType:起机进度框,在一切之上
        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
        //WindowType:假窗,消费导航栏隐藏时触摸事件
        public static final int TYPE_HIDDEN_NAV_CONSUMER = FIRST_SYSTEM_WINDOW+22;
        //WindowType:梦想(屏保)窗口,略高于键盘
        public static final int TYPE_DREAM = FIRST_SYSTEM_WINDOW+23;
        //WindowType:导航栏面板(不同于状态栏的导航栏)
        public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;
        //WindowType:universe背后真正的窗户
        public static final int TYPE_UNIVERSE_BACKGROUND = FIRST_SYSTEM_WINDOW+25;
        //WindowType:显示窗口覆盖,用于模拟辅助显示设备
        public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;
        //WindowType:放大窗口覆盖,用于突出显示的放大部分可访问性放大时启用
        public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;
        //WindowType:......
        public static final int TYPE_KEYGUARD_SCRIM           = FIRST_SYSTEM_WINDOW+29;
        public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
        public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;
        public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;
        //WindowType:系统窗口结束
        public static final int LAST_SYSTEM_WINDOW      = 2999;
        ......
    }

这里需要我们知道的是WindowManager.LayoutParams有三种窗口type,分别对应为:

  • 应用窗口程序:type值在FIRST_APPLICATION_WINDOW ~ LAST_APPLICATION_WINDOW必须将token设置为Activity的token。比如Dialog。
  • 子窗口: type值在FIRST_SUB_WINDOW ~ LAST_SUB_WINDOW SubWindows必须将token设置为Activity的token。比如PopupWindow。
  • 系统窗口: type值在FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW,使用需要权限,属于特定的系统功能。比如Toast。

这里就说到了token的问题,应用窗口程序和子窗口均需要获取到Activity的token。那么token是什么呢?

token

AMS(ActivityManagerService)ActivityThread之间的通信采用了token来对Activity进行标识,且WMS(WindowManagerService)会用token进行鉴别,只有符合条件的window才会被添加。

那么token是怎么保存的呢?

Activity
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, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
            IBinder shareableActivityToken) {
            ...
             mWindow = new PhoneWindow(this);
             mToken = token;
   			 mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
            ...
            }

从前面我知道ActivityThread通过调用performLaunchActivity来启动Activity,随后调用Activity的attach的方法,会传入token。Activity自己会保存一份,随后传入mWindow.setWindowManager中。

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
	//WindowManager,并且赋值当前的window给到parentWindow属性
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

setWindowManager方法中,token被赋值到Window的mAppToken属性上,同时在当前Window上创建了WindowManager。

接下来再看addView,在前面章节的分析中,我们知道addView最终在WindowManagerGlobal中进行了实现,

WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow, int userId) {
            
            final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
            if (parentWindow != null) {
                parentWindow.adjustLayoutParamsForSubWindow(wparams);
            } 
            ...
            }

由前面的parentWindow赋值情况我们知道,对于Activity启动流程来说,走到这里,parentWindow一定是不为null的。

其实:只有系统窗口,parentWindow才会为null。

window
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
        CharSequence curTitle = wp.getTitle();
        if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
            wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            if (wp.token == null) {
                View decor = peekDecorView();
                if (decor != null) {
                    //子窗口,则给到父window即Activity的token
                    wp.token = decor.getWindowToken();
                }
            }
            ...
        } else {
            if (wp.token == null) {
                //否则token则是自己
                wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
            }
           ...
        }
     ...
    }

这里会判断窗口类型,设置token。获取到Token后就保存在了LayoutParams里面,之后被传递到ViewRootImpl.setView中去。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                ...
                mWindowAttributes.copyFrom(attrs);
                ...
                res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
            }
        }

这里将包含token的LayoutParams通过Session最终调用到了WMSaddWindow方法(这些流程前面的章节都提到过,所以这里就简单带过)。

WindowManagerService
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
            int displayId, int requestUserId, InsetsState requestedVisibility,
            InputChannel outInputChannel, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls) {
            
            ...
            //通过token获取到DisplayContent,来判断是否是非法的显示内容
            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

            if (displayContent == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window to a display that does "
                        + "not exist: %d. Aborting.", displayId);
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }
            if (!displayContent.hasAccess(session.mUid)) {
                ProtoLog.w(WM_ERROR,
                        "Attempted to add window to a display for which the application "
                                + "does not have access: %d.  Aborting.",
                        displayContent.getDisplayId());
                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
            }

            if (mWindowMap.containsKey(client.asBinder())) {
                ProtoLog.w(WM_ERROR, "Window %s is already added", client);
                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
            } 
            ...
            //根据token获取activity,判断所添加的合法性
            activity = token.asActivityRecord();
            if (activity == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with non-application token "
                           + ".%s Aborting.", token);
                return WindowManagerGlobal.ADD_NOT_APP_TOKEN;
            } else if (activity.getParent() == null) {
                ProtoLog.w(WM_ERROR, "Attempted to add window with exiting application token "
                           + ".%s Aborting.", token);
                return WindowManagerGlobal.ADD_APP_EXITING;
            } else if (type == TYPE_APPLICATION_STARTING) {
                if (activity.mStartingWindow != null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                               + "token with already existing starting window");
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
                if (activity.mStartingData == null) {
                    ProtoLog.w(WM_ERROR, "Attempted to add starting window to "
                               + "token but already cleaned");
                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                }
            }
            ...
            
            }

以上,WMS会对token进行校验,只有合理的token才允许添加Window,否则返回相关的错误指标(判断的代码过于长,只是列出了一部分)。

那么这些返回值如何进行处理呢?别慌,看下面的代码。

              if (res < WindowManagerGlobal.ADD_OKAY) {
                    mAttachInfo.mRootView = null;
                    mAdded = false;
                    mFallbackEventHandler.setView(null);
                    unscheduleTraversals();
                    setAccessibilityFocus(null, null);
                    switch (res) {
                        case WindowManagerGlobal.ADD_BAD_APP_TOKEN:
                        case WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not valid; is your activity running?");
                        case WindowManagerGlobal.ADD_NOT_APP_TOKEN:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- token " + attrs.token
                                    + " is not for an application");
                        case WindowManagerGlobal.ADD_APP_EXITING:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- app for token " + attrs.token
                                    + " is exiting");
                        case WindowManagerGlobal.ADD_DUPLICATE_ADD:
                            throw new WindowManager.BadTokenException(
                                    "Unable to add window -- window " + mWindow
                                    + " has already been added");
                        case WindowManagerGlobal.ADD_STARTING_NOT_NEEDED:
                            // Silently ignore -- we would have just removed it
                            // right away, anyway.
                            return;
                        case WindowManagerGlobal.ADD_MULTIPLE_SINGLETON:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- another window of type "
                                    + mWindowAttributes.type + " already exists");
                        case WindowManagerGlobal.ADD_PERMISSION_DENIED:
                            throw new WindowManager.BadTokenException("Unable to add window "
                                    + mWindow + " -- permission denied for window type "
                                    + mWindowAttributes.type);
                        case WindowManagerGlobal.ADD_INVALID_DISPLAY:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified display can not be found");
                        case WindowManagerGlobal.ADD_INVALID_TYPE:
                            throw new WindowManager.InvalidDisplayException("Unable to add window "
                                    + mWindow + " -- the specified window type "
                                    + mWindowAttributes.type + " is not valid");
                        case WindowManagerGlobal.ADD_INVALID_USER:
                            throw new WindowManager.BadTokenException("Unable to add Window "
                                    + mWindow + " -- requested userId is not valid");
                    }
                    throw new RuntimeException(
                            "Unable to add window -- unknown error code " + res);
                }

而ViewRootImpl中也对返回的值进行了判断,如果token不合理,则直接抛出了相关的异常!!!

所以,简单总结一下,每个Activity都有一个自己的token,用于各种校验,而对于WMS来说,如果想添加非系统级别的窗口,都需要一个合理的token。

好了,有了上面的了解,我们分析一下其他的窗口,比如Dialog。

Dialog

先简单使用一下:

//创建dialog
val dialog = object : AppCompatDialog(this) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(TextView(this@LearnTestActivity).apply {
            text = "dialog simple test"
        })
    }
}

dialog.show()

//设置宽高
val phoneWindow = dialog.window
if (phoneWindow != null) {
    val wlp = phoneWindow.attributes
    wlp.gravity = Gravity.CENTER
    wlp.height = (obtainPhoneCurrentHeight(this@LearnTestActivity) * 0.5).toInt()
    wlp.width = (obtainPhoneCurrentWidth(this@LearnTestActivity) * 0.5).toInt()

    phoneWindow.attributes = wlp
}

Dialog使用起来很简单的,只需要创建dialog对象重写onCreate方法,设置自己的布局,然后使用的时候,调用show方法即可。但是构建Dialog对象的时候,传递给构造器的context必须为Activity。为什么呢?就和我们上面聊到的token有关系了,原因下面分析:

好,那么我们就从Dialog的构造函数看起来。PS:上面我使用的AppCompatDialog最终也是调用到了Dialog的构造器,所以直接看Dialog即可。

    //Dialog 
    Dialog(@UiContext @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;
        }
		//获取Activity的WindowManager
        mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
		//创建PhoneWindow
        final Window w = new PhoneWindow(mContext);
        mWindow = w;
        //设置布局更改之后的回调,比如我们上面设置了Window.LayoutParams。通过这个回调,就会调用到Dialog的onWindowAttributesChanged方法,然后具体执行相关的改变。
        w.setCallback(this);
        w.setOnWindowDismissedCallback(this);
        w.setOnWindowSwipeDismissedCallback(() -> {
            if (mCancelable) {
                cancel();
            }
        });
        //设置windowmanager
        w.setWindowManager(mWindowManager, null, null);
        w.setGravity(Gravity.CENTER);

        mListenersHandler = new ListenersHandler(this);
    }
	//获取WindowManager
	Activity
    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)) {
            return mWindowManager;
        } else if (SEARCH_SERVICE.equals(name)) {
            ensureSearchManager();
            return mSearchManager;
        }
        return super.getSystemService(name);
    }

整体流程和Activity的创建很相似,首先创建相关的主题资源,其次获取到Activity的WindowManager,之后创建自己的PhoneWindow,设置相关的监听回调等等。最后调用PhoneWindow的setWindowManager方法。但是注意,这是我们调用setWindowManager时,传入的token为null,也就是说,此时PhoneWindow内部的mAppToken属性为null

我们接下来看Dialog的show方法。

    public void show() {
        ...
        if (!mCreated) {
            //调用到onCreate 会调用到setContentView,之后会构建DecorView,自己的布局填充到DecorView上,并且被phoneWindow引用。和Activity的流程是一样的,这里就不再展开分析了。
            dispatchOnCreate(null);
        } else {
            // Fill the DecorView in on any configuration changes that
            // may have occured while it was removed from the WindowManager.
            final Configuration config = mContext.getResources().getConfiguration();
            mWindow.getDecorView().dispatchConfigurationChanged(config);
        }

        onStart();
        mDecor = mWindow.getDecorView();
		...
        WindowManager.LayoutParams l = mWindow.getAttributes();
       	...
        //因为添加的是应用程序窗口,所以在WindowManagerGlobel的addView中,会获取到parentWindow的token,即Activity的token。
        mWindowManager.addView(mDecor, l);
        ...

        mShowing = true;

        sendShowMessage();
    }

这里会调用到onCreate方法,构建布局。之后使用Activity的WindowManager调用了addView方法。之后的流程和Activity的添加是一致的**。因为这里拿到的是Activity的WindowManager,且WindowManager的构建类型为应用程序窗口,所以在WindowManagerGlobeladdView中,会获取到parentWindow的token,即Activity的token。所以Dialog才可以被添加。如果此时传递的是Appliocation或者是Service,则在ViewRootImpl.setView中会抛出token相关的错误异常,找不到对应的token**,所以这就是为什么使用Dialog我们必须传入Activity的原因。

Dialog流程,上面已经分析完毕了。

接下来回答最初的几个问题:

  1. token是什么?

    简单理解,Activity的标识符。

  2. Dialog为什么一定需要Activity作为Context

    因为需要依赖Activity的token进行构建。

  3. Dialog弹出后对于Activity生命周期有何影响

    分两种情况:

    • 如果弹出的是当前界面的Dialog,则当前Activity不会有任何的变化。
    • 如果是别的Activity的Dialog,则当前Activity会走onPause
  4. 如何正确的设置到Dialog的宽高

    这个像上面写的demo一样,需要等到调用dialog.show()方法之后才可以去设置WindowManager.LayoutParams的宽高,因为只有调用show()方法之后,decorview才不会为null,只有它不为null,才会进行真正的相关属性设置,具体实现代码如下所示:

    	Dialog
    	public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
            if (mDecor != null) {
                mWindowManager.updateViewLayout(mDecor, params);
            }
        }
    

创作不易,如有帮助一键三连咯🙆?♀?。欢迎技术探讨噢!

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-24 00:42:07  更:2022-03-24 00:44:59 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年9日历 -2024/9/20 22:40:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码