Android Marshmallow如何请求权限?

202

我目前正在开发一个需要多个“危险”权限的应用程序。因此,我尝试在Android Marshmallow(API级别23)中添加“请求权限”,但无法找到如何执行的方法。

如何在我的应用程序中使用新的权限模型请求权限?


1
看这里,这就是你需要的全部内容:https://developer.android.com/training/permissions/requesting.html - Thomas R.
1
请看这个链接:http://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en - Dory
你有没有看过Google在GitHub上的示例代码库? - IgorGanapolsky
当时我刚接触安卓开发,看了developer.android.com/training/permissions/requesting.html这个网页,后来安卓6.0发布了,Google的培训让我更加困惑,而且找不到任何相关教程。 - Nilabja
https://dev59.com/G1sW5IYBdhLWcg3wSFnX#52708289 - HAXM
显示剩余2条评论
26个回答

4
这可能是一种更简洁的方式。将所有权限添加到一个数组中,如下所示:
private static final String[] INITIAL_PERMS={
            android.Manifest.permission.ACCESS_FINE_LOCATION,
            android.Manifest.permission.ACCESS_COARSE_LOCATION
    };
    private static final int INITIAL_REQUEST=1337;

无论您的权限是什么,都需要为每个权限创建一个方法。
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean canAccessFineLocation() {
    return(hasPermission(Manifest.permission.ACCESS_FINE_LOCATION));
}

@RequiresApi(api = Build.VERSION_CODES.M)
private boolean canAccessCoarseLocation() {
    return(hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION));
}

@RequiresApi(api = Build.VERSION_CODES.M)
private boolean hasPermission(String perm) {
    return(PackageManager.PERMISSION_GRANTED == checkSelfPermission(perm));
}

onCreate方法中调用此方法。

 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
      if(!canAccessCoarseLocation() || !canAccessFineLocation()){
            requestPermissions(INITIAL_PERMS, INITIAL_REQUEST);
        }
 }

现在重写 onRequestPermissionsResult 方法。
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

    if(requestCode == INITIAL_REQUEST){
        if (canAccessFineLocation() && canAccessCoarseLocation())  {
            //call your method
        }
        else {
            //show Toast or alert that this permissions is neccessary
        }
    }
}

1
我的活动中没有requireApi,我使用了TragetApi注释,导致出现错误。 - Navid_pdp11
@Jawad,这个可以工作,但是如果你添加一些被拒绝的权限回调会更好。 - binrebin

4
如果您正在使用 AndroidX Activity 1.2.0AndroidX Fragment 1.3.0,则可以使用新的活动结果 API 请求权限:
val launcher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
    if (isGranted) {
        // Permission granted. Do the tasks.
    }
}
launcher.launch(Manifest.permission.ACCESS_FINE_LOCATION)

警告!截至2020年9月,这两个库仍处于alpha版本。 - bavaza
仍然不稳定,因此我不建议使用此功能。 - ShadeToD

3

您可以使用此方法一次性获取多个权限。这对我很有帮助。我还有另一种解决方案。如果您将targetSdkVersion设置为22以下,则对我有效,并且其行为类似于从manifest.xml中获取权限。经过测试,它对我有效。

final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;

    private void insertDummyContactWrapper() {
        List<String> permissionsNeeded = new ArrayList<String>();

        final List<String> permissionsList = new ArrayList<String>();
        if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
            permissionsNeeded.add("GPS");
        if (!addPermission(permissionsList, Manifest.permission.READ_CONTACTS))
            permissionsNeeded.add("Read Contacts");
        if (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))
            permissionsNeeded.add("Write Contacts");

        if (permissionsList.size() > 0) {
            if (permissionsNeeded.size() > 0) {
                // Need Rationale
                String message = "You need to grant access to " + permissionsNeeded.get(0);
                for (int i = 1; i < permissionsNeeded.size(); i++)
                    message = message + ", " + permissionsNeeded.get(i);
                showMessageOKCancel(message,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                        REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                            }
                        });
                return;
            }
            requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                    REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
            return;
        }

        insertDummyContact();
    }

    private boolean addPermission(List<String> permissionsList, String permission) {
        if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
            permissionsList.add(permission);
            // Check for Rationale Option
            if (!shouldShowRequestPermissionRationale(permission))
                return false;
        }
        return true;
    }






