Framework学习(十一)WindowManager体系(1),20道Android工程师面试必问题

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

(2)子窗口

子窗口,顾名思义,它不能独立的存在,需要附着在其他窗口才可以,PopupWindow就属于子窗口。子窗口的类型定义如下:

public static final int FIRST_SUB_WINDOW = 1000;//子窗口类型初始值

public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;

public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

public static final int LAST_SUB_WINDOW = 1999;//子窗口类型结束值

子窗口的Type值范围为1000到1999。

(3)系统窗口

Toast、输入法窗口、系统音量条窗口、系统错误窗口都属于系统窗口。系统窗口的类型定义如下:

public static final int FIRST_SYSTEM_WINDOW = 2000;//系统窗口类型初始值

public static final int TYPE_STATUS_BAR = FIRST_SYSTEM_WINDOW;//系统状态栏窗口

public static final int TYPE_SEARCH_BAR = FIRST_SYSTEM_WINDOW+1;//搜索条窗口

public static final int TYPE_PHONE = FIRST_SYSTEM_WINDOW+2;//通话窗口

public static final int TYPE_SYSTEM_ALERT = FIRST_SYSTEM_WINDOW+3;//系统ALERT窗口

public static final int TYPE_KEYGUARD = FIRST_SYSTEM_WINDOW+4;//锁屏窗口

public static final int TYPE_TOAST = FIRST_SYSTEM_WINDOW+5;//TOAST窗口

public static final int LAST_SYSTEM_WINDOW = 2999;//系统窗口类型结束值

系统窗口的Type值范围为2000到2999。

Window的标志


Window的标志也就是Flag,用于控制Window的显示,同样被定义在WindowManager的内部类LayoutParams中,一共有20多个,这里我们给出几个比较常用的。

// 只要窗口可见,就允许在开启状态的屏幕上锁屏.

public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001;

//窗口不能获得输入焦点,设置该标志的同时,FLAG_NOT_TOUCH_MODAL也会被设置

public static final int FLAG_NOT_FOCUSABLE = 0x00000008;

//窗口不接收任何触摸事件

public static final int FLAG_NOT_TOUCHABLE = 0x00000010;

//在该窗口区域外的触摸事件传递给其他的Window,而自己只会处理窗口区域内的触摸事件

public static final int FLAG_NOT_TOUCH_MODAL = 0x00000020;

//只要窗口可见,屏幕就会一直亮着

public static final int FLAG_KEEP_SCREEN_ON = 0x00000080;

//允许窗口超过屏幕之外

public static final int FLAG_LAYOUT_NO_LIMITS = 0x00000200;

//隐藏所有的屏幕装饰窗口,比如在游戏、播放器中的全屏显示

public static final int FLAG_FULLSCREEN = 0x00000400;

//窗口可以在锁屏的窗口之上显示

public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;

//当用户的脸贴近屏幕时(比如打电话),不会去响应此事件

public static final int FLAG_IGNORE_CHEEK_PRESSES = 0x00008000;

//窗口显示时将屏幕点亮

public static final int FLAG_TURN_SCREEN_ON = 0x00200000;

设置Window的Flag有三种方法,第一种是通过Window的addFlags方法:

Window mWindow =getWindow();

mWindow.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

第二种通过Window的setFlags方法,其实内部还是会调用setFlags方法:

Window mWindow =getWindow();

mWindow.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);

第三种则是给LayoutParams设置Flag,并通过WindowManager的addView方法进行添加:

WindowManager.LayoutParams mWindowLayoutParams = new WindowManager.LayoutParams();

mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;

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

TextView mTextView=new TextView(this);

mWindowManager.addView(mTextView, mWindowLayoutParams);

软键盘相关模式


窗口和窗口的叠加是非常常见的场景,但如果其中的窗口是软键盘窗口,可能就会出现一些问题,比如典型的用户登录界面,默认的情况弹出的软键盘窗口可能会盖住输入框,这样用户体验会非常糟糕。

为了使得软键盘窗口能够按照期望来显示,WindowManager的静态内部类LayoutParams中还定义了软键盘相关模式,这里给出常用的几个:

//没有指定状态,系统会选择一个合适的状态或依赖于主题的设置

public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;

//不会改变软键盘状态

public static final int SOFT_INPUT_STATE_UNCHANGED = 1;

