Android 6.0 運(yùn)行時(shí)權(quán)限詳解

一志膀、概述

隨著Android 7.0的發(fā)布幽崩,Android 6.0的普及速度很快就升上去了,目前Android 6.0的市場(chǎng)占有率是15.2%(具體數(shù)據(jù)可以查看Android信息中心鳞绕,自從Android Developer Day大會(huì)的召開(kāi)饥努,有很多網(wǎng)站,我們開(kāi)發(fā)者可以直接訪問(wèn)了,不必再爬梯子,對(duì)國(guó)內(nèi)開(kāi)發(fā)者來(lái)說(shuō)颗胡,是很大的福音毫深。唉,扯遠(yuǎn)了.....言歸正傳)這時(shí)毒姨,我們就不得不對(duì)新版本SDK中的變化做一些適配哑蔫,這樣才能保證應(yīng)用更好的運(yùn)行。對(duì)于6.0中的變化,我們可以參考官網(wǎng)的這篇文章:Android 6.0變更。該篇文章主要對(duì)Android 6.0 運(yùn)行時(shí)權(quán)限(Runtime Permissions)做一下介紹闸迷。

這里推薦官網(wǎng)的兩篇文章嵌纲,畢竟官方的文檔才是最科學(xué)的:

二、運(yùn)行時(shí)權(quán)限

從Android6.0(API級(jí)別23)開(kāi)始腥沽,用戶開(kāi)始在應(yīng)用運(yùn)行時(shí)向其授權(quán)逮走,而不是在應(yīng)用安裝時(shí)授權(quán)。此方法可以簡(jiǎn)化應(yīng)用安裝過(guò)程今阳,同時(shí)用戶可以對(duì)應(yīng)用的功能進(jìn)行更多的控制师溅。對(duì)于6.0以下的,當(dāng)我們安裝應(yīng)用時(shí)默認(rèn)就授權(quán)所有的權(quán)限了盾舌,用戶也不了解這些權(quán)限到底有什么用墓臭,只能默默忍受。妖谴。而新的權(quán)限機(jī)制可以很好的解決這一系列問(wèn)題窿锉。Google將新的權(quán)限分為正常權(quán)限危險(xiǎn)權(quán)限

  • 正常權(quán)限:正常權(quán)限涵蓋應(yīng)用需要訪問(wèn)起沙盒外部數(shù)據(jù)或資源,但對(duì)于用戶隱私或其它應(yīng)用操作風(fēng)險(xiǎn)很小的區(qū)域膝舅。例如嗡载,設(shè)置時(shí)區(qū)的權(quán)限就是正常權(quán)限。如果應(yīng)用聲明氣需要正常的權(quán)限铸史,系統(tǒng)會(huì)自動(dòng)向應(yīng)用授予該權(quán)限鼻疮。這里可以參考官網(wǎng)的正常權(quán)限的列表

  • 危險(xiǎn)權(quán)限:危險(xiǎn)權(quán)限涵蓋應(yīng)用需要涉及用戶隱私信息的數(shù)據(jù)活資源琳轿,或者可能對(duì)用戶存儲(chǔ)的數(shù)據(jù)活其它應(yīng)用的操作產(chǎn)生影響的區(qū)域判沟。例如,讀取用戶的聯(lián)系人就屬于危險(xiǎn)權(quán)限崭篡。如果應(yīng)用聲明其需要危險(xiǎn)權(quán)限挪哄,則用戶必須明確向應(yīng)用授予該權(quán)限。其實(shí)我們?cè)陂_(kāi)發(fā)中琉闪,只要處理好危險(xiǎn)權(quán)限迹炼,正常權(quán)限的處理方式和之前一樣。下面貼出危險(xiǎn)權(quán)限圖:

我們看上面的危險(xiǎn)權(quán)限颠毙,會(huì)發(fā)現(xiàn)危險(xiǎn)權(quán)限是分組的斯入,那么分組會(huì)對(duì)我們的權(quán)限有影響嗎?的確是有影響的蛀蜜。如果你的APP運(yùn)行在Android 6.0以上的機(jī)器上(targetSdkVersion >= 23下面會(huì)細(xì)說(shuō))刻两,授權(quán)機(jī)制是這樣的。如果你申請(qǐng)某個(gè)危險(xiǎn)權(quán)限滴某,假設(shè)你的App早已被用戶授予了同一組中的某個(gè)危險(xiǎn)權(quán)限磅摹,那么系統(tǒng)會(huì)立即授權(quán)滋迈,則不會(huì)彈出對(duì)話框讓用戶去授權(quán)。例如户誓,你的App已經(jīng)對(duì)CONTACTS權(quán)限組中的READ_CONTACTS授權(quán)了饼灿,當(dāng)你的App申請(qǐng)WRITE_CONTACTS權(quán)限時(shí),系統(tǒng)則會(huì)直接授權(quán)通過(guò)帝美。此外碍彭,對(duì)于申請(qǐng)時(shí)彈出Dialog的文本說(shuō)明也是對(duì)整個(gè)權(quán)限組的說(shuō)明,而不是單個(gè)權(quán)限证舟。這里需要注意的是:彈出的Dialog是系統(tǒng)提供硕旗,我們是不能進(jìn)行定制的窗骑。

