年末无力症继续侵袭而来,临界年关项目又适配到了最新的8.0。然鹅一大波剧即将到来,这里仅记录其中一种,那就是:
Android O 开启悬浮窗权限之后,返回应用使用Settings.canDrawOverlays()
发现我们手动开启的权限并没有生效,生效,生效,效,效,,,(此处一个懵逼脸What The FXXK)
但是,更精彩的还在后边,为什么我退出界面然后再回来在判断,这个权限又生效了呢,生效了,笑了,,,我真的笑了。
那么我们还是来探究下这个究竟是什么原因。
First try
万能的谷大神快显灵~~~
一波APM达到300的手速过后,终于在关键字查找找到了:
这个问题的探索一(科学上网人人必备)
那么对于没办法科学上网的同学,一下是原文照抄:(如果你不想看这个直接跳过到solution吧)
I have migrated my Android application try Android O (26).
I have enrolled my supported device in the Android Beta Program, and I am testing on a Google PIXEL XL (OPP3.170518.006)
Functionality within my application requires "Display over other apps".
The process flow is as follows:-
- User attempts to employ a feature that requires "Display over other apps" and it hasnt been granted.
- The Activity requests the permission as follows:-
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, RC_PERMISSION_MANAGE_OVERLAY_PERMISSION);
- within
onActivityResult
I check the permission was granted by the user and start my associated process.
This sequence of events worked fine until migrating to Android O.
The only way I can get this to work now is to use a Handler with postDelay of 3 seconds from within onActivityResult() to start my associated process.
How can I detect the granted permission immediately and not have to wait for 3 seconds?
I have developed the following research application to illustrate this issue
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_physical);
img = findViewById(R.id.spring_button);
img.setOnClickListener(this);
Log.d(TAG, "onCreate: " + Settings.canDrawOverlays(Physical.this));
}
@Override
public void onClick(View view) {
final Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, RC_PERMISSION_MANAGE_OVERLAY_PERMISSION);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
Log.d(TAG, "onPostCreate: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onPostResume() {
super.onPostResume();
Log.d(TAG, "onPostResume: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: " + Settings.canDrawOverlays(Physical.this));
}
@Override
public void onBackPressed() {
super.onBackPressed();
Log.d(TAG, "onBackPressed: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume: " + Settings.canDrawOverlays(Physical.this));
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RC_PERMISSION_MANAGE_OVERLAY_PERMISSION:
Log.d(TAG, "onActivityResult: " + Settings.canDrawOverlays(Physical.this));
break;
}
}
The Logcat output is as follows:
D/Physical: onCreate: true
D/Physical: onStart: true
D/Physical: onPostCreate: true
D/Physical: onResume: true
D/Physical: onPostResume: true
D/Physical: onPause: true
D/Physical: onStop: false
D/Physical: onActivityResult: false
D/Physical: onStart: false
D/Physical: onResume: false
D/Physical: onPostResume: false
My application has been granted the "
Display over other apps
" permission, e.g. the toggle switch associated with "Allow display over other apps
" isON
for the entire time these logs were being produced.
So why does the call to
Settings.canDrawOverlays(Physical.this)
return false inonStop
,onActivityResult
,onStart
,onResume
, andonPostResume
?
辣么仔细看到这里的小伙伴们应该不难发现这位同学在activity的生命周期里边都打印了权限的开启关闭状态。一开始都是OK的,肿么到了onStop()
就GG了呢,原因我也没找到(另外一个文章的小哥哥已经把这个BUG提交给Google了,同时Google也回复了表示会修复这个问题)。那么在这段时间有什么方法可以解决呢?哈哈哈,不要急,接下来就是解决方法了。
Solutions:
正确的解决方式(科学上网人人必备)
没办法科学上网?没关系:
重点来了:
AppOpsManager appOpsMgr = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
int mode = appOpsMgr.checkOpNoThrow("android:system_alert_window", android.os.Process.myUid(), getPackageName());
Log.d(TAG, "android:system_alert_window: mode=" + mode);
这个mode的值就可以判断是否开启了权限。
- when the application does not have this permission, the mode is "2" (MODE_ERRORED) (canDrawOverlays() returns false) when the user
- granted permission and returned to the application, the mode is "1" (MODE_IGNORED) (canDrawOverlays() returns false)
- and if you now reopen the app, the mode is "0" (MODE_ALLOWED) (canDrawOverlays() returns true)
简单来讲,这个mode 0、1 对应的是开启,2对应了关闭。但是在为1的时候,可能使用Settings.canDrawOverlays(context)
方法返回的是false。
辣么我们开启权限返回应用,再次监测权限返回false的圆胸终于可以被干掉了