//当用户进入该窗口时,软键盘默认隐藏

public static final int SOFT_INPUT_STATE_HIDDEN = 2;

//当窗口获取焦点时,软键盘总是被隐藏

public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;

//当软键盘弹出时,窗口会调整大小

public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;

//当软键盘弹出时,窗口不需要调整大小,要确保输入焦点是可见的

public static final int SOFT_INPUT_ADJUST_PAN = 0x20;

从上面给出的SoftInputMode ,可以发现,它们与AndroidManifest中Activity的属性android:windowSoftInputMode是对应的。因此,除了在AndroidMainfest中为Activity设置android:windowSoftInputMode以外还可以在Java代码中为Window设置SoftInputMode:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);

Window的添加过程


WindowManager对Window的操作最终都是交由WMS来进行处理。窗口的操作分为两大部分,一部分是WindowManager处理部分,另一部分是WMS处理部分。我们知道Window分为三大类,分别是:Application Window(应用程序窗口)、Sub Windwow(子窗口)和System Window(系统窗口),对于不同类型的窗口添加过程会有所不同,但是对于WMS处理部分,添加的过程基本上是一样的。

无论是哪种窗口,它的添加过程在WMS处理部分中基本是类似的,只不过会在权限和窗口显示次序等方面会有些不同。但是在WindowManager处理部分会有所不同,这里以最典型的应用程序窗口Activity为例。

Activity在启动过程中,会调用ActivityThread的handleLaunchActivity()方法,具体可以参考Framework学习(五)应用程序启动过程这篇文章。

frameworks/base/core/java/android/app/ActivityThread.java

ActivityThread#handleLaunchActivity()

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

Activity a = performLaunchActivity(r, customIntent); //1

if (a != null) {

r.createdConfig = new Configuration(mConfiguration);

reportSizeConfigurations®;

Bundle oldState = r.state;

//2

handleResumeActivity(r.token, false, r.isForward, !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

if (!r.activity.mFinished && r.startsNotResumed) {

performPauseActivityIfNeeded(r, reason);

if (r.isPreHoneycomb()) {

r.state = oldState;

}

}

} else {

}

}

注释1处的performLaunchActivity方法用来启动Activity。

注释2处的代码用来添加Activity的window窗口。

ActivityThread#performLaunchActivity()

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

//1

activity.attach(appContext, this, getInstrumentation(), r.token, r.ident, app, r.intent, r.activityInfo, title, r.parent, r.embeddedID, r.lastNonConfigurationInstances, config, r.referrer, r.voiceInteractor, window);

return activity;

}

注释1调用Activity的attach方法。

frameworks/base/core/java/android/app/Activity.java

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

mFragments.attachHost(null /parent/);

mWindow = new PhoneWindow(this, window); //1

//2

mWindow.setWindowManager(

(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),

mToken, mComponent.flattenToString(),

(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);

}

注释1处创建了PhoneWindow。

注释2处调用了PhoneWindow的setWindowManager方法,这个方法的具体的实现在PhoneWindow的父类Window中。

frameworks/base/core/java/android/view/Window.java

Window#setWindowManager()

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

}

mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);//2

}

注释1处如果传入的WindowManager为null,就会调用Context的getSystemService方法,并传入服务的名称Context.WINDOW_SERVICE(”window”),Context的实现类是ContextImpl,具体的实现在ContextImpl中。

frameworks/base/core/java/android/app/ContextImpl.java

ContextImpl#getSystemService()

@Override

public Object getSystemService(String name) {

return SystemServiceRegistry.getSystemService(this, name);

}

frameworks/base/core/java/android/app/SystemServiceRegistry.java

SystemServiceRegistry#getSystemService()

private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap

public static Object getSystemService(ContextImpl ctx, String name) {

ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);

return fetcher != null ? fetcher.getService(ctx) : null;

}

SYSTEM_SERVICE_FETCHERS是一个HashMap类型的数据,它是用来存储服务的,这里是取得地方,那存的地方在哪里呢?找啊找,终于在SystemServiceRegistry 的静态代码块中找到了。

final class SystemServiceRegistry {

private SystemServiceRegistry() { }

static {

registerService(Context.WINDOW_SERVICE, WindowManager.class,

new CachedServiceFetcher() {

@Override

public WindowManager createService(ContextImpl ctx) {

return new WindowManagerImpl(ctx);

}});

}

}