三女责、權(quán)限適配

首先我們按照之前的方式來(lái)申請(qǐng)撥打電話的權(quán)限(撥打電話權(quán)限),在Android 6.0(targetSdkVersion >= 23)手機(jī)上進(jìn)行測(cè)試创译。

//首先在清單文件中申請(qǐng)撥打電話的權(quán)限
<uses-permission android:name="android.permission.CALL_PHONE"/>

//在Button的點(diǎn)擊事件中抵知,使用Intent撥打電話
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + phoneNumber));
startActivity(intent);        //此行代碼會(huì)報(bào)紅線。(android studio 2.2.2版本)看來(lái)AS還是挺人性化的软族。

運(yùn)行App刷喜,點(diǎn)擊撥打電話按鈕,你會(huì)發(fā)現(xiàn)App崩潰了立砸。掖疮。。下面貼出異常原因圖:

從圖中可以很清楚的看到是因?yàn)镾ecurityException權(quán)限異常颗祝。解決這個(gè)異常有兩種方法:

  1. 在android studio中浊闪,打開(kāi)build.gradle(module:app)文件,將targetSdkVersion的版本號(hào)修改為低于23的螺戳,即可解決該異常搁宾。那就繼續(xù)使用舊有規(guī)則:用戶在安裝的時(shí)候不得不接受所有權(quán)限,安裝后app就有了那些權(quán)限咯倔幼!
  2. 使用Android提供的相關(guān)API進(jìn)行權(quán)限的檢查盖腿,避免這個(gè)異常。

但是作為一個(gè)有“情懷”的程序猿损同,我們?cè)趺纯赡苡玫谝环N這么low的方法去解決問(wèn)題呢翩腐。下面我們使用Android提供的相關(guān)API來(lái)處理異常。

  1. 首先在清單文件中申請(qǐng)撥打電話的權(quán)限膏燃,這一步是必不可少的茂卦。

     <uses-permission android:name="android.permission.CALL_PHONE"/>
    
  2. 在Button的點(diǎn)擊事件,撥打電話前蹄梢,首先使用ActivityCompat.checkSelfPermission()方法檢查是否有撥打電話權(quán)限(ActivityCompat和ContextCompat是子父類的關(guān)系)疙筹,該方法有兩個(gè)int類型的返回值:分別為PERMISSION_GRANTED(表示應(yīng)用有此權(quán)限)和PERMISSION_DENIED(表示應(yīng)用沒(méi)有權(quán)限)富俄,如果此時(shí)返回值為PERMISSION_DENIED,那么我們就應(yīng)該手動(dòng)去請(qǐng)求應(yīng)用的權(quán)限而咆,看代碼霍比。
    
     if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
         /**
          * 請(qǐng)求撥打電話權(quán)限
          * 該方法是異步的,第一個(gè)參數(shù)是Context暴备;
          * 第二個(gè)參數(shù)是需要申請(qǐng)的權(quán)限的字符串?dāng)?shù)組悠瞬;
          * 第三個(gè)參數(shù)為requestCode,主要用于回調(diào)的時(shí)候檢測(cè)涯捻。
          * 可以從方法名requestPermissions以及第二個(gè)參數(shù)看出浅妆,是支持一次性申請(qǐng)多個(gè)權(quán)限的,系統(tǒng)會(huì)通過(guò)對(duì)話框逐一詢問(wèn)用戶是否授權(quán)障癌。
          */
         ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1);
     } else {
         //有權(quán)限,直接調(diào)用撥打電話的方法
         mLoginPresenter.call(this);
     }
    
  3. 在Activity中重寫(xiě)onRequestPermissionsResult方法凌外,處理請(qǐng)求權(quán)限的回調(diào)。首先驗(yàn)證requestCode定位到你的申請(qǐng)涛浙,然后驗(yàn)grantResults對(duì)應(yīng)于申請(qǐng)的結(jié)果康辑,這里的數(shù)組對(duì)應(yīng)于申請(qǐng)時(shí)的第二個(gè)權(quán)限字符串?dāng)?shù)組。如果你同時(shí)申請(qǐng)兩個(gè)權(quán)限轿亮,那么grantResults的length就為2疮薇,分別記錄你兩個(gè)權(quán)限的申請(qǐng)結(jié)果。如果申請(qǐng)成功我注,就可以做你的事情了按咒。

     @Override
     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
         switch (requestCode) {
             case 1:
                 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                     mLoginPresenter.call(this);
                 } else {
                     Toast.makeText(this, "未授權(quán)撥打電話權(quán)限", Toast.LENGTH_LONG).show();
                 }
                 break;
         }
     }
    