@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
                {
                Map<String, Integer> perms = new HashMap<String, Integer>();
                // Initial
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED);
                // Fill with results
                for (int i = 0; i < permissions.length; i++)
                    perms.put(permissions[i], grantResults[i]);
                // Check for ACCESS_FINE_LOCATION
                if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                        && perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED
                        && perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
                    // All Permissions Granted
                    insertDummyContact();
                } else {
                    // Permission Denied
                    Toast.makeText(MainActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT)
                            .show();
                }
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }

更多细节请查看下面的链接

https://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en


将targetSdkVersion设置为22以下可以工作,但如果用户在Android M中从设置中拒绝了一个权限,会发生什么? - Nilabja
是的,你说得对,有时应用程序会崩溃。因此,在这种情况下,您需要再次检查用户是否拒绝了权限。我没有测试过,但我希望它能够正常工作。对于用户的每个操作,最好检查是否已授予权限。 - Md Tariqul Islam

2
以下代码完美运行。我将通过一个例子来解释。
在我的情况下,我将权限检查单独放在一个工具类中,并从相应的类中传递需要检查的特定权限。这使得可以在整个应用程序中重复使用权限检查工具文件。
以下代码部分显示了函数调用。在这种情况下,我正在请求 android.Manifest.permission.READ_EXTERNAL_STORAGE 权限。
//the below call is from a fragment
     @OnClick(R.id.button)//butterknife implementation
        public void attachPressed() {
            if (PermissionUtils.hasThisPermission(getContext(), android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
                onAttachPressed();
            } else {
                PermissionUtils.isPermissionRequestNeeded(getActivity(), this, android.Manifest.permission.READ_EXTERNAL_STORAGE, PermissionUtils.REQUEST_GROUP_STORAGE);
            }
        }   

在上述情况中,如果允许权限,则调用onAttachPressed();函数,否则我们将检查请求权限。
以下是我案例中PermissionUtils实用程序类中的代码。
public final class PermissionUtils {

    public static final int REQUEST_GROUP_STORAGE = 1508;

    private PermissionUtils() {
    }

    public static boolean hasThisPermission(Context context, String permission) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            return ActivityCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
        } else {
            return true;
        }
    }

    public static boolean isPermissionRequestNeeded(Activity activity, Fragment fragment, String permission, int requestCode) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasThisPermission(activity, permission)) {
            final String[] permissions = new String[]{permission};
            if (fragment == null) {
                activity.requestPermissions(permissions, requestCode);
            } else {
                fragment.requestPermissions(permissions, requestCode);
            }
            return true;
        }
        return false;
    }
}

在请求之后,如果您想从 onRequestPermissionsResult 调用该函数,则需要再次按下按钮才能进行函数调用。

因此,只需从 onRequestPermissionsResult 中调用即可。

//the below call  is from a fragment
     @Override
        public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
            if (requestCode == PermissionUtils.REQUEST_GROUP_STORAGE && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                onAttachPressed();
            } else {
                Log.e("value", "Permission Denied, You cannot use local drive .");
            }
        }

2

我看了所有的答案,但没有满足我确切需要的答案,所以这里是一个我写的例子,即使用户勾选了“不再询问”复选框,它也可以完美地工作。

  1. Create a method that will be called when you want to ask for runtime permission like readContacts() or you can also have openCamera() as shown below:

    private void readContacts() {
        if (!askContactsPermission()) {
            return;
        } else {
            queryContacts();
        } }
    
现在我们需要创建askContactsPermission(),你也可以将其命名为askCameraPermission()或者任何你要请求的权限名称。请注意保留HTML标签,但不要添加解释。
    private boolean askContactsPermission() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
        return true;
    }
    if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
        return true;
    }
    if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
        Snackbar.make(parentLayout, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                .setAction(android.R.string.ok, new View.OnClickListener() {
                    @Override
                    @TargetApi(Build.VERSION_CODES.M)
                    public void onClick(View v) {
                        requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
                    }
                }).show();
    } else if (contactPermissionNotGiven) {
        openPermissionSettingDialog();
    } else {
        requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
        contactPermissionNotGiven = true;

    }
    return false;
}

