如何处理Activity和Fragment之外的权限请求?

How to handle permission requests outside Activity and Fragment?

我正在开发一个需要访问外部存储的自定义复合视图。如何在不涉及外部各方(即 Activity 或 Fragment)的情况下实现权限处理?

我知道我可以使用视图的上下文请求权限,但我如何处理视图内的 onRequestPermissionsResult()?有可能吗?

如果不可能,处理此类问题的最优雅的解决方案是什么?

只能在 Activity 和 Fragments 中使用。

您可以做的是在您的视图中复制 public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 并在上下文所在的 Activity 或片段中的相应方法中调用该方法。

I'm developing a custom compound View that needs to access external storage

恕我直言,这是一个架构错误。 View 用于向用户显示内容,有时用于收集低级输入事件并将它们转化为高阶结构(例如,点击、滑动)。 View 不应与文件、数据库等有任何联系。请参阅 MVC、MVP、MVVM 和类似的 GUI 架构模式。

WebView 不遵守此规定,结果导致问题(例如,在主应用程序线程上执行磁盘 I/O)。

How can I implement the permission handling without involving outside parties, i.e. Activity or Fragment?

你不能。请求权限是 activity 或片段的责任,大概是在您的视图需要此数据之前。

what would be the most elegant solution to handle something like this?

将此 View 的数据访问部分提取到由 activity 或片段管理的其他内容中,其中线程、权限和与该数据访问相关的其他工作可以是管理。

如果没有 activity 的实例,您将无法使用权​​限,但您可以使代码更漂亮。如果你想发送一个请求并在一个地方处理它,那么你可以使用下面的例子。

只要创建一些看起来像 BaseActivity 的东西,然后把这样的代码放在那里

public class PermActivity extends Activity {

    interface OnPermissionCallback{
        void requestResult(String[] permissions, int[] grantResults);
    }

    private SparseArray<OnPermissionCallback> permissionCallback = new SparseArray<>();

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        permissionCallback.get(requestCode).requestResult(permissions, grantResults);
    }

    public void addPermissionCallback(int requestCode, OnPermissionCallback  callback){
        permissionCallback.put(requestCode, callback);
    }
}

现在在我们的客户端代码中,我们可以做类似的事情

class SomeClasThatWorksWithPerms{

    private PermActivity activity;

    public SomeClasWorksWithPerms(PermActivity activity) {
        this.activity = activity;
    }

    void foo(){
        if (ContextCompat.checkSelfPermission(activity, WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
            // do something
        }else {
            activity.addPermissionCallback(0, (perms, grantResults) -> {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    foo(); // try one more
                }
            });
            activity.requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 0);
        }
    }
}

我已经通过请求代码使用了 spareArray 和索引,但您可以使用另一种存储回调的方式。

这是一个非常简单的例子,你可以在那里看到更严肃的东西 https://github.com/mrizyver/Fl_Launcher/blob/master/app/src/main/java/com/izyver/fllauncher/presentation/activity/FlActivity.kt - 如您所见,它是 activity https://github.com/mrizyver/Fl_Launcher/blob/master/app/src/main/java/com/izyver/fllauncher/presentation/loaders/WallpaperLoader.kt - 我们使用权限的客户端代码

让我们假设您需要在用户单击“确定”或其他按钮时从对话框片段调用 requestPermissionLauncher。这是在 MainActivity 中找到的 requestPermissionLauncher,或者您可以将其放在调用对话框片段的任何其他 activity 中。

public ActivityResultLauncher<String> requestPermissionLauncher =
        registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> {
            if (isGranted) {
                // Permission is granted. Continue the action or workflow in your
                // app.
               
            } else {
                // Explain to the user that the feature is unavailable because the
                // features requires a permission that the user has denied. At the
                // same time, respect the user's decision. Don't link to system
                // settings in an effort to convince the user to change their
                // decision.
               
            }
        });

这里是代码源,如果你想参考的话https://developer.android.com/training/permissions/requesting

然后在您的对话片段中使用以下代码调用实例 requestPermissionLauncher

((MainActivity)getContext()).requestPermissionLauncher.launch(Manifest.permission.[*your permission goes here*]);