Android6.0運(yùn)行時(shí)權(quán)限封裝(避免用戶選擇不再提示后無法獲取權(quán)限的問題)

前言
Android 6.0 為了保護(hù)用戶隱私廉赔,將一些權(quán)限的申請放在了應(yīng)用運(yùn)行的時(shí)候去申請芋浮, 比如以往的開發(fā)中招狸,開發(fā)人員只需要將需要的權(quán)限在清單文件中配置即可婿滓,安裝后用戶可以在設(shè)置中的應(yīng)用信息中看到:XX應(yīng)用以獲取****權(quán)限甜孤。用戶點(diǎn)擊可以選擇給應(yīng)用相應(yīng)的權(quán)限协饲。此前的應(yīng)用權(quán)限用戶可以選擇允許、提醒和拒絕缴川。在安裝的時(shí)候用戶是已經(jīng)知道應(yīng)用需要的權(quán)限的茉稠。但是這樣存在一個(gè)問題,就是用戶在安裝的時(shí)候把夸,應(yīng)用需要的權(quán)限十分的多(有些開發(fā)者為了省事而线,會請求一些不必要的權(quán)限或者請求全部的權(quán)限),這個(gè)時(shí)候用戶在安裝應(yīng)用的時(shí)候也許并沒有發(fā)現(xiàn)某些侵犯自己隱私的權(quán)限請求恋日,安裝之后才發(fā)現(xiàn)自己的隱私數(shù)據(jù)被竊取膀篮。其實(shí)Android6.0 動態(tài)權(quán)限一方面是為了廣大用戶考慮,另一方面其實(shí)是Google為了避免一些不必要的官司岂膳。
目前針對Android6.0權(quán)限適配的邏輯代碼誓竿,網(wǎng)上已經(jīng)很多了。這里針對用戶拒絕了權(quán)限請求并且選擇了□ 不在提示”時(shí)該如何處理谈截。

權(quán)限流程
▲在Api 23中, 權(quán)限需要?jiǎng)討B(tài)獲取, 核心權(quán)限必須滿足. 標(biāo)準(zhǔn)流程:

▲但這里有個(gè)問題筷屡,那就是在系統(tǒng)授權(quán)彈窗環(huán)節(jié)涧偷,提醒框會有個(gè)不再提示的復(fù)選框,如果用戶點(diǎn)擊不再提示毙死,并拒絕授權(quán)燎潮,那么再下次授權(quán)的時(shí)候,系統(tǒng)授權(quán)彈窗的提示框就不會在提示扼倘,所以我們很有必要需要自定義權(quán)限彈窗提示框确封,那么流程圖就變成如下了。

權(quán)限類型


▲ 在圖中唉锌,我們可以看到整個(gè)權(quán)限里隅肥,可以分為系統(tǒng)權(quán)限和特殊權(quán)限授權(quán)。系統(tǒng)權(quán)限中袄简,又分為normal和dangerous類型腥放。
normal:這個(gè)權(quán)限類型并不直接威脅到用戶的隱私,可以直接在manifest清單里注冊绿语,系統(tǒng)會幫我們默認(rèn)授權(quán)的秃症。
dangerous:這個(gè)可以直接給app訪問用戶一些敏感的數(shù)據(jù),不僅需要在manifest清單里注冊吕粹,同時(shí)在使用的時(shí)候种柑,需要向系統(tǒng)請求授權(quán)。
值得注意一點(diǎn)匹耕,這里有特殊權(quán)限授權(quán)的區(qū)別聚请,分別是SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS,雖然這兩個(gè)權(quán)限也是屬于dangerous權(quán)限類型稳其,但是這兩個(gè)授權(quán)請求方式和其他dangerous權(quán)限是不一樣的驶赏,需要特殊處理 。