SystemServiceRegistry#registerService()

private static void registerService(String serviceName, Class serviceClass,

ServiceFetcher serviceFetcher) {

SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);

SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); //1

}

注释1处就是将创建的服务添加到SYSTEM_SERVICE_FETCHERS中的。

从上面代码可以看出,传入的Context.WINDOW_SERVICE对应的就是WindowManagerImpl实例,因此得出结论,Context的getSystemService方法得到的是WindowManagerImpl实例。我们再回到Window的setWindowManager方法,在注释1处得到WindowManagerImpl实例后转为WindowManager类型,在注释2处调用了WindowManagerImpl的createLocalWindowManager方法。

frameworks/base/core/java/android/view/WindowManagerImpl

WindowManagerImpl#createLocalWindowManager()

public WindowManagerImpl createLocalWindowManager(Window parentWindow) {

return new WindowManagerImpl(mContext, parentWindow);

}

createLocalWindowManager方法同样也是创建WindowManagerImpl,不同的是这次创建WindowManagerImpl时将创建它的Window作为参数传了进来,这样WindowManagerImpl就持有了Window的引用,就可以对Window进行操作。比如

在Window中添加View,来查看WindowManagerImpl的addView方法。

WindowManagerImpl#addView()

@Override

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

applyDefaultToken(params);

mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); //1

}

注释1处调用了WindowManagerGlobal的addView方法,其中最后一个参数mParentWindow就是Window,可以看出WindowManagerImpl虽然是WindowManager的实现类,但是却没有实现什么功能,而是将功能实现委托给了WindowManagerGlobal。

来查看WindowManagerImpl中如何定义的WindowManagerGlobal:

public final class WindowManagerImpl implements WindowManager {

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

private final Context mContext;

private final Window mParentWindow; //2

private WindowManagerImpl(Context context, Window parentWindow) {

mContext = context;

mParentWindow = parentWindow;

}

}

注释1可以看出WindowManagerGlobal是一个单例,说明在一个进程中只有一个WindowManagerGlobal实例。

注释2处说明WindowManagerImpl可能会实现多个Window,也就是说在一个进程中WindowManagerImpl可能会有多个实例。

Window和WindowManager的关系如下图所示。

PhoneWindow继承自Window,Window通过setWindowManager方法与WindowManager发生关联。WindowManager继承自接口ViewManager,WindowManagerImpl是WindowManager接口的实现类,但是具体的功能都会委托给WindowManagerGlobal来实现。

再次回到最初的ActivityThread#handleLaunchActivity()方法。注释2处调用了handleResumeActivity。

ActivityThread#handleResumeActivity()

final void handleResumeActivity(IBinder token,

boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {

r = performResumeActivity(token, clearHide, reason);//1

if (r.window == null && !a.mFinished && willBeVisible) {

r.window = r.activity.getWindow();

View decor = r.window.getDecorView();

decor.setVisibility(View.INVISIBLE);

ViewManager wm = a.getWindowManager();//2

WindowManager.LayoutParams l = r.window.getAttributes();

a.mDecor = decor;

l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;

l.softInputMode |= forwardBit;

if (r.mPreserveWindow) {

a.mWindowAdded = true;

r.mPreserveWindow = false;

ViewRootImpl impl = decor.getViewRootImpl();

if (impl != null) {

impl.notifyChildRebuilt();

}

}

if (a.mVisibleFromClient && !a.mWindowAdded) {

a.mWindowAdded = true;

wm.addView(decor, l);//3

}

}

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

欢迎大家一起交流讨论啊~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

.softInputMode |= forwardBit;

if (r.mPreserveWindow) {

a.mWindowAdded = true;

r.mPreserveWindow = false;

ViewRootImpl impl = decor.getViewRootImpl();

if (impl != null) {

impl.notifyChildRebuilt();

}

}

if (a.mVisibleFromClient && !a.mWindowAdded) {

a.mWindowAdded = true;

wm.addView(decor, l);//3

}

}

最后

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

[外链图片转存中…(img-Jn0RPkKi-1713473426818)]

欢迎大家一起交流讨论啊~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-6JLBZqRX-1713473426818)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值