Android6.0中的運(yùn)行時(shí)請(qǐng)求權(quán)限

原文鏈接:https://blog.lujun.co/2015/12/07/Android6.0%E4%B8%AD%E7%9A%84%E8%BF%90%E8%A1%8C%E6%97%B6%E8%AF%B7%E6%B1%82%E6%9D%83%E9%99%90/

問題來源于最近的一個(gè)項(xiàng)目中淌实,原本好好的程序睡汹,在一臺(tái)Nexus 6(Android6.0)上測(cè)試發(fā)現(xiàn)所有需要保存圖片到本地的都不行,看報(bào)錯(cuò):


error_1.png

其實(shí)不僅僅是保存圖片這里報(bào)這個(gè)錯(cuò),還有打開相機(jī)的時(shí)候也會(huì)報(bào)錯(cuò)拢切。好了問題就這么開始了!

看到這里都知道是權(quán)限的問題所引起的原因啃奴,對(duì)扯旷,確實(shí)是因?yàn)闄?quán)限的問題引起的,但是程序明明已經(jīng)在Manifest中聲明了以上操作需要的權(quán)限培己,而且在以前測(cè)試的系統(tǒng)機(jī)型中都是沒問題的碳蛋,為什么!突然間想到了Marshmallow發(fā)布的時(shí)候提及到的Requesting Permissions at Run Time這東西省咨,對(duì)就是他的原因肃弟!

Requesting Permissions at Run Time究竟是啥,官方對(duì)他是如下介紹滴:
從Android6.0(API >= 23)開始零蓉,用戶在APP運(yùn)行的時(shí)候授予其權(quán)限而不是像以前安裝的時(shí)候就通通授予了(以前授權(quán)方式好像沒什么卵用)笤受。由于不在需要在安裝或更新APP的時(shí)候授予相關(guān)權(quán)限,這就簡化了APP的安裝過程敌蜂。這也提高了用戶對(duì)APP功能的控制箩兽,比如:用戶可以選擇讓一個(gè)Camera APP使用Camera,用戶可以在任何時(shí)候在設(shè)置面板撤銷這個(gè)權(quán)限章喉。汗贫。。
看完是不是有點(diǎn)像我們?cè)趪a(chǎn)ROM中常見到的每個(gè)應(yīng)用運(yùn)行時(shí)權(quán)限授予秸脱。

系統(tǒng)權(quán)限也被分成normaldangerous兩類:

  • Normal類的權(quán)限不會(huì)直接涉及到用戶隱私風(fēng)險(xiǎn)落包。如果APP在Manifest文件中聲明了Normal類的權(quán)限,系統(tǒng)會(huì)自動(dòng)授予這些權(quán)限摊唇。
  • Dangerous類的權(quán)限可能會(huì)讓APP涉及到用戶機(jī)密的數(shù)據(jù)咐蝇。如果APP在Manifest文件中聲明了Normal類的權(quán)限,系統(tǒng)會(huì)自動(dòng)授予這些權(quán)限巷查。如果在Manifest文件中添加了Dangerous類的權(quán)限有序,用戶必須明確的授予對(duì)應(yīng)的權(quán)限后APP才具有這些權(quán)限撮竿。
    關(guān)于哪些權(quán)限屬于Normal類,哪些屬于Dangerous類笔呀,如下圖:


    normal-dangerous-permissions.png

更詳細(xì)請(qǐng)看normal-dangerous-permissions文檔。

現(xiàn)在我們知道了在APP中normal和dangerous類的權(quán)限都需要Manifest文件中聲明髓需,但是在不同的版本系統(tǒng)和target SDK效果是不一樣的许师,有如下幾點(diǎn)需要注意:

  • 系統(tǒng)Android 5.1以下或target SDK 22以下,只要在Manifest文件中聲明了需要的權(quán)限僚匆,用戶在安裝APP的時(shí)候就會(huì)授予相應(yīng)的權(quán)限微渠;如果不授予當(dāng)然APP也無法安裝。
  • 如果設(shè)備運(yùn)行在6.0以上并且你的應(yīng)用的target SDK版本>=23咧擂,APP除了需要在Manifest文件中聲明相應(yīng)的權(quán)限之外逞盆,還要在APP運(yùn)行時(shí)向用戶進(jìn)行請(qǐng)求每個(gè)dangerous類的權(quán)限。用戶可以選擇授予或不授予該權(quán)限松申,即使用戶不授予該權(quán)限APP也可以繼續(xù)運(yùn)行云芦,但是相關(guān)的需要權(quán)限的操作是沒法進(jìn)行的。

使用系統(tǒng)提供的API檢查并請(qǐng)求權(quán)限

a. 檢查權(quán)限

