如今來談 Android 6.0 運行時權限適配,能夠說是很過期了,但是爲何還要寫呢? 一是試用了目前 GitHub 上排名比較靠前的開源項目,確實都很棒,可是在易用性仍是難以使人滿意,便萌生了本身擼一個的想法。 二是看了下目前國內主流的應用,發現不少都尚未適配 Android 6.0 ,所以以爲這篇文章還有它的意義。安全
既然上面說到了易用性,那咱們先來看看使用方法 在須要申請權限的地方調用bash
PermissionReq.with(this) // Activity or Fragment
.permissions(Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE) // 須要申請的權限
.result(new PermissionReq.Result() { // 申請結果回調
@Override
public void onGranted() { // 申請成功
// do something
}
@Override
public void onDenied() { // 申請失敗
// do something
}
})
.request();
複製代碼
在 Activity 基類和 Fragment 基類中添加如下代碼框架
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PermissionReq.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
複製代碼
API 設計採用了比較流行的流式調用,不知道你們看了是什麼感受,我以爲使用起來比較簡單,並且不會破壞原來的代碼結構。 說到這裏,我多說一句,如今有不少開源框架都是使用註解的方式來回調申請結果的,我以爲用這種方式雖然代碼層次變少了,可是可讀性變差了,並且可能會破壞原來代碼結構。ide
這裏還有個亮點不知道你們注意到沒,咱們沒有用到 RequestCode
,那 RequestCode
哪裏去了呢,在接下來的內容中我會告訴你們。 這也是我最喜歡的地方,不須要在每一個申請權限的地方定義一個 RequestCode
,更不用擔憂 RequestCode
會重複,由於 PermissionReq
已經幫你們處理好了。ui
看完使用方式後咱們來看下內部實現,咱們按照流程來看this
首先咱們要檢查 App 註冊了哪些權限,若是要申請的權限壓根就沒有在 Manifest 中註冊,那麼確定會失敗的spa
initManifestPermission(activity);
for (String permission : mPermissions) {
if (!sManifestPermissionSet.contains(permission)) {
if (mResult != null) {
mResult.onDenied();
}
return;
}
}
private static Set<String> sManifestPermissionSet;
private static synchronized void initManifestPermission(Context context) {
if (sManifestPermissionSet == null) {
sManifestPermissionSet = new HashSet<>();
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
String[] permissions = packageInfo.requestedPermissions;
Collections.addAll(sManifestPermissionSet, permissions);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
複製代碼
爲了線程安全咱們添加了 synchronized
修飾符。線程
若是要申請的權限已經在 Manifest 中註冊了,咱們接下來就要區分下系統版本了,若是系統版本低於 26 直接返回成功,不然才須要申請權限 這段代碼比較簡單,我就不貼了設計
若是系統版本 >= 26 ,那麼纔開始咱們真正的申請流程 檢查要申請的權限是否已經被容許,若是已經被容許,那麼就不必再申請了code
List<String> deniedPermissionList = getDeniedPermissions(activity, mPermissions);
if (deniedPermissionList.isEmpty()) {
if (mResult != null) {
mResult.onGranted();
}
return;
}
private static List<String> getDeniedPermissions(Context context, String[] permissions) {
List<String> deniedPermissionList = new ArrayList<>();
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
deniedPermissionList.add(permission);
}
}
return deniedPermissionList;
}
複製代碼
若是要申請的權限沒有所有被容許,那麼咱們就須要向系統發送申請了 生成 RequestCode
int requestCode = genRequestCode();
private static AtomicInteger sRequestCode = new AtomicInteger(0);
private static int genRequestCode() {
return sRequestCode.incrementAndGet();
}
複製代碼
還記得上面說咱們在使用時不須要定義 RequestCode 嗎,至此,RequestCode 終於浮出水面 咱們在內部使用一個靜態自增的 AtomicInteger 做爲 RequestCode ,保證 RequestCode 不會重複,使用 AtomicInteger 而不直接使用 int 是爲了線程安全
向系統發送申請
String[] deniedPermissions = deniedPermissionList.toArray(new String[deniedPermissionList.size()]);
requestPermissions(mObject, deniedPermissions, requestCode);
sResultArray.put(requestCode, mResult);
@TargetApi(Build.VERSION_CODES.M)
private static void requestPermissions(Object object, String[] permissions, int requestCode) {
if (object instanceof Activity) {
((Activity) object).requestPermissions(permissions, requestCode);
} else if (object instanceof Fragment) {
((Fragment) object).requestPermissions(permissions, requestCode);
}
}
複製代碼
申請時區分來源,申請後把 Result 放入 Array 中保存起來,等待申請結果到達
申請結果到達後通知申請者
public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Result result = sResultArray.get(requestCode);
if (result == null) {
return;
}
sResultArray.remove(requestCode);
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
result.onDenied();
return;
}
}
result.onGranted();
}
複製代碼
到這裏咱們的申請權限流程已經走完了,源碼也看完了。
爲了方便你們使用,我貼一下完整代碼
public class PermissionReq {
private static AtomicInteger sRequestCode = new AtomicInteger(0);
private static SparseArray<Result> sResultArray = new SparseArray<>();
private static Set<String> sManifestPermissionSet;
public interface Result {
void onGranted();
void onDenied();
}
private Object mObject;
private String[] mPermissions;
private Result mResult;
private PermissionReq(Object object) {
mObject = object;
}
public static PermissionReq with(@NonNull Activity activity) {
return new PermissionReq(activity);
}
public static PermissionReq with(@NonNull Fragment fragment) {
return new PermissionReq(fragment);
}
public PermissionReq permissions(@NonNull String... permissions) {
mPermissions = permissions;
return this;
}
public PermissionReq result(@Nullable Result result) {
mResult = result;
return this;
}
public void request() {
Activity activity = getActivity(mObject);
if (activity == null) {
throw new IllegalArgumentException(mObject.getClass().getName() + " is not supported");
}
initManifestPermission(activity);
for (String permission : mPermissions) {
if (!sManifestPermissionSet.contains(permission)) {
if (mResult != null) {
mResult.onDenied();
}
return;
}
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
if (mResult != null) {
mResult.onGranted();
}
return;
}
List<String> deniedPermissionList = getDeniedPermissions(activity, mPermissions);
if (deniedPermissionList.isEmpty()) {
if (mResult != null) {
mResult.onGranted();
}
return;
}
int requestCode = genRequestCode();
String[] deniedPermissions = deniedPermissionList.toArray(new String[deniedPermissionList.size()]);
requestPermissions(mObject, deniedPermissions, requestCode);
sResultArray.put(requestCode, mResult);
}
public static void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
Result result = sResultArray.get(requestCode);
if (result == null) {
return;
}
sResultArray.remove(requestCode);
for (int grantResult : grantResults) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
result.onDenied();
return;
}
}
result.onGranted();
}
@TargetApi(Build.VERSION_CODES.M)
private static void requestPermissions(Object object, String[] permissions, int requestCode) {
if (object instanceof Activity) {
((Activity) object).requestPermissions(permissions, requestCode);
} else if (object instanceof Fragment) {
((Fragment) object).requestPermissions(permissions, requestCode);
}
}
private static List<String> getDeniedPermissions(Context context, String[] permissions) {
List<String> deniedPermissionList = new ArrayList<>();
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
deniedPermissionList.add(permission);
}
}
return deniedPermissionList;
}
private static synchronized void initManifestPermission(Context context) {
if (sManifestPermissionSet == null) {
sManifestPermissionSet = new HashSet<>();
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_PERMISSIONS);
String[] permissions = packageInfo.requestedPermissions;
Collections.addAll(sManifestPermissionSet, permissions);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
private static Activity getActivity(Object object) {
if (object != null) {
if (object instanceof Activity) {
return (Activity) object;
} else if (object instanceof Fragment) {
return ((Fragment) object).getActivity();
}
}
return null;
}
private static int genRequestCode() {
return sRequestCode.incrementAndGet();
}
}
複製代碼
本文主要介紹瞭如何快速、簡單的適配 Android 6.0 運行時權限,雖然寫的比較晚了,但仍是但願能幫到你們。 若是你在閱讀本文時發現什麼問題或者紕漏,或者你有不一樣的見解,歡迎指出!
遷移自個人簡書 2017.08.31