申請(qǐng)權(quán)限的基本步驟就如上所示,沒(méi)圖沒(méi)真相但骨。接下來(lái)我們就來(lái)看下真相吧励七。上圖。嗽冒。呀伙。


如果用戶拒絕某授權(quán)。下一次彈框添坊,用戶會(huì)有一個(gè)“不再提醒”的選項(xiàng)的來(lái)防止app以后繼續(xù)請(qǐng)求授權(quán)剿另。如果這個(gè)選項(xiàng)在拒絕授權(quán)前被用戶勾選了,那么下次你再點(diǎn)擊撥打電話時(shí)贬蛙,Dialog將不會(huì)在提示雨女,App什么也不干,這對(duì)用戶來(lái)說(shuō)是很差的體驗(yàn)阳准。后文會(huì)說(shuō)處理的方法氛堕。

注意:不同手機(jī)上,可能提示的方式不同野蝇,下面看下下米手機(jī)上的提示讼稚。(小米4手機(jī)上即使你拒絕很多次括儒,它的那個(gè)Dialog上也不會(huì)出現(xiàn)“不在詢問(wèn)”的勾選框),可能是國(guó)內(nèi)的手機(jī)廠商對(duì)Rom做了處理锐想。

四帮寻、更優(yōu)雅的處理權(quán)限提示問(wèn)題

如果用戶拒絕某授權(quán)。下一次彈框赠摇,用戶會(huì)有一個(gè)“不再提醒”的選項(xiàng)的來(lái)防止app以后繼續(xù)請(qǐng)求授權(quán)固逗。如果這個(gè)選項(xiàng)在拒絕授權(quán)前被用戶勾選了。下次為這個(gè)權(quán)限請(qǐng)求requestPermissions時(shí)藕帜,對(duì)話框就不彈出來(lái)了烫罩,結(jié)果就是,app啥都不干洽故。這將是很差的用戶體驗(yàn)贝攒,用戶做了操作卻得不到響應(yīng)。這種情況需要好好處理一下收津。在請(qǐng)求requestPermissions前饿这,我們需要檢查是否需要展示請(qǐng)求權(quán)限的提示通過(guò)activity的shouldShowRequestPermissionRationale方法浊伙,如果該方法返回true撞秋,則表示用戶已經(jīng)拒絕過(guò)一次權(quán)限,此時(shí)我們應(yīng)該彈一個(gè)消息提示框嚣鄙,表明請(qǐng)求該權(quán)限的原因吻贿,讓用戶授權(quán)該權(quán)限。代碼如下:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
            showSecurityMessage("是否授權(quán)撥打電話權(quán)限哑子,若未授權(quán)舅列,則不能撥打電話。", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    ActivityCompat.requestPermissions(LoginActivity.this,
                            new String[]{Manifest.permission.CALL_PHONE},
                            1);
                }
            });
            return;
        }
        /**
         * 請(qǐng)求撥打電話權(quán)限
         * 該方法是異步的卧蜓,第一個(gè)參數(shù)是Context帐要;
         * 第二個(gè)參數(shù)是需要申請(qǐng)的權(quán)限的字符串?dāng)?shù)組;
         * 第三個(gè)參數(shù)為requestCode弥奸,主要用于回調(diào)的時(shí)候檢測(cè)榨惠。
         * 可以從方法名requestPermissions以及第二個(gè)參數(shù)看出,是支持一次性申請(qǐng)多個(gè)權(quán)限的盛霎,系統(tǒng)會(huì)通過(guò)對(duì)話框逐一詢問(wèn)用戶是否授權(quán)赠橙。
         */
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1);
    } else {
        //有權(quán)限,直接撥打
        mLoginPresenter.call(this);
    }   

private void showSecurityMessage(String message, DialogInterface.OnClickListener okListener) {
    new AlertDialog.Builder(this)
            .setMessage(message)
            .setPositiveButton("是", okListener)
            .setNegativeButton("否", null)
            .create()
            .show();
}