在编写此函数之前,请确保已定义以下实例变量,如下所示:
    private View parentLayout;
    private boolean contactPermissionNotGiven;;


/**
 * Id to identity READ_CONTACTS permission request.
 */
private static final int REQUEST_READ_CONTACTS = 0;

现在,最后一步是覆盖 onRequestPermissionsResult 方法,如下所示:
/**
 * Callback received when a permissions request has been completed.
 */
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode == REQUEST_READ_CONTACTS) {
        if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            queryContacts();
        }
    }
}

我们已经完成了运行时权限的设置,插件中的openPermissionSettingDialog()方法只是打开设置屏幕,如果用户通过点击不再询问复选框永久禁用了权限。以下是该方法:

    private void openPermissionSettingDialog() {
    String message = getString(R.string.message_permission_disabled);
    AlertDialog alertDialog =
            new AlertDialog.Builder(MainActivity.this, AlertDialog.THEME_DEVICE_DEFAULT_LIGHT)
                    .setMessage(message)
                    .setPositiveButton(getString(android.R.string.ok),
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Intent intent = new Intent();
                                    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                    Uri uri = Uri.fromParts("package", getPackageName(), null);
                                    intent.setData(uri);
                                    startActivity(intent);
                                    dialog.cancel();
                                }
                            }).show();
    alertDialog.setCanceledOnTouchOutside(true);
}

我们错过了什么? 1. 在strings.xml中定义使用的字符串。

<string name="permission_rationale">"Contacts permissions are needed to display Contacts."</string>
    <string name="message_permission_disabled">You have disabled the permissions permanently,
        To enable the permissions please go to Settings -> Permissions and enable the required Permissions,
        pressing OK you will be navigated to Settings screen</string>

  1. onCreate方法中初始化parentLayout变量

    parentLayout = findViewById(R.id.content);

  2. AndroidManifest.xml中定义所需的权限

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

  1. The queryContacts method, based on your need or the runtime permission you can call your method before which the permission was needed. in my case I simply use the loader to fetch the contact as shown below:

    private void queryContacts() {
    getLoaderManager().initLoader(0, null, this);}
    
这很好用,愉快编码 :)

2

试试这个

这是在Marshmallow版本中请求权限的最简单方法。

 if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED&&ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
    {
        //TO do here if permission is granted by user
    }
    else
    {
        //ask for permission if user didnot given
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
        {
            requestPermissions(new String[]{Manifest.permission.CAMERA,Manifest.permission.ACCESS_FINE_LOCATION}, 0);
        }
    }

注意:不要忘记在清单文件中添加相同的权限。

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

第二种方法 检查权限是否被授予的代码是什么?

ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.CAMERA}, 1);

覆盖该方法

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 1: {
            if (grantResults.length > 0 && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
         //                    grantResult[0] means it will check for the first postion permission which is READ_EXTERNAL_STORAGE
        //                    grantResult[1] means it will check for the Second postion permission which is CAMERA
                Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show();
            }
            else
                Toast.makeText(this, "Permission not Granted", Toast.LENGTH_SHORT).show();
            return;
        }
    }
}

所以你的意思是,如果我针对Marshmallow或更高版本,则不需要在清单中声明权限?我知道我们必须放置它,无论我们针对哪个Android版本。如果我错了,请纠正我@Sunil - Nilabja
@Nilabja 在 Android 中,无论是 Marshmallow 还是其他版本,声明权限在清单文件中都是必须的。 - Sunil

2

有一个很好的库可以在后台服务需要权限时用来请求权限。虽然该库的局限性在于它不能仅用于确定应用程序当前是否已获得权限。如果应用程序还没有权限,它总是会询问用户。

请尝试使用它,因为它能使生活更简单:Android Permissions


