基類繼承方式處理Android平臺動態(tài)權(quán)限申請問題

android從MM開始,添加了動態(tài)權(quán)限機制欠拾。

針對一些特別的敏感權(quán)限胰锌,要求程序主動向用戶申請,用戶同意之后才可以使用相關(guān)權(quán)限清蚀。

個人覺得處理添加動態(tài)權(quán)限最方便的是寫一個基類(類似于PermissionBaseActivity匕荸,PermissionBaseFragment),然后讓需要申請權(quán)限的界面(某個Activity或者Fragment)繼承于對應(yīng)的基類枷邪。

因為在基類中已經(jīng)寫好了申請權(quán)限的基本方法榛搔,所以繼承自基類的子類,可以方便的處理權(quán)限相關(guān)的邏輯东揣。

以Activity為例践惑,新建基類PermissonBaseActivity繼承于AppCompatActivity

public abstract class PermissionBaseActivity extends AppCompatActivity {

private Set<String> mPermsAll = new HashSet();
private Set<String> mPermsNeedful = new HashSet();
protected String TAG;
protected Context mContext;
protected static final int PERMISSION_REQUEST_CODE = 110;
protected FragmentManager mFragmentManager;

private int mTransactionIndex = 1;
private int getTransactionId() {
    return mTransactionIndex++;
}
private void resetTransactionId(@IntRange(from = 0) int v) {
    mTransactionIndex = v;
}

public interface IPermissionCallback {
    void onPermissionResult(@NonNull String perm, int result);
}

private static final String KEY_PERMISSION = "permission";
private final SparseArray<HashMap<String, IPermissionCallback>> mPermissionMapHandler = new SparseArray<>();
private IPermissionCallback pushPermissionsHolderData(int key, String[] perms, @NonNull IPermissionCallback permissionCallback) {
    HashMap<String, IPermissionCallback> permKey = mPermissionMapHandler.get(key);
    if (permKey == null) {
        permKey = new HashMap<>();
    }
    permKey.clear();
    for (String perm: perms) {
        permKey.put(perm, permissionCallback);
    }
    mPermissionMapHandler.put(key, permKey);
    return permissionCallback;
}

private void addPermissionsHolderGuardKey(int key, @NonNull IPermissionCallback permissionCallback) {
    HashMap<String, IPermissionCallback> permKey = mPermissionMapHandler.get(key);
    if (permKey == null) {
        permKey = new HashMap<>();
    }
    permKey.put(KEY_PERMISSION, permissionCallback);
    mPermissionMapHandler.put(key, permKey);
}

private IPermissionCallback removePermissionHolder(int key, @NonNull String perm) {
    HashMap<String, IPermissionCallback> permCallback = mPermissionMapHandler.get(key);
    if (permCallback != null) {
        return permCallback.get(perm);
    }
    return null;
}

private IPermissionCallback getPermissionCallback(int key) {
    HashMap<String, IPermissionCallback> permissionMap = mPermissionMapHandler.get(key);
    for (IPermissionCallback callback: permissionMap.values()) {
        if (callback != null) {
            return callback;
        }
    }
    return null;
}

private void iteratePermissionHolder(int key) {
    HashMap<String, IPermissionCallback> permsMap = mPermissionMapHandler.get(key);
    if (permsMap == null) {
        return;
    }
    Iterator<Map.Entry<String, IPermissionCallback>> it = permsMap.entrySet().iterator();
    Map.Entry<String, IPermissionCallback> entry;
    while (it.hasNext()) {
        entry = it.next();
        if (!entry.getKey().equals(KEY_PERMISSION)) {
            entry.getValue().onPermissionResult(entry.getKey(), PackageManager.PERMISSION_GRANTED);
        }
    }
    mPermissionMapHandler.remove(key);
}

@CallSuper
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mContext = getApplicationContext();
    TAG = this.getClass().getSimpleName();
    mFragmentManager = getFragmentManager();
}

protected void pushPermsData(String... perms) {
    //略
}

protected void updatePermInfo() {
   //略
}

protected boolean checkPermission(String perm) {
    if (Build.VERSION.SDK_INT >= M) {
        return ActivityCompat.checkSelfPermission(mContext, perm) == PackageManager.PERMISSION_GRANTED;
    }
    return true;
}

protected boolean checkPermState() {
    updatePermInfo();
    //略
}

protected ArrayList<String> updatePermissions(String[] perms, ArrayList<String> granted) {
    if (perms == null) {
        return null;
    }

    ArrayList<String> resultPerms = new ArrayList<>();
    for (String perm : perms) {
        if (ActivityCompat.checkSelfPermission(mContext, perm) == PackageManager.PERMISSION_GRANTED) {
            granted.add(perm);
        } else {
            resultPerms.add(perm);
        }
    }
    return resultPerms;
}

