设备锁定时出现权限对话框

5
当我在Android中请求运行时权限时,权限对话框会出现。在这个阶段,如果我锁定设备并再次解锁手机,则警报对话框将出现在锁屏上。除非我按下接受或拒绝,否则它不会消失。 有什么方法可以避免这种行为吗?

enter image description here


1
我认为你的应用程序在请求权限后可能会崩溃。因此,权限对话框显示在锁定屏幕上。 - Tariqul Islam
你能在这里分享你的权限请求代码吗? - Hanzala
@TariqulIslam 我尝试了,但应用程序没有崩溃。 - Waqar UlHaq
1个回答

2

故事

在调查这个问题后,我发现以下内容:

当应用程序调用Activity.requestPermissions(String[], int)时,会调用该方法的源代码

public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
    if (requestCode < 0) {
        throw new IllegalArgumentException("requestCode should be >= 0");
    }
    if (mHasCurrentPermissionsRequest) {
        Log.w(TAG, "Can request only one set of permissions at a time");
        // Dispatch the callback with empty arrays which means a cancellation.
        onRequestPermissionsResult(requestCode, new String[0], new int[0]);
        return;
    }
    Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
    startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
    mHasCurrentPermissionsRequest = true;
}

我们可以看到,系统启动了一个新的活动。
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);

我追踪到源代码并发现该活动是GrantPermissionsActivity。它负责显示权限对话框并处理允许/拒绝/不再询问等事件。
有了这个提示,我使用以下adb命令来查看活动信息:

adb shell dumpsys activity activities

在调用requestPermissions()之前。
Running activities (most recent first):
  TaskRecord{e04b922d0 #1441 A=com.example.myapplication U=0 StackId=1 sz=1}
    Run #0: ActivityRecord{ca6ffc3 u0 com.example.myapplication/.MainActivity t1441}

mResumedActivity: ActivityRecord{ca6ffc3 u0 com.example.myapplication/.MainActivity t1441}

在调用 requestPermissions()

Running activities (most recent first):
  TaskRecord{e04b922d0 #1441 A=com.example.myapplication U=0 StackId=1 sz=2}
    Run #1: ActivityRecord{bb1aed2 u0 com.google.android.packageinstaller/com.android.packageinstaller.permission.ui.GrantPermissionsActivity t1441}
    Run #0: ActivityRecord{ca6ffc3 u0 com.example.myapplication/.MainActivity t1441}

mResumedActivity: ActivityRecord{bb1aed2 u0 com.google.android.packageinstaller/com.android.packageinstaller.permission.ui.GrantPermissionsActivity t1441}
mLastPausedActivity: ActivityRecord{ca6ffc3 u0 com.example.myapplication/.MainActivity t1441}

解决方案

我认为这里是解决问题的流程:

  • 当权限活动正在显示时,如果用户按下电源键,我们将记住应用程序完成权限活动的操作,并通过组合Intent.FLAG_ACTIVITY_CLEAR_TOPIntent.FLAG_ACTIVITY_SINGLE_TOP从后退堆栈中弹出权限活动。

  • 用户解锁手机后,我们检查权限活动是否已完成,然后再次显示权限活动(恢复用户离开活动时的最后状态)。

实现

因为应用程序无法使用Activity.onKeyDown(KeyEvent)KeyEvent.KEYCODE_POWER检测到用户按下电源键,所以我们将使用Intent.ACTION_SCREEN_ONIntent.ACTION_SCREEN_OFF代替。

步骤1。MyApplication.java

public class MyApplication extends Application {

    private boolean isScreenOn = false;

    private final BroadcastReceiver screenStateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            isScreenOn = intent.getAction().equals(Intent.ACTION_SCREEN_ON);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
        registerReceiver(screenStateReceiver, intentFilter);
    }

    public boolean isScreenOn() {
        return isScreenOn;
    }
}

步骤 2. activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="showMyLocation"
        android:text="Show My Location" />

</FrameLayout>

第三步。 MainActivity.java

public class MainActivity extends AppCompatActivity {

    // When the permission dialog is showing, if users press the Power Key
    // to lock the phone, then the onStop() of this activity will be called first,
    // then the system will send a broadcast with Intent.ACTION_SCREEN_OFF action.
    // The duration between 2 events is under 1 seconds.
    private static final long TIMEOUT_MS = 1000;

    // When onNewIntent() is called, we use this extra key to distinct with another events
    // which might invoke this method.
    private static final String EXTRA_IS_PERMISSION_DIALOG_DISMISS_WHEN_SCREEN_OF =
            "EXTRA_IS_PERMISSION_DIALOG_DISMISS_WHEN_SCREEN_OF";

    // Your location request information.
    private static final int LOCATION_REQUEST_CODE = 100;
    private static final String[] locationPermission = {Manifest.permission.ACCESS_FINE_LOCATION};
    private boolean isPermissionDialogShowing = false;

    private final Handler mainHandler = new Handler(Looper.getMainLooper());

    // Check whether permission activity is finished when screen off.
    boolean isPermissionActivityFinishedWhenScreenOff = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Bundle data = intent.getExtras();
        if (data != null && data.containsKey(EXTRA_IS_PERMISSION_DIALOG_DISMISS_WHEN_SCREEN_OF)) {
            isPermissionActivityFinishedWhenScreenOff = true;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Show the permission dialog again if we dismiss it when screen off before.
        MyApplication myApplication = (MyApplication) getApplication();
        if (myApplication.isScreenOn() && isPermissionActivityFinishedWhenScreenOff) {
            isPermissionActivityFinishedWhenScreenOff = false;
            isPermissionDialogShowing = true;
            requestPermissions(locationPermission, LOCATION_REQUEST_CODE);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();

        if (!isPermissionActivityFinishedWhenScreenOff) {
            mainHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    // Because the permission activity is on the top of this activity,
                    // so we will use Intent.FLAG_ACTIVITY_CLEAR_TOP combine with
                    // Intent.FLAG_ACTIVITY_SINGLE_TOP to finish the permission activity.
                    MyApplication myApplication = (MyApplication) getApplication();
                    if (!myApplication.isScreenOn() && isPermissionDialogShowing) {
                        Intent i = new Intent(MainActivity.this, MainActivity.class);
                        i.putExtra(EXTRA_IS_PERMISSION_DIALOG_DISMISS_WHEN_SCREEN_OF, true);
                        i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                        startActivity(i);
                    }
                }
            }, TIMEOUT_MS);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        isPermissionDialogShowing = false;
    }

    // User click on this button to show my location on the map for example.
    // This method is just for testing.
    public void showMyLocation(View view) {
        if (checkSelfPermission(locationPermission[0]) != PackageManager.PERMISSION_GRANTED) {
            isPermissionDialogShowing = true;
            requestPermissions(locationPermission, LOCATION_REQUEST_CODE);
        }
    }
}

Step 4. AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

1
非常感谢您的时间 @son-truong,您的发现非常有趣和有用! - Jesús Barrera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接