以為用戶可以隨時(shí)撤銷對(duì)APP的授權(quán)贸桶,所以在每次準(zhǔn)備進(jìn)行需要dangerous類權(quán)限操作的時(shí)候舅逸,需要檢查APP是否具有對(duì)應(yīng)的權(quán)限。使用[ContextCompat.checkSelfPermission()](http://developer.android.com/reference/android/support/v4/content/ContextCompat.html#checkSelfPermission(android.content.Context, java.lang.String))檢查權(quán)限皇筛,代碼如下:

// 檢查activity是否有寫日程的權(quán)限
// Assume thisActivity is the current activity
intpermissionCheck=ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.WRITE_CALENDAR);

上面代碼琉历,如果APP有該權(quán)限返回PackageManager.PERMISSION_GRANTED,APP接著可以進(jìn)行對(duì)應(yīng)操作水醋;如果沒有權(quán)限旗笔,以上方法返回PERMISSION_DENIED,APP需要明確的向用戶請(qǐng)求授權(quán)拄踪。

b. 請(qǐng)求權(quán)限

如何使用系統(tǒng)的API向用戶請(qǐng)求dangerous類的權(quán)限蝇恶,也很簡單,support-library都基本上幫我們做好了惶桐。首先這個(gè)權(quán)限已經(jīng)在Manifest文件中聲明(廢話么)艘包,然后向用戶請(qǐng)求該權(quán)限,同意了你就用耀盗,否則想都別想(用戶是上帝)想虎。Google是這么解釋why the app needs permissions的:

在某些情況下,你可能想讓用戶理解為啥你的APP需要這個(gè)權(quán)限叛拷。比如用戶打開了一個(gè)拍攝APP(好吧舌厨,又是Camera),用戶不會(huì)對(duì)這個(gè)APP需要使用Camera感到奇怪忿薇,但是用戶可能不能理解為什么這破APP還需要使用我的位置和聯(lián)系人數(shù)據(jù)(不能忍了)裙椭!所以在你請(qǐng)求某個(gè)權(quán)限之前躏哩,你應(yīng)該考慮提供一段解釋性的話給用戶。請(qǐng)記住你不是要靠這點(diǎn)解釋性的話就讓用戶徹底的明白你為什么需要這個(gè)權(quán)限揉燃,如果你解釋過多扫尺,用戶覺得沒卵用直接卸了APP。只有當(dāng)用戶拒絕了你權(quán)限請(qǐng)求之后才是使用那段解釋性的話最好的時(shí)候炊汤,因?yàn)槿绻脩粢恢眹L試使用某個(gè)需要權(quán)限的功能正驻,但是卻又一直不授予該權(quán)限,這就表明用戶真不知道為什么這個(gè)功能需要這個(gè)這個(gè)權(quán)限抢腐,在這種情況下姑曙,你的解釋性的話就派上用場(chǎng)了。

