前言
权限申请效果
GitHub地址:PermissionUtils
Android8.0昨天已经发布,但是关于Android版本的最新统计,来看看图
很明显,6.0版本目前比重最高,从6.0开始的运行时权限,是每个Android开发者绕不过的问题
正文
虽然Google大佬允许我们将targetSdkVersion 设置为22及以下,但是向来紧跟Google大佬步伐的我们,怎么会用这种投机取消的方式呢。
关于Android权限,Google分为两类,一类为普通权限,默认在AndroidManifest中注册即可,主要为以下,这类权限默认不需要用户授权,比如震动、访问网络权限:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
而另一类就是危险权限,涉及用户隐私或影响设备安全
根据表中可以看出,危险权限都是成组出现的,那么在授权时,如果申请一组中某个权限,权限申请弹窗会提示整组权限的说明,而且如果一组权限中的某个权限已经被授权,那么在申请同组其他权限时,系统立即授权,不需要通过用户允许。
注意: 运行时权限依然要在AndroidManifest中注册
开始适配
这里举例适配Manifest.permission.ACCESS_FINE_LOCATION位置权限
- 1、在AndroidManifest中注册权限
- 2、检查权限
ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED
- 3、申请权限
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
myRequestCode);
- 4、申请回调的处理
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case myRequestCode: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//申请通过
} else {
//申请失败
//是否需要向用户解释申请的原因,在用户点击拒绝后,弹出dialog,给用户解释
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.ACCESS_FINE_LOCATION))
}
}
}
}
}
封装
运行时权限的流程很清晰,那么开发中每次写这么多固定代码,为何不进行封装一下呢?那就来分析申请的流程和特点,进行处理
package com.ddz.lifestyle.utils;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.SparseArray;
import com.ddz.lifestyle.LifeStyle;
import com.ddz.lifestyle.http.GankApiStores;
import com.ddz.lifestyle.http.bean.BookBean;
import com.tencent.smtt.sdk.QbSdk;
import org.jetbrains.annotations.NotNull;
/**
* @Author: ddz
* Creation time: 17.8.11 17:11
* describe:{Android权限申请的封装,并在内部实现申请结果的处理}
*/
public class PermissionUtils {
private static PermissionUtils permission;
private String[] permissions;
private Activity mActivity;
private RequestPermissionListener PermissionListener;
public static int requestCode = 100; //requestCode传值为100
public static PermissionUtils getInstance() {
if (null == permission) {
synchronized (PermissionUtils.class) {
if (null == permission) {
permission = new PermissionUtils();
}
}
}
return permission;
}
/**
* 权限检查
*
* @param permission
* @return
*/
public boolean checkPermission(@NonNull String permission) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
return true;
} else {
return ContextCompat.checkSelfPermission(LifeStyle.getContext(), permission) == PackageManager.PERMISSION_GRANTED;
}
}
/**
* Activity 页面申请权限
*
* @param activity
* @param permissions
* @param requestCode
* @param requestPermissionListener
*/
public void requestPermissiion(final @NonNull Activity activity,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode, @NonNull RequestPermissionListener requestPermissionListener) {
this.mActivity = activity;
PermissionListener = requestPermissionListener;
this.permissions = permissions;
ActivityCompat.requestPermissions(activity, permissions, requestCode);
}
/**
* Fragment页面申请权限
*
* @param fragment
* @param permissions
* @param requestCode
* @param requestPermissionListener
*/
public void requestFragmentPermission(final @NonNull Fragment fragment,
final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode, @NonNull RequestPermissionListener requestPermissionListener) {
PermissionListener = requestPermissionListener;
this.permissions = permissions;
fragment.requestPermissions(permissions, requestCode);
}
/**
* 权限申请结果的回调
*
* @param activity
* @param requestCode
* @param permissions
* @param grantResults
*/
public void onRequestPermissionResult(final @NonNull Activity activity, int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
this.mActivity = activity;
if (null != PermissionListener) {
if (requestCode == PermissionUtils.requestCode && grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
PermissionListener.requestConfirm();
} else {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permissions[0])) {
PermissionListener.requestCancel();
} else {
PermissionListener.requestCancelAgain();
}
}
}
}
/**
* 用户点击拒绝,弹出申请权限的说明弹窗,也可以自定义实现
*
* @param context Context
* @param title 弹窗标题
* @param message 申请权限解释说明
* @param confirm 确认按钮的文字,默认OK
* @param cancel 取消按钮呢的文字,默认不显示取消按钮
*/
public void requestDialog(Context context, @NonNull String title, @NonNull String message, String confirm, String cancel) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle(title).setMessage(message);
builder.setPositiveButton(confirm == null ? "OK" : confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
requestPermissiion(mActivity, permissions, requestCode, PermissionListener);
dialog.dismiss();
}
});
if (null != cancel) {
builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
builder.setCancelable(false);
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 用户勾选不再显示并点击拒绝,弹出打开设置页面申请权限,也可以自定义实现
*
* @param context Context
* @param title 弹窗标题
* @param message 申请权限解释说明
* @param confirm 确认按钮的文字,默认OK
* @param cancel 取消按钮呢的文字,默认不显示取消按钮
*/
public void requestDialogAgain(Context context, @NonNull String title, @NonNull String message, String confirm, String cancel) {
try {
AlertDialog.Builder builder = new AlertDialog.Builder(context).setTitle(title).setMessage(message);
builder.setPositiveButton(confirm == null ? "OK" : confirm, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startSettingActivity(mActivity);
dialog.dismiss();
}
});
if (null != cancel) {
builder.setNegativeButton(cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
builder.setCancelable(false);
builder.show();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打开设置页面打开权限
*
* @param context
*/
public void startSettingActivity(@NonNull Activity context) {
try {
Intent intent =
new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" +
context.getPackageName()));
intent.addCategory(Intent.CATEGORY_DEFAULT);
context.startActivityForResult(intent, 10); //这里的requestCode和onActivityResult中requestCode要一致
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打开设置页面的回调
*
* @param requestCode
* @param resultCode
* @param data
*/
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 10: //这里值是打开设置页面申请权限的RequestCode,默认为10
try {
if (null != PermissionListener) {
if (null != permissions && permissions.length > 0) {
for (String permission : permissions) {
if (checkPermission(permission)) {
PermissionListener.requestConfirm();
} else {
PermissionListener.requestFailed();
}
}
} else {
PermissionListener.requestFailed();
}
} else {
PermissionListener.requestFailed();
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
}
/**
* 权限申请回调
*/
public interface RequestPermissionListener {
void requestConfirm(); //申请成功
void requestCancel(); //拒绝
void requestCancelAgain(); //勾选不再提示并拒绝
void requestFailed(); //在设置页面申请权限失败
}
}
封装的工具类使用 (分为四步)
检查权限
PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)
申请权限
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申请成功
toast(null);
}
@Override
public void requestCancel() {
//用户拒绝,对用户解释申请理由
//如果想使用封装好的弹窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申请权限", "需要位置权限", null, null);
}
@Override
public void requestCancelAgain() {
//用户勾选不再提示并拒绝,申请打开应用设置页面申请权限,具体逻辑自己写
//使用默认封装好的提示,并打开设置页面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申请权限", "去设置页面打开位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申请失败
toast("对不起,没有权限,退出");
}
});
权限回调的处理
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
打开设置页面申请权限的处理
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);
根据封装好的工具类,写个简单例子
package com.ddz.lifestyle.view.activity;
import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import com.ddz.lifestyle.R;
import com.ddz.lifestyle.utils.PermissionUtils;
/**
* @Author: ddz
* Creation time: 17.8.11 16:07
* describe:()
*/
public class TestActivity extends AppCompatActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initPremission();
}
private void initPremission() {
if (PermissionUtils.getInstance().checkPermission(Manifest.permission.ACCESS_FINE_LOCATION)) {
toast(null);
} else {
PermissionUtils.getInstance().requestPermissiion(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PermissionUtils.requestCode, new PermissionUtils.RequestPermissionListener() {
@Override
public void requestConfirm() {
//申请成功
toast(null);
}
@Override
public void requestCancel() {
//用户拒绝,对用户解释申请理由
//如果想使用封装好的弹窗提示
PermissionUtils.getInstance().requestDialog(TestActivity.this, "申请权限", "需要位置权限", null, null);
}
@Override
public void requestCancelAgain() {
//用户勾选不再提示并拒绝,申请打开应用设置页面申请权限,具体逻辑自己写
//使用默认封装好的提示,并打开设置页面
PermissionUtils.getInstance().requestDialogAgain(TestActivity.this, "申请权限", "去设置页面打开位置限才能正常使用", null, null);
}
@Override
public void requestFailed() {
//申请失败
toast("对不起,没有权限,退出");
}
});
}
}
//一定要对权限回调进行处理
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionUtils.getInstance().onRequestPermissionResult(TestActivity.this, requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
//如果打开了设置页面申请权限,一定要对回调进行处理
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
PermissionUtils.getInstance().onActivityResult(requestCode, resultCode, data);
super.onActivityResult(requestCode, resultCode, data);
}
private void toast(String message) {
Toast.makeText(TestActivity.this, message == null ? "权限申请成功" : message, Toast.LENGTH_SHORT).show();
}
}
结束
封装后的权限申请,结构更加清晰,根据回调结果的不同可以做出相应处理,也封装了权限申请的说明弹窗,如果没有特殊的设计要求,即可满足日常开发的权限申请工作。
GitHub地址:PermissionUtils