Android 6.0動態權限申請

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

到此 動態權限的功能也就處理完畢了!

你以爲我會說結束了麼,哈哈哈,想多了吧,老鐵!^_^

不知道讀到這裏,大家有沒有一個問題:難道我一涉及到危險權限的操作,就要寫這麼多代碼來處理嗎?這樣會不會很喪心病狂啊!

下面,高大上的東西來了:封裝一個高大上的權限申請框架!

寫博客之前,也看了好些技術求人的方案,最終選擇的是一個註解 + 反射的方案來處理

下面直接貼代碼,註釋已經很詳細了,相信大家能看明白!

  1. 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);
    }

到此 就真的結束了 !咵咵咵寫了這麼多,感覺都不知道到寫了啥,希望大家能看懂吧!

如若碰到什麼問題或疑問的,可以通過評論進行交流,謝謝!

發佈了23 篇原創文章 · 獲贊 10 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章