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

前言
Android 6.0 為了保護(hù)用戶隱私,將一些權(quán)限的申請(qǐng)放在了應(yīng)用運(yùn)行的時(shí)候去申請(qǐng), 比如以往的開(kāi)發(fā)中,開(kāi)發(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è)問(wèn)題,就是用戶在安裝的時(shí)候,應(yīng)用需要的權(quán)限十分的多(有些開(kāi)發(fā)者為了省事,會(huì)請(qǐng)求一些不必要的權(quán)限或者請(qǐng)求全部的權(quán)限),這個(gè)時(shí)候用戶在安裝應(yīng)用的時(shí)候也許并沒(méi)有發(fā)現(xiàn)某些侵犯自己隱私的權(quán)限請(qǐng)求,安裝之后才發(fā)現(xiàn)自己的隱私數(shù)據(jù)被竊取。其實(shí)Android6.0 動(dòng)態(tài)權(quán)限一方面是為了廣大用戶考慮,另一方面其實(shí)是Google為了避免一些不必要的官司。
目前針對(duì)Android6.0權(quán)限適配的邏輯代碼,網(wǎng)上已經(jīng)很多了。這里針對(duì)用戶拒絕了權(quán)限請(qǐng)求并且選擇了□ 不在提示”時(shí)該如何處理。

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

▲但這里有個(gè)問(wèn)題,那就是在系統(tǒng)授權(quán)彈窗環(huán)節(jié),提醒框會(huì)有個(gè)不再提示的復(fù)選框,如果用戶點(diǎn)擊不再提示,并拒絕授權(quán),那么再下次授權(quán)的時(shí)候,系統(tǒng)授權(quán)彈窗的提示框就不會(huì)在提示,所以我們很有必要需要自定義權(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清單里注冊(cè),系統(tǒng)會(huì)幫我們默認(rèn)授權(quán)的。
dangerous:這個(gè)可以直接給app訪問(wèn)用戶一些敏感的數(shù)據(jù),不僅需要在manifest清單里注冊(cè),同時(shí)在使用的時(shí)候,需要向系統(tǒng)請(qǐng)求授權(quán)。
值得注意一點(diǎn),這里有特殊權(quán)限授權(quán)的區(qū)別,分別是SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS,雖然這兩個(gè)權(quán)限也是屬于dangerous權(quán)限類型,但是這兩個(gè)授權(quán)請(qǐng)求方式和其他dangerous權(quán)限是不一樣的,需要特殊處理 。



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

▲以RECORD_AUDIO,CAMERA,READ_EXTERNAL_STORAGE為例。全部權(quán)限請(qǐng)求成功,直接看妹砸。下面看圖 ヾ(?°?°?)?? 沒(méi)圖我會(huì)亂說(shuō)

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


▲當(dāng)部分權(quán)限受限,且用戶選中“不再提示”,彈出對(duì)話框說(shuō)明,引導(dǎo)用戶到設(shè)置頁(yè)手動(dòng)授權(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)限請(qǐng)求結(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è)置頁(yè)手動(dòng)授權(quán)
                        if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
                            new AlertDialog.Builder(BaseActivity.this).setTitle("PermissionTest")//設(shè)置對(duì)話框標(biāo)題
                                    .setMessage("【用戶選擇了不再提示按鈕,或者系統(tǒng)默認(rèn)不在提示(如MIUI)。" +
                                            "引導(dǎo)用戶到應(yīng)用設(shè)置頁(yè)去手動(dòng)授權(quán),注意提示用戶具體需要哪些權(quán)限】" +
                                            "獲取相關(guān)權(quán)限失敗:" + permissionName +
                                            "將導(dǎo)致部分功能無(wú)法正常使用,需要到設(shè)置頁(yè)面手動(dòng)授權(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)事件中顯示此對(duì)話框
                        } else {
                            //用戶拒絕權(quán)限請(qǐng)求,但未選中“不再提示”選項(xiàng)
                            mRequestPermissionCallBack.denied();
                        }
                        break;
                    }
                }
                if (hasAllGranted) {
                    mRequestPermissionCallBack.granted();
                }
            }
        }
    }

    /**
     * 發(fā)起權(quán)限請(qǐng)求
     *
     * @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)限請(qǐng)求
        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è)置對(duì)話框標(biāo)題
                            .setMessage("【用戶曾經(jīng)拒絕過(guò)你的請(qǐng)求,所以這次發(fā)起請(qǐng)求時(shí)解釋一下】" +
                                    "您好,需要如下權(quán)限:" + permissionNames +
                                    " 請(qǐng)?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)事件中顯示此對(duì)話框
                } else {
                    ActivityCompat.requestPermissions(((Activity) context), permissions, mRequestCode);
                }
                break;
            }
        }
        if (isAllGranted) {
            mRequestPermissionCallBack.granted();
            return;
        }
    }

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

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

▲SplashActivity.java中Button請(qǐng)求權(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秒鐘之后自動(dòng)退出", Toast.LENGTH_LONG).show();
                        //2秒鐘之后自動(dòng)退出
                        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)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評(píng)論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,034評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 177,413評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 63,449評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,165評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 55,559評(píng)論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 42,781評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,327評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,084評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,278評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,495評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 34,927評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 36,172評(píng)論 1 291
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,010評(píng)論 3 396
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,241評(píng)論 2 375

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