Android 8.0 悬浮窗权限开启延迟问题记录

年末无力症继续侵袭而来,临界年关项目又适配到了最新的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" is ON for the entire time these logs were being produced.

So why does the call to Settings.canDrawOverlays(Physical.this) return false in onStop, onActivityResult, onStart, onResume, and onPostResume?

辣么仔细看到这里的小伙伴们应该不难发现这位同学在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的圆胸终于可以被干掉了

你可能感兴趣的:(Android 8.0 悬浮窗权限开启延迟问题记录)