一志膀、概述
隨著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è)異常有兩種方法:
- 在android studio中浊闪,打開(kāi)build.gradle(module:app)文件,將targetSdkVersion的版本號(hào)修改為低于23的螺戳,即可解決該異常搁宾。那就繼續(xù)使用舊有規(guī)則:用戶在安裝的時(shí)候不得不接受所有權(quán)限,安裝后app就有了那些權(quán)限咯倔幼!
- 使用Android提供的相關(guān)API進(jìn)行權(quán)限的檢查盖腿,避免這個(gè)異常。
但是作為一個(gè)有“情懷”的程序猿损同,我們?cè)趺纯赡苡玫谝环N這么low的方法去解決問(wèn)題呢翩腐。下面我們使用Android提供的相關(guān)API來(lái)處理異常。
-
首先在清單文件中申請(qǐng)撥打電話的權(quán)限膏燃,這一步是必不可少的茂卦。
<uses-permission android:name="android.permission.CALL_PHONE"/>
在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); }
-
在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