Android 6.0運行時權限動態申請處理
最近需要在項目中對android 6.0以上的手機做權限處理,所以抽了個時間,將大於或等於6.0以上的權限處理做一個總結!
android 在6.0以後,將權限分爲兩類,分別爲Normal Pemission和Dangerous Permission,Normal Permission不需要用戶在操作的時候進行授權,例如最常用的訪問網絡的權限;Dangerous Permission 則不同,其涉及到了用戶的隱私,必須在用戶操作的過程中,進行授權通過了,纔可以進行某些預定的操作,例如獲取手機聯繫人,若用戶沒有對此進行授權,則會導致程序無法獲取到手機上的聯繫人。而當用戶拒絕該權限之後,沒有在設置中將其打開,則會一直處於權限拒絕狀態,所以,我們需要對此進行處理。
下面,將通過一個打電話的例子來演示操作。
在6.0以下的手機中,我們可以直接通過Intent的方式來撥打電話,代碼如下:
private void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + mPhone);
intent.setData(data);
startActivity(intent);
}
而在6.0以上的手機中,我們則需要通過一些步驟,確定當前程序是否撥打電話的權限,如果有權限,則可以和如上方式一樣直接調用call()方法來打電話,但是若檢測到當前程序沒有撥打電話的權限。那麼,我們需要通過如下操作來進行處理:
1:申請用戶授權撥打電話的權限
2:若授予了該權限,則調用call()方法進行撥打電話
3:若用戶拒絕了,則我們需要進行一些其他的操作,來告訴用戶,該功能不可用。
下面,我將具體的完整流程整理了下(畫圖技術太爛,不要在意,哈哈,希望大家都能看明白我畫的是啥玩意兒 ^_^ ):
————————————-一條有逼格的分界線,下面開始擼碼—————————————–
首先,先完成功能上的操作,具體代碼如下,代碼中,打了很多的Log,也寫了很多註釋,這樣能讓大家看的更清楚,明白!
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
call = (TextView) findViewById(R.id.call);
call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//檢查權限
checkPermission();
}
});
}
private void checkPermission(){
//1:判斷當前手機系統是否大於或等於6.0
//2:若大於6.0,則檢查是否由打電話的權限
//3:若沒有打電話的權限 則申請打電話的權限
//4:若有打電話的權限 則直接打電話
//5:若當前小於6.0,則直接打電話
if(Build.VERSION.SDK_INT >= M ){
Log.e(TAG,"版本大於等於6.0");
//checkSelfPermission方法用於檢測程序是否含有某項權限
//參數一:當前的上下文對象
//參數二:需要檢測的權限
/**
* checkSelfPermission方法用於檢測程序是否含有某項權限
* 參數一:當前的上下文對象
* 參數二:需要檢測的權限
*
* 該方法得返回值爲int類型,有兩種值,分別爲:
* 1:PackageManager.PERMISSION_DENIED 該權限被拒絕
* 2:PackageManager.PERMISSION_GRANTED 該權限被授予
*/
int isPermission = ContextCompat.checkSelfPermission(this, android.Manifest.permission.CALL_PHONE);
if(isPermission == PackageManager.PERMISSION_GRANTED){
//該權限已被授予
Log.e(TAG,"該權限已被授予");
call();
}else{
Log.e(TAG,"該權限已被拒絕");
//申請權限
/**
* ActivityCompat.requestPermissions()方法用於申請權限
* 參數一:Activity
* 參數二:需要申請的權限,其爲一個數組,可同時傳入多個將要申請的權限
* 參數三:請求碼,用於在請求完成之後的回調中使用
*/
ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},REQUEST_CODE);
}
}else {
call();
}
}
private void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + mPhone);
intent.setData(data);
startActivity(intent);
}
/**
* 該方法即爲申請權限的回調方法 無論申請成功或者失敗 都會回調這個函數
* @param requestCode 上文中提到的請求碼
* @param permissions 申請的權限
* @param grantResults 申請的結果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode == REQUEST_CODE){
if(grantResults != null && grantResults.length>0){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
Log.e(TAG,"權限已被授予,此時可以撥打電話了");
call();
}else{
Toast.makeText(this,"您拒絕了撥打電話的權限!",Toast.LENGTH_SHORT).show();
}
}
}
}
到此 動態權限的功能也就處理完畢了!
你以爲我會說結束了麼,哈哈哈,想多了吧,老鐵!^_^
不知道讀到這裏,大家有沒有一個問題:難道我一涉及到危險權限的操作,就要寫這麼多代碼來處理嗎?這樣會不會很喪心病狂啊!
下面,高大上的東西來了:封裝一個高大上的權限申請框架!
寫博客之前,也看了好些技術求人的方案,最終選擇的是一個註解 + 反射的方案來處理
下面直接貼代碼,註釋已經很詳細了,相信大家能看明白!
PermissionHelper:權限問題集中在此處理
package com.justh.dell.util; import android.app.Activity; import android.app.Fragment; import android.support.v4.app.ActivityCompat; import java.util.ArrayList; import static com.justh.dell.util.PermissionUtils.getDeniedPermissions; /** * Created by DELL on 2017/7/29. * 權限處理類 */ public class PermissionHelper { /** * 需要的參數: * 1:申請全的的Activity 或 fragment * 2:需要申請的權限 * 3:申請權限的請求碼 */ private Object mObject; private String[] permissions; private int requestCode; private PermissionHelper(Object mObject){ this.mObject = mObject; } //第一種使用方式,直接傳入參數 public static PermissionHelper requestPermission(Activity activity,String[] permissions,int requestCode){ return PermissionHelper.with(activity).requestCode(requestCode).requestPermission(permissions); } public static PermissionHelper requestPermission(Fragment fragment,String[] permissions,int requestCode){ return PermissionHelper.with(fragment).requestCode(requestCode).requestPermission(permissions); } //第二種方式:鏈式調用 public static PermissionHelper with(Activity activity){ return new PermissionHelper(activity); } public static PermissionHelper with(Fragment fragment){ return new PermissionHelper(fragment); } public PermissionHelper requestCode(int requestCode){ this.requestCode = requestCode; return this; } public PermissionHelper requestPermission(String... permissions){ this.permissions = permissions; return this; } //發起操作 public void request(){ /** * 1:判斷當前手機系統是否大於或等於6.0 */ if(!PermissionUtils.isMoreThanM()){ /** * 當前系統小於6.0,則直接執行需要執行的方法,如撥號 * 由於需要執行的方法是不確定的 但Activity或fragment是已知的, * 所以這裏使用註解 + 反射 的方式來獲取到需要執行的方法並執行操作 */ PermissionUtils.executeSuccesseMethod(mObject,requestCode); }else{ /** * 當前系統大於 或等於6.0 * 1:判斷這些權限中,是否有被被拒絕的權限 * 2:若有,則向用戶申請權限 * 2.1:若申請成功 則執行成功的方法 * 2.2:若申請失敗 則執行一些提示操作 * 3:若無,則直接執行成功的方法 */ ArrayList<String> deniedPermissions = getDeniedPermissions(mObject,permissions); if (deniedPermissions.size() > 0){ //有被用戶拒絕的權限 需要向用戶提交申請 ActivityCompat.requestPermissions(PermissionUtils.getActivity(mObject), deniedPermissions.toArray(new String[deniedPermissions.size()]),requestCode); }else{ //無被拒絕的權限 PermissionUtils.executeSuccesseMethod(mObject,requestCode); } } } public static void requestPermissionsResult(int requestCode, String[] permissions,Object object) { //再去獲取以下 看看是否還有被用戶拒絕的權限 ArrayList<String> deniedPermission = PermissionUtils.getDeniedPermissions(object,permissions); if(deniedPermission.size() == 0){ //用戶已授權所有權限 PermissionUtils.executeSuccesseMethod(object,requestCode); }else{ /** * 還是有部分權限被用戶拒絕了 * 執行一個類似提示或捕獲異常等的方法 * 視情況而定 * * 這裏彈出一個Toast * 套路一樣 還是使用 註解 + 反射的方法 */ PermissionUtils.executeFailedMethod(object,requestCode); } } }
2.PermissionUtils: PermissionUtils的輔助類
package com.justh.dell.util;
import android.app.Activity;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* Created by DELL on 2017/7/29.
*/
public class PermissionUtils {
private static final String TAG = PermissionUtils.class.getSimpleName();
/**
*
* @return true or false
* 如果大於或等於 返回true
* 否則 返回 false
*/
public static boolean isMoreThanM(){
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
/**
* 執行方法
* @param object
* @param requestCode
*/
public static void executeSuccesseMethod(Object object, int requestCode) {
Class clz = object.getClass();
//通過反射獲取到這個類的全部方法
Method[] methods = clz.getDeclaredMethods();
//遍歷所有方法,尋找被我們加了註解的方法
for (Method method : methods){
Log.i(TAG,method+"");
PermissionSucceed correctMethod = method.getAnnotation(PermissionSucceed.class);
//從註解中獲取到requestCode 比較是否與參數中的request一致,若一致,則可確定就是該方法
if(correctMethod != null){
if(correctMethod.requestCode() == requestCode){
//執行該方法
executeMethod(object,method);
}
}
}
}
//執行方法
public static void executeMethod(Object object,Method method){
try {
/**
* 代表允許執行私有方法
* 若不加這句代碼
* 在執行私有方法時,是會出問題滴
*/
method.setAccessible(true);
method.invoke(object,new Object[]{});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public static ArrayList<String> getDeniedPermissions(Object object, String[] permissions) {
//遍歷檢測所有權限 將未授權的權限添加到集合中去 並返回
ArrayList<String> deniedPermissions = new ArrayList<>();
for (String permission : permissions){
if(ContextCompat.checkSelfPermission(getActivity(object),permission) != PackageManager.PERMISSION_GRANTED){
//即爲被拒絕的權限 將其添加到集合中 後面統一處理
deniedPermissions.add(permission);
}
}
return deniedPermissions;
}
/**
*
* @param object activity or fragment
* @return Activity
*/
public static Activity getActivity(Object object){
if(object instanceof Activity){
return (Activity) object;
}else if(object instanceof Fragment){
return ((Fragment) object).getActivity();
}
return null;
}
public static void executeFailedMethod(Object object, int requestCode) {
Class clz = object.getClass();
Method[] methods = clz.getDeclaredMethods();
for (Method method : methods){
PermissionFailed permissionFailed = method.getAnnotation(PermissionFailed.class);
if(permissionFailed != null){
if(permissionFailed.requestCode() == requestCode){
executeMethod(object,method);
}
}
}
}
}
3.PermissionSucceed :成功時的註解(相當於TAG的作用)在含有全部權限授權的情況下使用,作用在執行成功的方法的頭部
package com.justh.dell.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by DELL on 2017/7/29.
* 執行正確的操作
*/
@Target(ElementType.METHOD)//代表其作用於方法中
@Retention(RetentionPolicy.RUNTIME) //表示在運行時起作用
public @interface PermissionSucceed {
public int requestCode();
}
4.PermissionFailed:失敗時的註解 在程序含有未被用戶授權的危險權限時使用,作用在執行失敗的方法的頭部
package com.justh.dell.util;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by DELL on 2017/7/29.
* 執行某些提示
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PermissionFailed {
public int requestCode();
}
5:在Activity中調用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView(){
call = (TextView) findViewById(R.id.call);
call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//檢查權限
checkPermission();
}
});
}
private void checkPermission(){
//發起請求
PermissionHelper.with(this).requestCode(REQUEST_CODE).requestPermission(Manifest.permission.CALL_PHONE).request();
}
@PermissionSucceed(requestCode = REQUEST_CODE)
private void call(){
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + mPhone);
intent.setData(data);
startActivity(intent);
}
@PermissionFailed(requestCode = REQUEST_CODE)
private void showToast(){
Toast.makeText(this,"您拒絕了撥打電話的權限!",Toast.LENGTH_SHORT).show();
}
/**
* 該方法即爲申請權限的回調方法 無論申請成功或者失敗 都會回調這個函數
* @param requestCode 上文中提到的請求碼
* @param permissions 申請的權限
* @param grantResults 申請的結果
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
PermissionHelper.requestPermissionsResult(requestCode, permissions, this);
}
到此 就真的結束了 !咵咵咵寫了這麼多,感覺都不知道到寫了啥,希望大家能看懂吧!
如若碰到什麼問題或疑問的,可以通過評論進行交流,謝謝!