private void requestPermissionManage(@NonNull String[] perms, @NonNull IPermissionCallback permissionCallback) {
    int key = getTransactionId();
    if (key != -1 && (key & 0xffff0000) != 0) {
        key = 1;
        resetTransactionId(1);
    }

    if (perms.length == 1) {
        //如果只申請一個權(quán)限,調(diào)用這里
        requestSingleCompatPermission(key, perms[0], permissionCallback);
    } else {
        //申請多個權(quán)限嘶卧,調(diào)用這里
        requestMultiCompatPermissions(key, perms, permissionCallback);
    }
}

private void requestMultiCompatPermissions(@IntRange(from = 0) int key, @NonNull String[] perms, @NonNull IPermissionCallback permissionCallback) {
    ArrayList<String> granted = new ArrayList<>();
    //申請多個權(quán)限的時候尔觉,需要過濾權(quán)限,篩選出已經(jīng)獲得過的權(quán)限
    ArrayList<String> needRequest = updatePermissions(perms, granted);
    if (granted.size() > 0) {
        if (needRequest.size() > 0) {
            String[] grantedArray = new String[granted.size()];
            granted.toArray(grantedArray);
            //這個稍后補充說明
            pushPermissionsHolderData(key, grantedArray, permissionCallback);
        } else {
            //如果想申請的所有權(quán)限都已經(jīng)被授予過了芥吟,那么直接回調(diào)返回
            for (String perm: granted) {
                permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
            }
            return;
        }
    }
    //TODO check ActivityCompat.shouldShowRequestPermissionRationale for every permission
    if (needRequest.size() > 0) {
        String[] permsArray = new String[needRequest.size()];
        needRequest.toArray(permsArray);
        addPermissionsHolderGuardKey(key, permissionCallback);
        //開始申請那些還未獲得的權(quán)限
        ActivityCompat.requestPermissions(this, permsArray, key);
    }
}

private void requestSingleCompatPermission(@IntRange(from = 0) int key, @NonNull String perm, @NonNull IPermissionCallback permissionCallback) {
    if (ActivityCompat.checkSelfPermission(mContext, perm) == PackageManager.PERMISSION_GRANTED) {
        //如果要申請的權(quán)限已經(jīng)被授予侦铜,直接回調(diào)返回
        permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
    } else {
       //以前申請權(quán)限時专甩,用戶是否勾選過不再提示的復(fù)選框
        boolean should = ActivityCompat.shouldShowRequestPermissionRationale(this, perm);
        if (!should) {//如果勾選過,那么直接跳到APP詳細(xì)信息頁面
            jumpToAppDetailsDialog();
        } else {//開始申請權(quán)限
            addPermissionsHolderGuardKey(key, permissionCallback);
            ActivityCompat.requestPermissions(this, new String[]{perm}, key);
        }
    }
}
//申請權(quán)限從這里開始钉稍,String... perms就是需要申請的權(quán)限涤躲,permissionCallback是申請成功或者失敗后的回調(diào)
protected void requestPermissions(IPermissionCallback permissionCallback, String... perms) {
    if (perms == null) {
        return;
    }

    if (permissionCallback == null) {
        return;
    }

    if (Build.VERSION.SDK_INT >= M) {
        //如果是MM平臺以上,跳轉(zhuǎn)去處理申請權(quán)限
        requestPermissionManage(perms, permissionCallback);
    } else {
        //如果MM平臺之前的贡未,直接處理回調(diào)
        for (String perm: perms) {
            permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
        }
    }
}

protected void requestPermissions(IPermissionCallback permissionCallback) {
    //略
}

protected void requestPermissions() {
    //略
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    updatePermInfo();
    permissionResultHandler(requestCode, permissions, grantResults);
}

private void permissionResultHandler(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if (mPermissionMapHandler.size() > 0) {
        //這里主要是回調(diào)而已
        IPermissionCallback callback = getPermissionCallback(requestCode);
        iteratePermissionHolder(requestCode);
        for (int i = 0; i < permissions.length; i++) {
            if (callback != null) {
                callback.onPermissionResult(permissions[i], grantResults[i]);
            }
        }
    }
}

protected void jumpToAppSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivity(intent);
}

private void jumpToAppDetailsDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder(this)
            .setTitle("Title")
            .setMessage("Message")
            .setPositiveButton("Okay", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    jumpToAppSettings();
                }
            })
            .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {

                }
            });
    builder.create();
    builder.show();
}
}

