Android 6.0 執行時許可權詳解
一、概述
隨著Android 7.0的釋出,Android 6.0的普及速度很快就升上去了,目前Android 6.0的市場佔有率是15.2%(具體資料可以檢視Android資訊中心,自從Android Developer Day大會的召開,有很多網站,我們開發者可以直接訪問了,不必再爬梯子,對國內開發者來說,是很大的福音。唉,扯遠了.....言歸正傳)這時,我們就不得不對新版本SDK中的變化做一些適配,這樣才能保證應用更好的執行。對於6.0中的變化,我們可以參考官網的這篇文章:Android 6.0變更。該篇文章主要對Android 6.0 執行時許可權(Runtime Permissions)做一下介紹。
這裡推薦官網的兩篇文章,畢竟官方的文件才是最科學的:
二、執行時許可權
從Android6.0(API級別23)開始,使用者開始在應用執行時向其授權,而不是在應用安裝時授權。此方法可以簡化應用安裝過程,同時使用者可以對應用的功能進行更多的控制。對於6.0以下的,當我們安裝應用時預設就授權所有的許可權了,使用者也不瞭解這些許可權到底有什麼用,只能默默忍受。。而新的許可權機制可以很好的解決這一系列問題。Google將新的許可權分為正常許可權和危險許可權:
正常許可權:正常許可權涵蓋應用需要訪問起沙盒外部資料或資源,但對於使用者隱私或其它應用操作風險很小的區域。例如,設定時區的許可權就是正常許可權。如果應用宣告氣需要正常的許可權,系統會自動向應用授予該許可權。這裡可以參考官網的正常許可權的列表。
-
危險許可權:危險許可權涵蓋應用需要涉及使用者隱私資訊的資料活資源,或者可能對使用者儲存的資料活其它應用的操作產生影響的區域。例如,讀取使用者的聯絡人就屬於危險許可權。如果應用宣告其需要危險許可權,則使用者必須明確嚮應用授予該許可權。其實我們在開發中,只要處理好危險許可權,正常許可權的處理方式和之前一樣。下面貼出危險許可權圖:
我們看上面的危險許可權,會發現危險許可權是分組的,那麼分組會對我們的許可權有影響嗎?的確是有影響的。如果你的APP執行在Android 6.0以上的機器上(targetSdkVersion >= 23下面會細說),授權機制是這樣的。如果你申請某個危險許可權,假設你的App早已被使用者授予了同一組中的某個危險許可權,那麼系統會立即授權,則不會彈出對話方塊讓使用者去授權。例如,你的App已經對CONTACTS許可權組中的READ_CONTACTS授權了,當你的App申請WRITE_CONTACTS許可權時,系統則會直接授權通過。此外,對於申請時彈出Dialog的文字說明也是對整個許可權組的說明,而不是單個許可權。這裡需要注意的是:彈出的Dialog是系統提供,我們是不能進行定製的。
三、許可權適配
首先我們按照之前的方式來申請撥打電話的許可權(撥打電話許可權),在Android 6.0(targetSdkVersion >= 23)手機上進行測試。
//首先在清單檔案中申請撥打電話的許可權
<uses-permission android:name="android.permission.CALL_PHONE"/>
//在Button的點選事件中,使用Intent撥打電話
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + phoneNumber));
startActivity(intent); //此行程式碼會報紅線。(android studio 2.2.2版本)看來AS還是挺人性化的。
執行App,點選撥打電話按鈕,你會發現App崩潰了。。。下面貼出異常原因圖:
從圖中可以很清楚的看到是因為SecurityException許可權異常。解決這個異常有兩種方法:
- 在android studio中,開啟build.gradle(module:app)檔案,將targetSdkVersion的版本號修改為低於23的,即可解決該異常。那就繼續使用舊有規則:使用者在安裝的時候不得不接受所有許可權,安裝後app就有了那些許可權咯!
- 使用Android提供的相關API進行許可權的檢查,避免這個異常。
但是作為一個有“情懷”的程式猿,我們怎麼可能用第一種這麼low的方法去解決問題呢。下面我們使用Android提供的相關API來處理異常。
-
首先在清單檔案中申請撥打電話的許可權,這一步是必不可少的。
<uses-permission android:name="android.permission.CALL_PHONE"/>
在Button的點選事件,撥打電話前,首先使用ActivityCompat.checkSelfPermission()方法檢查是否有撥打電話許可權(ActivityCompat和ContextCompat是子父類的關係),該方法有兩個int型別的返回值:分別為PERMISSION_GRANTED(表示應用有此許可權)和PERMISSION_DENIED(表示應用沒有許可權),如果此時返回值為PERMISSION_DENIED,那麼我們就應該手動去請求應用的許可權,看程式碼。 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) { /** * 請求撥打電話許可權 * 該方法是非同步的,第一個引數是Context; * 第二個引數是需要申請的許可權的字串陣列; * 第三個引數為requestCode,主要用於回撥的時候檢測。 * 可以從方法名requestPermissions以及第二個引數看出,是支援一次性申請多個許可權的,系統會通過對話方塊逐一詢問使用者是否授權。 */ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1); } else { //有許可權,直接呼叫撥打電話的方法 mLoginPresenter.call(this); }
-
在Activity中重寫onRequestPermissionsResult方法,處理請求許可權的回撥。首先驗證requestCode定位到你的申請,然後驗grantResults對應於申請的結果,這裡的陣列對應於申請時的第二個許可權字串陣列。如果你同時申請兩個許可權,那麼grantResults的length就為2,分別記錄你兩個許可權的申請結果。如果申請成功,就可以做你的事情了。
@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, "未授權撥打電話許可權", Toast.LENGTH_LONG).show(); } break; } }
申請許可權的基本步驟就如上所示,沒圖沒真相。接下來我們就來看下真相吧。上圖。。。
如果使用者拒絕某授權。下一次彈框,使用者會有一個“不再提醒”的選項的來防止app以後繼續請求授權。如果這個選項在拒絕授權前被使用者勾選了,那麼下次你再點選撥打電話時,Dialog將不會在提示,App什麼也不幹,這對使用者來說是很差的體驗。後文會說處理的方法。
注意:不同手機上,可能提示的方式不同,下面看下下米手機上的提示。(小米4手機上即使你拒絕很多次,它的那個Dialog上也不會出現“不在詢問”的勾選框),可能是國內的手機廠商對Rom做了處理。
四、更優雅的處理許可權提示問題
如果使用者拒絕某授權。下一次彈框,使用者會有一個“不再提醒”的選項的來防止app以後繼續請求授權。如果這個選項在拒絕授權前被使用者勾選了。下次為這個許可權請求requestPermissions時,對話方塊就不彈出來了,結果就是,app啥都不幹。這將是很差的使用者體驗,使用者做了操作卻得不到響應。這種情況需要好好處理一下。在請求requestPermissions前,我們需要檢查是否需要展示請求許可權的提示通過activity的shouldShowRequestPermissionRationale方法,如果該方法返回true,則表示使用者已經拒絕過一次許可權,此時我們應該彈一個訊息提示框,表明請求該許可權的原因,讓使用者授權該許可權。程式碼如下:
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) {
showSecurityMessage("是否授權撥打電話許可權,若未授權,則不能撥打電話。", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(LoginActivity.this,
new String[]{Manifest.permission.CALL_PHONE},
1);
}
});
return;
}
/**
* 請求撥打電話許可權
* 該方法是非同步的,第一個引數是Context;
* 第二個引數是需要申請的許可權的字串陣列;
* 第三個引數為requestCode,主要用於回撥的時候檢測。
* 可以從方法名requestPermissions以及第二個引數看出,是支援一次性申請多個許可權的,系統會通過對話方塊逐一詢問使用者是否授權。
*/
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, 1);
} else {
//有許可權,直接撥打
mLoginPresenter.call(this);
}
private void showSecurityMessage(String message, DialogInterface.OnClickListener okListener) {
new AlertDialog.Builder(this)
.setMessage(message)
.setPositiveButton("是", okListener)
.setNegativeButton("否", null)
.create()
.show();
}
注:當你一次請求多個許可權時,不要忘了為沒個許可權新增解釋說明。
效果圖:
五、使用相容庫相容舊版本
以上程式碼在android 6.0以上執行沒有問題,但是API 23之前的就不行了,因為沒有那些方法。粗暴的方法就是檢查版本:
if (Build.VERSION.SDK_INT >= 23) {
// Marshmallow+
} else {
// Pre-Marshmallow
}
但是太複雜,這裡我們可以使用v4相容庫,已對這個做過相容,用以下函式代替:
- ContextCompat.checkSelfPermission() 被授權函式返回PERMISSION_GRANTED,否則返回PERMISSION_DENIED ,在所有版本都是如此。
- ActivityCompat.requestPermissions() 這個方法在M之前版本呼叫,OnRequestPermissionsResultCallback 直接被呼叫,帶著正確的 PERMISSION_GRANTED或者 PERMISSION_DENIED結束 。
- ActivityCompat.shouldShowRequestPermissionRationale() 如果此函式在M之前呼叫,它將永遠返回false。
用v4包的這三方法,完美相容所有版本!這個方法需要額外的引數,Context or Activity。其它的就沒什麼特別的了。上面的後兩個方法,我們也可以在Fragment中使用,用v13相容包:FragmentCompat.requestPermissions() and FragmentCompat.shouldShowRequestPermissionRationale()和activity效果一樣。
六、使用三方開源庫
以上程式碼在實際開發中寫著還是很麻煩的,只有申請的許可權是危險許可權,那麼就要去檢查。當然,你也可以自己去封裝下,方便自己使用。下面是github上star數最多的關於Permissions庫,大家在開發中可以直接使用。
首發地址: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
相關文章
- Android 6.0 在執行時請求許可權Android
- Android 6.0 執行時許可權管理最佳實踐Android
- Android6.0------許可權申請管理(單個許可權和多個許可權申請)Android
- Android6.0許可權的動態適配Android
- 原生Android之(6.0及以上)許可權申請Android
- android 6.0許可權機制的簡單封裝(支援批量申請許可權)Android封裝
- Android6.0動態許可權最簡單的解決方法Android
- android 6.0許可權申請機制(簡單案例)Android
- 「Android6.0許可權適配| 掘金技術徵文 」Android
- Android 採用AOP方式封裝6.0許可權管理Android封裝
- MySQL 許可權詳解MySql
- django開發之許可權管理(一)——許可權管理詳解(許可權管理原理以及方案)、不使用許可權框架的原始授權方式詳解Django框架
- 許可權框架之Shiro詳解框架
- Android 通知許可權Android
- Android SELinux許可權AndroidLinux
- Docker容器執行時許可權和Linux系統功能DockerLinux
- Android呼叫相簿、相機(相容6.0、7.0、8.0)所需新增的許可權Android
- 【詳解】GrantedAuthority(已授予的許可權)
- android動態許可權到自定義許可權框架Android框架
- android 許可權庫EasyPermissionsAndroid
- Linux檔案讀、寫、執行許可權Linux
- 普通使用者許可權執行dockerDocker
- Linux常用檔案許可權命令詳解Linux
- 基於註解的6.0許可權動態請求框架——JPermission框架
- android 許可權元件設計Android元件
- php執行shell指令碼需要sudo許可權PHP指令碼
- Android SELinux avc dennied許可權問題解決方法AndroidLinux
- Android property屬性許可權新增Android
- Android 中的危險許可權Android
- Android動態許可權總結Android
- Android許可權處理分類Android
- android強制申請許可權Android
- Laravel 日誌有時候有許可權有時候沒有許可權?Laravel
- XtraBackup工具詳解 Part 4 XtraBackup許可權及配置
- 許可權之選單許可權
- vscode在Linux環境下執行“許可權不夠”的解決方法VSCodeLinux
- linux 檔案許可權 s 許可權和 t 許可權解析Linux
- android AVC錯誤修改許可權方法Android
- Android手機獲取Root許可權Android