注:當(dāng)你一次請(qǐng)求多個(gè)權(quán)限時(shí),不要忘了為沒(méi)個(gè)權(quán)限添加解釋說(shuō)明愤炸。

效果圖:

五期揪、使用兼容庫(kù)兼容舊版本

以上代碼在android 6.0以上運(yùn)行沒(méi)有問(wèn)題,但是API 23之前的就不行了规个,因?yàn)闆](méi)有那些方法凤薛。粗暴的方法就是檢查版本:

if (Build.VERSION.SDK_INT >= 23) {
    // Marshmallow+
} else {
    // Pre-Marshmallow
}

但是太復(fù)雜姓建,這里我們可以使用v4兼容庫(kù),已對(duì)這個(gè)做過(guò)兼容缤苫,用以下函數(shù)代替:

  • ContextCompat.checkSelfPermission() 被授權(quán)函數(shù)返回PERMISSION_GRANTED引瀑,否則返回PERMISSION_DENIED ,在所有版本都是如此榨馁。
  • ActivityCompat.requestPermissions() 這個(gè)方法在M之前版本調(diào)用憨栽,OnRequestPermissionsResultCallback 直接被調(diào)用,帶著正確的 PERMISSION_GRANTED或者 PERMISSION_DENIED結(jié)束 翼虫。
  • ActivityCompat.shouldShowRequestPermissionRationale() 如果此函數(shù)在M之前調(diào)用屑柔,它將永遠(yuǎn)返回false。

用v4包的這三方法珍剑,完美兼容所有版本掸宛!這個(gè)方法需要額外的參數(shù),Context or Activity招拙。其它的就沒(méi)什么特別的了唧瘾。上面的后兩個(gè)方法,我們也可以在Fragment中使用别凤,用v13兼容包:FragmentCompat.requestPermissions() and FragmentCompat.shouldShowRequestPermissionRationale()和activity效果一樣饰序。

六、使用三方開(kāi)源庫(kù)

以上代碼在實(shí)際開(kāi)發(fā)中寫(xiě)著還是很麻煩的规哪,只有申請(qǐng)的權(quán)限是危險(xiǎn)權(quán)限求豫,那么就要去檢查。當(dāng)然诉稍,你也可以自己去封裝下蝠嘉,方便自己使用。下面是github上star數(shù)最多的關(guān)于Permissions庫(kù)杯巨,大家在開(kāi)發(fā)中可以直接使用蚤告。

首發(fā)地址:http://blog.csdn.net/listeners_gao/article/details/53606845
參考文章:https://inthecheesefactory.com/blog/things-you-need-to-know-about-android-m-permission-developer-edition/en

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市服爷,隨后出現(xiàn)的幾起案子杜恰,更是在濱河造成了極大的恐慌,老刑警劉巖层扶,帶你破解...
    沈念sama閱讀 218,682評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件箫章,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡镜会,警方通過(guò)查閱死者的電腦和手機(jī)檬寂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)戳表,“玉大人桶至,你說(shuō)我怎么就攤上這事昼伴。” “怎么了镣屹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,083評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵圃郊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我女蜈,道長(zhǎng)持舆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,763評(píng)論 1 295
  • 正文 為了忘掉前任伪窖,我火速辦了婚禮逸寓,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘覆山。我一直安慰自己竹伸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布簇宽。 她就那樣靜靜地躺著勋篓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪魏割。 梳的紋絲不亂的頭發(fā)上譬嚣,一...
    開(kāi)封第一講書(shū)人閱讀 51,624評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音见妒,去河邊找鬼孤荣。 笑死,一個(gè)胖子當(dāng)著我的面吹牛须揣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钱豁,決...
    沈念sama閱讀 40,358評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了歉糜?” 一聲冷哼從身側(cè)響起戳玫,我...
    開(kāi)封第一講書(shū)人閱讀 39,261評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谤碳,沒(méi)想到半個(gè)月后溃卡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,722評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜒简,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年瘸羡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搓茬。...
    茶點(diǎn)故事閱讀 40,030評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡犹赖,死狀恐怖队他,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情峻村,我是刑警寧澤麸折,帶...
    沈念sama閱讀 35,737評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站粘昨,受9級(jí)特大地震影響垢啼,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜张肾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評(píng)論 3 330
  • 文/蒙蒙 一膊夹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧捌浩,春花似錦放刨、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,941評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至浪听,卻和暖如春螟碎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背迹栓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,057評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工掉分, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人克伊。 一個(gè)月前我還...
    沈念sama閱讀 48,237評(píng)論 3 371
  • 正文 我出身青樓酥郭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親愿吹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子不从,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評(píng)論 2 355

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