Android提供[shouldShowRequestPermissionRationale()](http://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String))方法求向用戶展示為啥你需要這個(gè)權(quán)限迈倍,當(dāng)用戶之前已經(jīng)請(qǐng)求過該權(quán)限并且拒絕了授權(quán)這個(gè)方法返回true伤靠。
注意:如果用戶拒絕權(quán)限請(qǐng)求的時(shí)候選擇了Don’t ask again選項(xiàng),上面的方法返回false啼染,當(dāng)然如果設(shè)備本身就不允許有這個(gè)權(quán)限也是返回false宴合。
看看代碼:

// Here, thisActivity is the current activity
if(ContextCompat.checkSelfPermission(thisActivity,Manifest.permission.READ_CONTACTS)!=PackageManager.PERMISSION_GRANTED){
  // Should we show an explanation?
   if(ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,Manifest.permission.READ_CONTACTS)){
      // Show an expanation to the user *asynchronously* -- don't block
      // this thread waiting for the user's response! After the user
      // sees the explanation, try again to request the permission.
  }else{
      // No explanation needed, we can request the permission.
       ActivityCompat.requestPermissions(thisActivity,newString[{Manifest.permission.READ_CONTACTS},MY_PERMISSIONS_REQUEST_READ_CONTACTS);
      // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
      // app-defined int constant. The callback method gets the
      // result of the request.
  }
}

其中[requestPermissions()](http://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int))就是請(qǐng)求權(quán)限方法,異步方法迹鹅⌒畏模看看這個(gè)方法需要三個(gè)參數(shù):

@param activity The target activity
@param permissions The requested permissions(這里就是你需要申請(qǐng)的權(quán)限,在Manifest類中可以找到你需要的權(quán)限)
@param requestCode Application specific request code to match with a result  reported to onRequestPermissionsResult(int, String[], int[])}(標(biāo)志這次授權(quán)的唯一請(qǐng)求碼徒欣,當(dāng)用戶進(jìn)行授權(quán)操作后在回調(diào)方法中可以根據(jù)這個(gè)標(biāo)識(shí)符進(jìn)行區(qū)分不同的授權(quán)操作)

有個(gè)缺點(diǎn)就是使用系統(tǒng)請(qǐng)求授權(quán)API你不能自定義樣式逐样,請(qǐng)求授權(quán)彈出來的是標(biāo)準(zhǔn)的Android Dialog(如下圖,遵循Android標(biāo)準(zhǔn)蠻好的)打肝,如果你希望提供一些信息之類的應(yīng)該在[requestPermissions()](http://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int))之前進(jìn)行操作脂新。

request_permission_dialog.png

c. 處理授權(quán)請(qǐng)求回調(diào)

使用onRequestPermissionsResult(int ,String , int[])方法處理回調(diào),上面說到了根據(jù)[requestPermissions()](http://developer.android.com/reference/android/support/v4/app/ActivityCompat.html#requestPermissions(android.app.Activity, java.lang.String[], int))方法中的requestCode粗梭,我們就可以在回調(diào)方法中區(qū)分授權(quán)請(qǐng)求争便,看代碼:

@Override
publicvoidonRequestPermissionsResult(intrequestCode,Stringpermissions[],int[]grantResults){
    switch(requestCode){
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS:{
          // If request is cancelled, the result arrays are empty.
          if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
              // permission was granted, yay! Do the
              // contacts-related task you need to do.
           }else{
              // permission denied, boo! Disable the
              // functionality that depends on this permission.
           }
           return;
        }
        // other 'case' lines to check for other
        // permissions this app might request
      }
}

最后注意以下兩點(diǎn):

  • 當(dāng)在Manifest文件中聲明了同一個(gè)permission group中的權(quán)限后,請(qǐng)求權(quán)限時(shí)不會(huì)列出具體的權(quán)限断医,只是將這個(gè)permission group權(quán)限進(jìn)行說明滞乙。對(duì)于permission group中的所有權(quán)限,用戶只需要授權(quán)一次鉴嗤,以后里面的所有其他權(quán)限都不需要在進(jìn)行授權(quán)(撤銷后等操作除外)斩启,系統(tǒng)會(huì)自動(dòng)認(rèn)為是授權(quán)并以PERMISSION_GRANTED為參數(shù)調(diào)用onRequestPermissionsResult方法,和彈出系統(tǒng)請(qǐng)求授權(quán)對(duì)話框點(diǎn)擊授權(quán)是一樣的效果醉锅。雖然這樣兔簇,對(duì)于每個(gè)授權(quán)還是需要進(jìn)行單獨(dú)請(qǐng)求授權(quán)操作。
  • Requesting Permissions at Run Time是當(dāng)target API >= 23并且系統(tǒng)為Android 6.0 (API level 23)及以上才有的,如不屬于這種情況APP還是像以前一樣在安裝的時(shí)候回提示所有需要的權(quán)限并授予這些權(quán)限垄琐。
    看看簡單的demo效果;


    compare.png

代碼:
https://gitlab.com/lujun/RuntimePermissionDemo

參考:
[http://developer.android.com/training/permissions/requesting.html#perm-request][1]
[][2]http://stackoverflow.com/questions/23527767/open-failed-eacces-permission-denied
[1]:http://developer.android.com/training/permissions/requesting.html#perm-request
[2]:http://stackoverflow.com/questions/23527767/open-failed-eacces-permission-denied

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末边酒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子狸窘,更是在濱河造成了極大的恐慌墩朦,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翻擒,死亡現(xiàn)場(chǎng)離奇詭異氓涣,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)韭寸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來荆隘,“玉大人恩伺,你說我怎么就攤上這事∫埽” “怎么了晶渠?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長燃观。 經(jīng)常有香客問我褒脯,道長,這世上最難降的妖魔是什么缆毁? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任番川,我火速辦了婚禮,結(jié)果婚禮上脊框,老公的妹妹穿的比我還像新娘颁督。我一直安慰自己,他們只是感情好浇雹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布沉御。 她就那樣靜靜地躺著,像睡著了一般昭灵。 火紅的嫁衣襯著肌膚如雪吠裆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天烂完,我揣著相機(jī)與錄音试疙,去河邊找鬼。 笑死抠蚣,一個(gè)胖子當(dāng)著我的面吹牛效斑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼缓屠,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奇昙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起敌完,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤储耐,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后滨溉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體什湘,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年晦攒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闽撤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡脯颜,死狀恐怖哟旗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情栋操,我是刑警寧澤闸餐,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站矾芙,受9級(jí)特大地震影響舍沙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剔宪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一拂铡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧葱绒,春花似錦和媳、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骚秦,卻和暖如春她倘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背作箍。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工硬梁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人胞得。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓荧止,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子跃巡,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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