基类继承方式处理Android平台动态权限申请问题

android从MM开始,添加了动态权限机制。

针对一些特别的敏感权限,要求程序主动向用户申请,用户同意之后才可以使用相关权限。

个人觉得处理添加动态权限最方便的是写一个基类(类似于PermissionBaseActivity,PermissionBaseFragment),然后让需要申请权限的界面(某个Activity或者Fragment)继承于对应的基类。

因为在基类中已经写好了申请权限的基本方法,所以继承自基类的子类,可以方便的处理权限相关的逻辑。

以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) {
        //如果只申请一个权限,调用这里
        requestSingleCompatPermission(key, perms[0], permissionCallback);
    } else {
        //申请多个权限,调用这里
        requestMultiCompatPermissions(key, perms, permissionCallback);
    }
}

private void requestMultiCompatPermissions(@IntRange(from = 0) int key, @NonNull String[] perms, @NonNull IPermissionCallback permissionCallback) {
    ArrayList<String> granted = new ArrayList<>();
    //申请多个权限的时候,需要过滤权限,筛选出已经获得过的权限
    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 {
            //如果想申请的所有权限都已经被授予过了,那么直接回调返回
            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);
        //开始申请那些还未获得的权限
        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) {
        //如果要申请的权限已经被授予,直接回调返回
        permissionCallback.onPermissionResult(perm, PackageManager.PERMISSION_GRANTED);
    } else {
       //以前申请权限时,用户是否勾选过不再提示的复选框
        boolean should = ActivityCompat.shouldShowRequestPermissionRationale(this, perm);
        if (!should) {//如果勾选过,那么直接跳到APP详细信息页面
            jumpToAppDetailsDialog();
        } else {//开始申请权限
            addPermissionsHolderGuardKey(key, permissionCallback);
            ActivityCompat.requestPermissions(this, new String[]{perm}, key);
        }
    }
}
//申请权限从这里开始,String... perms就是需要申请的权限,permissionCallback是申请成功或者失败后的回调
protected void requestPermissions(IPermissionCallback permissionCallback, String... perms) {
    if (perms == null) {
        return;
    }

    if (permissionCallback == null) {
        return;
    }

    if (Build.VERSION.SDK_INT >= M) {
        //如果是MM平台以上,跳转去处理申请权限
        requestPermissionManage(perms, permissionCallback);
    } else {
        //如果MM平台之前的,直接处理回调
        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) {
        //这里主要是回调而已
        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) {//以权限和回调接口建立键值对
       permKey.put(perm, permissionCallback);
   }
   mPermissionMapHandler.put(key, permKey);
   return permissionCallback;
 }

每次申请权限,都为这一次权限申请事件赋予一个独立的事务id : mTransactionIndex ,.

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

当申请权限时,以这个自定义的mTransactionIndex 做为申请权限传入的requestCode:

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

这样在onRequestPermissionsResult方法中,就可以通过

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

获取到正确的权限事件映射。

在方法onRequestPermissionsResult中,获取到requestCode对应的HashMap键值对之后,就可以遍历想申请的权限,然后回调申请的结果:

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 );

不管用户最后是同意还是拒绝权限,最后都回调到 onPermissionResult(@NonNull String perm, int result) 方法。perm表示权限的名字,result是这个perm权限申请的结果,是同意(0)还是拒绝(-1)。

我使用动态权限的想法是,比如在需要录音权限的地方,这样使用

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

这是个人偏好的用法,分享出来。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,192评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,186评论 1 303
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,844评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,471评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,876评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,891评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,068评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,791评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,539评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,772评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,250评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,577评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,244评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,146评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,949评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,995评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,812评论 2 276

推荐阅读更多精彩内容