解釋下pushPermissionsHolderData(key, grantedArray, permissionCallback)
在開頭的時候种樱,定義了一個SparseArray:
private final SparseArray<HashMap<String, IPermissionCallback>> mPermissionMapHandler = new SparseArray<>();

 IPermissionCallback pushPermissionsHolderData(int key, String[] perms,IPermissionCallback permissionCallback) {
   HashMap<String, IPermissionCallback> permKey = mPermissionMapHandler.get(key);
   if (permKey == null) {
       permKey = new HashMap<>();
   }
   permKey.clear();
   for (String perm: perms) {//以權(quán)限和回調(diào)接口建立鍵值對
       permKey.put(perm, permissionCallback);
   }
   mPermissionMapHandler.put(key, permKey);
   return permissionCallback;
 }

每次申請權(quán)限,都為這一次權(quán)限申請事件賦予一個獨立的事務(wù)id : mTransactionIndex ,.

private int mTransactionIndex = 1;
private int getTransactionId() {
    return mTransactionIndex++;
}

當(dāng)申請權(quán)限時俊卤,以這個自定義的mTransactionIndex 做為申請權(quán)限傳入的requestCode:

int key = getTransactionId();
ActivityCompat.requestPermissions(this, permsArray, key);

這樣在onRequestPermissionsResult方法中嫩挤,就可以通過

HashMap<String, IPermissionCallback> permissionMap = mPermissionMapHandler.get(key);

獲取到正確的權(quán)限事件映射。

在方法onRequestPermissionsResult中消恍,獲取到requestCode對應(yīng)的HashMap鍵值對之后岂昭,就可以遍歷想申請的權(quán)限,然后回調(diào)申請的結(jié)果:

private void permissionResultHandler(int requestCode, String[] permissions, @NonNull int[] grantResults) {
   if (mPermissionMapHandler.size() > 0) {
       IPermissionCallback callback = getPermissionCallback(requestCode);
       iteratePermissionHolder(requestCode);
       for (int i = 0; i < permissions.length; i++) {
           if (callback != null) {
               callback.onPermissionResult(permissions[i], grantResults[i]);
           }
      }
   }
}

大體流程就是這樣哺哼,具體的使用方法如下:

   requestPermissions(new IPermissionCallback() {
               @Override
               public void onPermissionResult(@NonNull String perm, int result) {
                   Log.e(TAG, "onPermissionResult2 " + perm + " " + result);
               }
  }, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_CONTACTS, Manifest.permission.CAMERA );

不管用戶最后是同意還是拒絕權(quán)限佩抹,最后都回調(diào)到 onPermissionResult(@NonNull String perm, int result) 方法。perm表示權(quán)限的名字取董,result是這個perm權(quán)限申請的結(jié)果棍苹,是同意(0)還是拒絕(-1)。

我使用動態(tài)權(quán)限的想法是茵汰,比如在需要錄音權(quán)限的地方枢里,這樣使用

   requestPermissions(new IPermissionCallback() {
               @Override
               public void onPermissionResult(@NonNull String perm, int result) {
                   if (result == PackageManager.PERMISSION_GRANTED) {
                       //開始錄音
                   } else {
                       //無法錄音,添加用戶提示
                   }
               }
   }, Manifest.permission.RECORD_AUDIO);

這是個人偏好的用法蹂午,分享出來栏豺。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市豆胸,隨后出現(xiàn)的幾起案子奥洼,更是在濱河造成了極大的恐慌,老刑警劉巖晚胡,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灵奖,死亡現(xiàn)場離奇詭異,居然都是意外死亡估盘,警方通過查閱死者的電腦和手機瓷患,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來遣妥,“玉大人擅编,你說我怎么就攤上這事。” “怎么了爱态?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵谭贪,是天一觀的道長。 經(jīng)常有香客問我肢藐,道長故河,這世上最難降的妖魔是什么吱韭? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任吆豹,我火速辦了婚禮,結(jié)果婚禮上理盆,老公的妹妹穿的比我還像新娘痘煤。我一直安慰自己,他們只是感情好猿规,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布衷快。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上头岔,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天懂鸵,我揣著相機與錄音,去河邊找鬼若锁。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的邓萨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼菊卷,長吁一口氣:“原來是場噩夢啊……” “哼缔恳!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起洁闰,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤歉甚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后扑眉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纸泄,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年襟雷,在試婚紗的時候發(fā)現(xiàn)自己被綠了刃滓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸弄,死狀恐怖咧虎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情计呈,我是刑警寧澤砰诵,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布征唬,位于F島的核電站,受9級特大地震影響茁彭,放射性物質(zhì)發(fā)生泄漏总寒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一理肺、第九天 我趴在偏房一處隱蔽的房頂上張望摄闸。 院中可真熱鬧,春花似錦妹萨、人聲如沸年枕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽熏兄。三九已至,卻和暖如春树姨,著一層夾襖步出監(jiān)牢的瞬間摩桶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工帽揪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留硝清,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓台丛,卻偏偏與公主長得像耍缴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子挽霉,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容