一個類快速實現 Android 6.0 運行時權限適配

前言

如今來談 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

相關文章
相關標籤/搜索