案例介紹
根據(jù)官方介紹既鞠,當(dāng)發(fā)起首次權(quán)限請求時(shí)不會提供“不再提示”這一選擇煤傍,但如果用戶之前拒絕過權(quán)限請求并且再次發(fā)起權(quán)限請求時(shí),權(quán)限請求對話框才會提供“不在提示”這一選項(xiàng)供用戶選擇嘱蛋,當(dāng)用戶選擇了“不再提示”之后就不會再彈出權(quán)限請求對話框蚯姆。各大手機(jī)廠商的定制系統(tǒng)對該邏輯進(jìn)行了一定的改動,例如小米MIUI系統(tǒng)當(dāng)用戶首次選擇拒絕權(quán)限時(shí)之后就會“不在提示”了洒敏,華為EMUI系統(tǒng)在首次彈出權(quán)限請求時(shí)就會提供“不在提示”的選項(xiàng)龄恋,三星部分系統(tǒng)始終不提供“不在提示”的選項(xiàng)。
當(dāng)用戶選擇了“不在提示”時(shí),開發(fā)者需要引導(dǎo)用戶去設(shè)置頁手動授權(quán)!
但是android 6.0系統(tǒng)并未提供對“不在提示”這一選項(xiàng)的監(jiān)聽摇零,那么開發(fā)者該如何判斷用戶是否選擇了“不在提示”這一選項(xiàng)呢柳刮?
答案是通過shouldShowRequestPermissionRationale()這一方法豁翎。
這一方法的作用是判斷是否需要向用戶解釋為何需要請求該權(quán)限主经。
當(dāng)首次發(fā)起權(quán)限請求時(shí)該方法返回false陪白。
當(dāng)?shù)诙螜?quán)限請求時(shí)該方法返回true窍株。
當(dāng)發(fā)起第二次權(quán)限請求并且當(dāng)用戶選擇了“不再提示”這一選項(xiàng)時(shí)該方法返回false煮落。 通過這個(gè)邏輯我們可以反推出:當(dāng)進(jìn)行第二次權(quán)限請求被拒絕并且shouldShowRequestPermissionRationale()返回false時(shí)敞峭,那么該用戶一定是選擇了“不再提示”這一選項(xiàng)。

▲以RECORD_AUDIO蝉仇,CAMERA旋讹,READ_EXTERNAL_STORAGE為例。全部權(quán)限請求成功轿衔,直接看妹砸沉迹。下面看圖 ヾ(?°?°?)?? 沒圖我會亂說

▲當(dāng)部分權(quán)限受限,且用戶未選中“不再提示”害驹,彈出對話框說明鞭呕,并進(jìn)行請求權(quán)限


▲當(dāng)部分權(quán)限受限,且用戶選中“不再提示”宛官,彈出對話框說明葫松,引導(dǎo)用戶到設(shè)置頁手動授權(quán)


▲主要代碼BaseActivity.java

package com.donkor.demo.permission;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;

public class BaseActivity extends AppCompatActivity {

    private final int mRequestCode = 1024;
    private RequestPermissionCallBack mRequestPermissionCallBack;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    /**
     * 權(quán)限請求結(jié)果回調(diào)
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        boolean hasAllGranted = true;
        StringBuilder permissionName = new StringBuilder();
        for (String s : permissions) {
            permissionName = permissionName.append(s + "\r\n");
        }
        switch (requestCode) {
            case mRequestCode: {
                for (int i = 0; i < grantResults.length; ++i) {
                    if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                        hasAllGranted = false;
                        //在用戶已經(jīng)拒絕授權(quán)的情況下,如果shouldShowRequestPermissionRationale返回false則
                        // 可以推斷出用戶選擇了“不在提示”選項(xiàng)底洗,在這種情況下需要引導(dǎo)用戶至設(shè)置頁手動授權(quán)
                        if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
                            new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//設(shè)置對話框標(biāo)題
                                    .setMessage("【用戶選擇了不再提示按鈕腋么,或者系統(tǒng)默認(rèn)不在提示(如MIUI)。" +
                                            "引導(dǎo)用戶到應(yīng)用設(shè)置頁去手動授權(quán),注意提示用戶具體需要哪些權(quán)限】" +
                                            "獲取相關(guān)權(quán)限失敗:" + permissionName +
                                            "將導(dǎo)致部分功能無法正常使用亥揖,需要到設(shè)置頁面手動授權(quán)")//設(shè)置顯示的內(nèi)容
                                    .setPositiveButton("去授權(quán)", new DialogInterface.OnClickListener() {//添加確定按鈕
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {//確定按鈕的響應(yīng)事件
                                            //TODO Auto-generated method stub
                                            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                            Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
                                            intent.setData(uri);
                                            startActivity(intent);
                                            dialog.dismiss();
                                        }
                                    }).setNegativeButton("取消", new DialogInterface.OnClickListener() {//添加返回按鈕
                                @Override
                                public void onClick(DialogInterface dialog, int which) {//響應(yīng)事件
                                    // TODO Auto-generated method stub
                                    dialog.dismiss();
                                }
                            }).setOnCancelListener(new DialogInterface.OnCancelListener() {
                                @Override
                                public void onCancel(DialogInterface dialog) {
                                    mRequestPermissionCallBack.denied();
                                }
                            }).show();//在按鍵響應(yīng)事件中顯示此對話框
                        } else {
                            //用戶拒絕權(quán)限請求珊擂,但未選中“不再提示”選項(xiàng)
                            mRequestPermissionCallBack.denied();
                        }
                        break;
                    }
                }
                if (hasAllGranted) {
                    mRequestPermissionCallBack.granted();
                }
            }
        }
    }

    /**
     * 發(fā)起權(quán)限請求
     *
     * @param context
     * @param permissions
     * @param callback
     */
    public void requestPermissions(final Context context, final String[] permissions,
                                   RequestPermissionCallBack callback) {
        this.mRequestPermissionCallBack = callback;
        StringBuilder permissionNames = new StringBuilder();
        for (String s : permissions) {
            permissionNames = permissionNames.append(s + "\r\n");
        }
        //如果所有權(quán)限都已授權(quán),則直接返回授權(quán)成功,只要有一項(xiàng)未授權(quán)费变,則發(fā)起權(quán)限請求
        boolean isAllGranted = true;
        for (String permission : permissions) {
            if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_DENIED) {
                isAllGranted = false;
                if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) context, permission)) {
                    new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//設(shè)置對話框標(biāo)題
                            .setMessage("【用戶曾經(jīng)拒絕過你的請求未玻,所以這次發(fā)起請求時(shí)解釋一下】" +
                                    "您好,需要如下權(quán)限:" + permissionNames +
                                    " 請?jiān)试S胡控,否則將影響部分功能的正常使用。")//設(shè)置顯示的內(nèi)容
                            .setPositiveButton("確定", new DialogInterface.OnClickListener() {//添加確定按鈕
                                @Override
                                public void onClick(DialogInterface dialog, int which) {//確定按鈕的響應(yīng)事件
                                    //TODO Auto-generated method stub
                                    ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode);
                                }
                            }).show();//在按鍵響應(yīng)事件中顯示此對話框
                } else {
                    ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode);
                }
                break;
            }
        }
        if (isAllGranted) {
            mRequestPermissionCallBack.granted();
            return;
        }
    }

    /**
     * 權(quán)限請求結(jié)果回調(diào)接口
     */
    interface RequestPermissionCallBack {
        /**
         * 同意授權(quán)
         */
        void granted();

        /**
         * 取消授權(quán)
         */
        void denied();
    }
}