2
我使用RxPermission库来请求权限。因为请求权限需要编写冗长的代码。请注意保留HTML标签。
RxPermissions rxPermissions = new RxPermissions(this); // where this is an Activity instance // Must be done during an initialization phase like onCreate
rxPermissions
    .request(Manifest.permission.CAMERA)
    .subscribe(granted -> {
        if (granted) { // Always true pre-M
           // I can control the camera now
        } else {
           // Oups permission denied
        }
    });

在你的 build.gradle 文件中添加以下依赖项:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

dependencies {
    implementation 'com.github.tbruyelle:rxpermissions:0.10.1'
    implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
}

1
避免编写大量代码的简单请求权限的方法,

https://github.com/sachinvarma/EasyPermission

如何添加:

repositories {
        maven { url "https://jitpack.io" }
    }

implementation 'com.github.sachinvarma:EasyPermission:1.0.1'

如何请求许可:

 List<String> permission = new ArrayList<>();
 permission.add(EasyPermissionList.READ_EXTERNAL_STORAGE);
 permission.add(EasyPermissionList.ACCESS_FINE_LOCATION);

 new EasyPermissionInit(MainActivity.this, permission);

希望对某些人有所帮助。

1
我从旧代码中找到了一个解决方案,它真的很有效,在我的最新应用程序上仍然运行良好。
将其放在MainActivity类或您的启动Activity类上。
private RequestPermissionHandler mRequestPermissionHandler;

在创建时。
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mRequestPermissionHandler = new RequestPermissionHandler();
   handleButtonClicked();
}

私有函数handleButtonClicked()

private void handleButtonClicked() {
        mRequestPermissionHandler.requestPermission(this, new String[]{
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.INTERNET,
                Manifest.permission.ACCESS_COARSE_LOCATION,
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.READ_PHONE_STATE,
                Manifest.permission.ACCESS_NETWORK_STATE,
                Manifest.permission.WAKE_LOCK

        }, 123, new RequestPermissionHandler.RequestPermissionListener() {
            @Override
            public void onSuccess() {
                //Toast.makeText(MainActivity.this, "request permission success", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onFailed() {
                Toast.makeText(MainActivity.this, "request permission failed", Toast.LENGTH_SHORT).show();
            }
        });

    }

RequestPermissionHandler.java 类

import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

public class RequestPermissionHandler {
    private Activity mActivity;
    private RequestPermissionListener mRequestPermissionListener;
    private int mRequestCode;

    public void requestPermission(Activity activity, @NonNull String[] permissions, int requestCode,
                                  RequestPermissionListener listener) {
        mActivity = activity;
        mRequestCode = requestCode;
        mRequestPermissionListener = listener;

        if (!needRequestRuntimePermissions()) {
            mRequestPermissionListener.onSuccess();
            return;
        }
        requestUnGrantedPermissions(permissions, requestCode);
    }

    private boolean needRequestRuntimePermissions() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
    }

    private void requestUnGrantedPermissions(String[] permissions, int requestCode) {
        String[] unGrantedPermissions = findUnGrantedPermissions(permissions);
        if (unGrantedPermissions.length == 0) {
            mRequestPermissionListener.onSuccess();
            return;
        }
        ActivityCompat.requestPermissions(mActivity, unGrantedPermissions, requestCode);
    }

    private boolean isPermissionGranted(String permission) {
        return ActivityCompat.checkSelfPermission(mActivity, permission)
                == PackageManager.PERMISSION_GRANTED;
    }

    private String[] findUnGrantedPermissions(String[] permissions) {
        List<String> unGrantedPermissionList = new ArrayList<>();
        for (String permission : permissions) {
            if (!isPermissionGranted(permission)) {
                unGrantedPermissionList.add(permission);
            }
        }
        return unGrantedPermissionList.toArray(new String[0]);
    }

    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode == mRequestCode) {
            if (grantResults.length > 0) {
                for (int grantResult : grantResults) {
                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
                        mRequestPermissionListener.onFailed();
                        return;
                    }
                }
                mRequestPermissionListener.onSuccess();
            } else {
                mRequestPermissionListener.onFailed();
            }
        }
    }

    public interface RequestPermissionListener {
        void onSuccess();

        void onFailed();
    }
}

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