Android关于Activity屏蔽/拦截Home键
写在前面:
这篇文章并没有提供屏蔽Home键的方法,仅仅是阐释一些原理,引发一些思考。
1.奇淫技巧的源泉:PhoneWindowManager#interceptKeyBeforeDispatching
拦截home键的思想大致由此发源。Input事件分发时,Service端就会过滤了一些事件,而Home的过滤,就在此方法。方法注释说明了,如果app是keyguard类型,则将home键事件派发给app。源码(API 19)如下:
if (keyCode == KeyEvent.KEYCODE_HOME) { ... // If a system window has focus, then it doesn't make sense // right now to interact with applications. WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null; if (attrs != null) { final int type = attrs.type; if (type == WindowManager.LayoutParams.TYPE_KEYGUARD || type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) { // the "app" is keyguard, so give it the key return 0; } } ... }
2.传说中的在onAttachedToWindow中调用getWindow().setType设置Window Type
方式如下:
@Override public void onAttachedToWindow() { getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD); super.onAttachedToWindow(); }
本人基于4.4(API 19)及以上系统版本进行测试,都报异常:
java.lang.IllegalArgumentException: Window type can not be changed after the window is added.
所以,这种方式并不可行。
并且,如果你的compileSdkVersion >= 21,TYPE_KEYGUARD这种Window类型都没有了……
3.思考IllegalArgumentException: Window type can not be changed after the window is added
报完错之后有种想法,既然在onAttachedToWindow方法中设置type不行,那在onCreate中或者onResume中,这两个生命周期方法调用时期早于onAttachedToWindow,可以一试。
然而发现并不行。在onKeyDown中打个断点,发现Window的type是TYPE_BASE_APPLICATION,也就是说,之前虽然该了type,但是没有生效,或者说,在我更改了type之后,系统又进行了更改,于是进入了下一步,撸源码。
4.何处设置了TYPE_BASE_APPLICATION
Activity Window从产生到WindowMnager#addView被调用,详细过程请自行查找,网上有较多的阐释。
我需要了解的核心大致如下:
在Activity#attach方法中产生PhoneWindow。
在ActivityThread#handleResumeActivity将DecorView添加到WindowManager。源码(API 19)如下:
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(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l); } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. }
好吧,找到了原因:add前,android“帮你”重设了type。
5.另有蹊径:WindowManager.LayoutParams privateFlags ???
撸Android 5.1(API 21)源码,发现它的PhoneWindowManager#interceptKeyBeforeDispatching方法home键事件处理逻辑与Android 4.4(API 19)有些许不同,如下:
if (keyCode == KeyEvent.KEYCODE_HOME) { ... // If a system window has focus, then it doesn't make sense // right now to interact with applications. WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null; if (attrs != null) { final int type = attrs.type; if (type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) { // the "app" is keyguard, so give it the key return 0; } } ... }
于是乎想,能不能在这个privateFlags上做文章。
WindowManager.LayoutParams中,privateFlags和PRIVATE_FLAG_KEYGUARD虽然是public的,但是是@hide注解的,所以访问不到,需要通过反射取值和修改,代码如下:
public static void changeWindowPrivateFlags(Window window) { WindowManager.LayoutParams attrObj = window.getAttributes(); Class<?> attrCls = attrObj.getClass(); try { //拿PRIVATE_FLAG_KEYGUARD的值 Field privateFlagConstField = attrCls.getField("PRIVATE_FLAG_KEYGUARD"); privateFlagConstField.setAccessible(true); int privateFlagConst = privateFlagConstField.getInt(attrCls); //设置privateFlags为PRIVATE_FLAG_KEYGUARD Field field = attrCls.getField("privateFlags"); field.setAccessible(true); field.setInt(attrObj, privateFlagConst); } catch (Exception e) { e.printStackTrace(); } }
在Activity onCreate中调用这个方法。好吧,还是无效。但奇怪的是,我在onKeyDown中打断点,发现privateFlags确实等于PRIVATE_FLAG_KEYGUARD。然后就想不通了,如果博友们有通了的,麻烦回复下,感谢!
至此,拦截home宣告失败,但明晰了一些原理,还是颇有收获的。
敢于怀疑,勇于探索。
更多相关文章
- Unity头像上传功能实现 二
- android中actionBar中字体颜色设置
- 【Android(安卓)UI设计与开发】第07期:底部菜单栏(二)Fragment的详
- Android(安卓)使用SQLiteDatabase操作SQLite数据库(一)
- Android笔记之广播Broadcast
- android 与服务器用 http Post方法通迅
- uniapp原生插件开发之调用原生方法(android)
- 初识Android(安卓)回调机制
- android 笔记 --- 相机应用