▲SplashActivity.java中Button請求權(quán)限主要代碼

requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO,
                        Manifest.permission.CAMERA,
                        Manifest.permission.READ_EXTERNAL_STORAGE},
                new RequestPermissionCallBack() {
                    @Override
                    public void granted() {
                        Toast.makeText(SplashActivity.this, "權(quán)限獲取成功旁趟,執(zhí)行下一步操作", Toast.LENGTH_SHORT).show();
                        Intent intent = new Intent(SplashActivity.this, MainActivity.class);
                        startActivity(intent);
                        SplashActivity.this.finish();
                    }

                    @Override
                    public void denied() {
                        Toast.makeText(SplashActivity.this, "部分權(quán)限獲取失敗昼激,正常功能受到影響,2秒鐘之后自動退出", Toast.LENGTH_LONG).show();
                        //2秒鐘之后自動退出
                        mHandler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                SplashActivity.this.finish();
                            }
                        }, 2000);
                    }
                });

資料與參考:
http://www.cnblogs.com/cr330326/p/5181283.html
http://blog.csdn.net/caroline_wendy/article/details/50587230


妹砸照片及源碼下載地址 : https://github.com/ChenYXin/PermissionDemo

關(guān)于我

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锡搜,隨后出現(xiàn)的幾起案子橙困,更是在濱河造成了極大的恐慌,老刑警劉巖耕餐,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凡傅,死亡現(xiàn)場離奇詭異,居然都是意外死亡肠缔,警方通過查閱死者的電腦和手機(jī)夏跷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門哼转,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人槽华,你說我怎么就攤上這事壹蔓。” “怎么了猫态?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵佣蓉,是天一觀的道長。 經(jīng)常有香客問我亲雪,道長勇凭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任义辕,我火速辦了婚禮虾标,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘终息。我一直安慰自己夺巩,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布周崭。 她就那樣靜靜地躺著柳譬,像睡著了一般。 火紅的嫁衣襯著肌膚如雪续镇。 梳的紋絲不亂的頭發(fā)上美澳,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音摸航,去河邊找鬼制跟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酱虎,可吹牛的內(nèi)容都是我干的雨膨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼读串,長吁一口氣:“原來是場噩夢啊……” “哼聊记!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恢暖,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤排监,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后杰捂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舆床,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了挨队。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谷暮。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瞒瘸,靈堂內(nèi)的尸體忽然破棺而出坷备,到底是詐尸還是另有隱情,我是刑警寧澤情臭,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布省撑,位于F島的核電站,受9級特大地震影響俯在,放射性物質(zhì)發(fā)生泄漏竟秫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一跷乐、第九天 我趴在偏房一處隱蔽的房頂上張望肥败。 院中可真熱鬧,春花似錦愕提、人聲如沸馒稍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纽谒。三九已至,卻和暖如春如输,著一層夾襖步出監(jiān)牢的瞬間鼓黔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工不见, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澳化,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓稳吮,卻偏偏與公主長得像缎谷,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子灶似,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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