- 一衰抑、前言
- 二、權(quán)限分類
- 三答恶、權(quán)限申請
- 四瀑粥、總結(jié)
一挣跋、前言
安卓平臺權(quán)限一直有被流氓應(yīng)用隨便利用詬病, android M(SDK 23)的發(fā)布徹底解決了這一問題,取而代之的是,app不得不在運(yùn)行時一個一個詢問用戶授予權(quán)限狞换。
Android 6.0避咆,代號棉花糖,其主要的特征運(yùn)行時權(quán)限就很受關(guān)注修噪。因?yàn)檫@一特征不僅改善了用戶對于應(yīng)用的使用體驗(yàn)查库,還使得應(yīng)用開發(fā)者在實(shí)踐開發(fā)中需要做出改變。Android 6.0(api23)系統(tǒng)中,做了一些限制, 開發(fā)者在使用到每條權(quán)限時必須自己調(diào)用相關(guān)代碼請求黄琼。如果沒有獲得某項(xiàng)權(quán)限,直接使用相關(guān)功能,則會導(dǎo)致自己程序crash樊销。
沒有深入了解運(yùn)行時權(quán)限的開發(fā)者通常會有很多疑問,比如什么是運(yùn)行時權(quán)限脏款,哪些是運(yùn)行時的權(quán)限围苫,我的應(yīng)用是不是會在6.0系統(tǒng)上各種崩潰呢,如何才能支持運(yùn)行時權(quán)限機(jī)制呢撤师。本文講嘗試回答這一些問題剂府,希望讀者閱讀完成之后,都能找到較為完美的答案剃盾。
二腺占、權(quán)限分類
Android 6.0以前版本:權(quán)限管理總結(jié)為一句話是:權(quán)限一刀切。
在6.0以前的系統(tǒng)痒谴,都是權(quán)限一刀切的處理方式衰伯,只要用戶安裝,Manifest申請的權(quán)限都會被賦予积蔚,并且安裝后權(quán)限也撤銷不了意鲸。Android6.0及以后版本:Android M(SDK23) 進(jìn)行了權(quán)限分類,運(yùn)行時動態(tài)申請權(quán)限。
盡管Android中有很多權(quán)限库倘,但并非所有的權(quán)限都是敏感權(quán)限临扮,于是6.0系統(tǒng)就對權(quán)限進(jìn)行了分類论矾,一般為下述三類:
正常(Normal)權(quán)限
危險(xiǎn)(Dangerous)權(quán)限
特殊(Particular)權(quán)限
-
正常(Normal)權(quán)限
一般權(quán)限都是一些系統(tǒng)認(rèn)為比較權(quán)限的權(quán)限教翩,流氓應(yīng)用就是擁有這些權(quán)限也干不出多大壞事,Normal 權(quán)限會在應(yīng)用安裝是直接授權(quán)贪壳。 正常(Normal)權(quán)限的列表:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT
UNINSTALL_SHORTCUT
上述的權(quán)限基本設(shè)計(jì)的是關(guān)于網(wǎng)絡(luò)饱亿,藍(lán)牙,時區(qū),快捷方式等方面彪笼,只要在Manifest指定了這些權(quán)限钻注,就會被授予,并且不能撤銷配猫。
-
危險(xiǎn)(Dangerous)權(quán)限
這些權(quán)限都是一些敏感性權(quán)限幅恋,一些廣告平臺或是流氓應(yīng)用會用這些權(quán)限干一些壞壞的事情,因此系統(tǒng)將這類權(quán)限分了幾個類別泵肄, 應(yīng)用每次都要檢測下是否有權(quán)限捆交,沒有的化必須彈出對話框申請,只要一個組別中的一個權(quán)限得到了授權(quán)腐巢,整個組的權(quán)限都會的到授權(quán)品追。
危險(xiǎn)權(quán)限實(shí)際上才是運(yùn)行時權(quán)限主要處理的對象,這些權(quán)限可能引起隱私問題或者影響其他程序運(yùn)行冯丙。Android中的危險(xiǎn)權(quán)限可以歸為以下幾個分組:
CALENDAR
CAMERA
CONTACTS
LOCATION
MICROPHONE
PHONE
SENSORS
SMS
STORAGE
各個權(quán)限分組與其具體的權(quán)限肉瓦,可以參考下圖:
這部分權(quán)限也是我們重點(diǎn)在M系統(tǒng)上關(guān)注和適配的部分。
6.0的運(yùn)行時權(quán)限胃惜,我們最終都是要支持的泞莉,通常我們需要使用如下的API:
int checkSelfPermission(String permission) 用來檢測應(yīng)用是否已經(jīng)具有權(quán)限
void requestPermissions(String[] permissions, int requestCode) 進(jìn)行請求單個或多個權(quán)限
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 用戶對請求作出響應(yīng)后的回調(diào)
具體如何操作,參考本文:權(quán)限申請模塊船殉。
-
特殊(Particular)權(quán)限
特殊權(quán)限戒财,顧名思義,就是一些特別敏感的權(quán)限捺弦,在Android系統(tǒng)中饮寞,主要由兩個
SYSTEM_ALERT_WINDOW 設(shè)置懸浮窗,進(jìn)行一些黑科技
WRITE_SETTINGS 修改系統(tǒng)設(shè)置
SYSTEM_ALERT_WINDOW and WRITE_SETTINGS, 這兩個權(quán)限比較特殊列吼,不能通過代碼申請方式獲取幽崩,必須得用戶打開軟件設(shè)置頁手動打開,才能授權(quán).
關(guān)于上面兩個特殊權(quán)限的授權(quán)寞钥,做法是使用startActivityForResult啟動授權(quán)界面來完成慌申。
官方文檔中這樣描述:
There are a couple of permissions that don’t behave like normal and dangerous permissions. SYSTEM_ALERT_WINDOW and WRITE_SETTINGS are particularly sensitive, so most apps should not use them. If an app needs one of these permissions, it must declare the permission in the manifest, and send an intent requesting the user’s authorization. The system responds to the intent by showing a detailed management screen to the user.
例如:請求SYSTEM_ALERT_WINDOW,設(shè)置懸浮窗
private static final int REQUEST_CODE_OVERLAY_PERMISSION = 110;
private void requestAlertWindowPermission() {
if (!Settings.canDrawOverlays(getBaseContext())) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE);
}
}
protected void onActivityResult1(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_OVERLAY_PERMISSION) {
if (Settings.canDrawOverlays(this)) {
// 已成功授權(quán)
}else{
// 未授權(quán)
}
}
}
提示
- 使用Action Settings.ACTION_MANAGE_OVERLAY_PERMISSION啟動隱式Intent
- 使用"package:" + getPackageName()攜帶App的包名信息
- 使用Settings.canDrawOverlays()方法來判斷授權(quán)結(jié)果
請求修改設(shè)置權(quán)限 WRITE_SETTINGS
private static final int REQUEST_CODE_WRITE_SETTINGS = 120;
private void requestWriteSettings() {
Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_WRITE_SETTINGS );
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_WRITE_SETTINGS) {
if (Settings.System.canWrite(this)) {
Log.i(LOGTAG, "onActivityResult write settings granted" );
}
}
}
提示
上述代碼需要注意的是
- 使用Action Settings.ACTION_MANAGE_WRITE_SETTINGS 啟動隱式Intent
- 使用"package:" + getPackageName()攜帶App的包名信息
- 使用Settings.System.canWrite方法檢測授權(quán)結(jié)果
注意:關(guān)于這兩個特殊權(quán)限理郑,除非需要蹄溉,否則一般不建議應(yīng)用申請。
三您炉、權(quán)限申請
前面已經(jīng)提到柒爵,系統(tǒng)將這類權(quán)限分了幾個類別, 應(yīng)用每次都要檢測下是否有權(quán)限赚爵,沒有的化必須彈出對話框申請棉胀,只要一個組別中的一個權(quán)限得到了授權(quán)法瑟,整個組的權(quán)限都會的到授權(quán)。
6.0的運(yùn)行時權(quán)限申請時唁奢,通常我們需要使用如下的API:
int checkSelfPermission(String permission) 用來檢測應(yīng)用是否已經(jīng)具有權(quán)限霎挟,參數(shù)就是對應(yīng)的權(quán)限名,如:相機(jī)權(quán)限是Manifest.permission.CAMERA
void requestPermissions(String[] permissions, int requestCode) 進(jìn)行請求單個或多個權(quán)限
void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 用戶對請求作出響應(yīng)后的回調(diào)
提示:
開發(fā)過程中麻掸,我們不是馬上就申請權(quán)限酥夭,而是先判斷是否已具有權(quán)限,方法是int checkSelfPermission(String permission) 脊奋。如果沒有權(quán)限采郎,才去申請void requestPermissions(String[] permissions, int requestCode) 。然后狂魔,拿到回調(diào)void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) 蒜埋,判斷用戶是否授權(quán)了。
下面以以一個請求讀取聯(lián)系人的權(quán)限為例進(jìn)行說明:
API的講解就跟著申請權(quán)限步驟一起了:
- 第一步:在AndroidManifest文件中添加需要的權(quán)限最楷。
這個步驟和我們之前的開發(fā)并沒有什么變化整份,試圖去申請一個沒有在AndroidManifest文件中聲明的權(quán)限可能會導(dǎo)致程序崩潰。
- 第二步:檢查權(quán)限
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
}else{
//
}
這里涉及到一個API籽孙,ContextCompat.checkSelfPermission烈评,主要用于檢測某個權(quán)限是否已經(jīng)被授予,方法返回值為PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED犯建。當(dāng)返回DENIED就需要進(jìn)行申請授權(quán)了讲冠。
- 第三步:申請授權(quán)
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
該方法是異步的,第一個參數(shù)是Context适瓦;第二個參數(shù)是需要申請的權(quán)限的字符串?dāng)?shù)組竿开;第三個參數(shù)為requestCode,主要用于回調(diào)的時候檢測玻熙》癫剩可以從方法名requestPermissions以及第二個參數(shù)看出,是支持一次性申請多個權(quán)限的嗦随,系統(tǒng)會通過對話框逐一詢問用戶是否授權(quán)列荔。
- 第四步:處理權(quán)限申請回調(diào)
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], 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;
}
}
}
ok,對于權(quán)限的申請結(jié)果枚尼,首先驗(yàn)證requestCode定位到你的申請贴浙,然后驗(yàn)證grantResults對應(yīng)于申請的結(jié)果,這里的數(shù)組對應(yīng)于申請時的第二個權(quán)限字符串?dāng)?shù)組署恍。如果你同時申請兩個權(quán)限崎溃,那么grantResults的length就為2,分別記錄你兩個權(quán)限的申請結(jié)果锭汛。如果申請成功笨奠,就可以做你的事情了~
當(dāng)然袭蝗,到此我們的權(quán)限申請的步驟唤殴,基本介紹就如上述般婆。不過還有個API值得提一下:
// 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.
}
這個API主要用于給用戶一個申請權(quán)限的解釋,該方法只有在用戶在上一次已經(jīng)拒絕過你的這個權(quán)限申請朵逝。也就是說蔚袍,用戶已經(jīng)拒絕一次了,你又彈個授權(quán)框配名,你需要給用戶一個解釋啤咽,為什么要授權(quán),則使用該方法渠脉。
- 小結(jié):那么將上述幾個步驟結(jié)合到一起就是:
// 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,
new String[]{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.
}
}
四宇整、總結(jié)
好在運(yùn)行時相關(guān)的API也比較簡單,所以適配起來并不會非常痛苦芋膘。對于其他的權(quán)限鳞青,其實(shí)申請的邏輯是類似的;唯一不同的肯定就是參數(shù)为朋。
Android6.0版本最大的特性:權(quán)限臂拓。對于6.0以下的權(quán)限及在安裝的時候,根據(jù)權(quán)限聲明產(chǎn)生一個權(quán)限列表习寸,用戶只有在同意之后才能完成app的安裝胶惰,造成了我們想要使用某個app,就要默默忍受其一些不必要的權(quán)限(比如不是每個app都要訪問通訊錄霞溪、短信等)孵滞。而在6.0以后,我們可以直接安裝鸯匹,當(dāng)app需要我們授予不恰當(dāng)?shù)臋?quán)限的時候剃斧,我們可以予以拒絕。當(dāng)然你也可以在設(shè)置界面對每個app的權(quán)限進(jìn)行查看忽你,以及對單個權(quán)限進(jìn)行授權(quán)或者解除授權(quán)幼东。
特別提示
在測試過程中,用的錘子·堅(jiān)果2科雳,發(fā)現(xiàn)不彈授權(quán)框根蟹。換了部樂視2手機(jī),可以彈出糟秘。
所以總結(jié):以下原因不會彈框
- 6.0以下版本(系統(tǒng)自動申請)
- 暫時發(fā)現(xiàn)錘子手機(jī)简逮、vivo、oppo尿赚、魅族的6.0以上版本
因?yàn)檫@些廠商修改了6.0系統(tǒng)申請機(jī)制散庶,他們修改成系統(tǒng)自動申請權(quán)限了蕉堰。也就是說這些系統(tǒng)會跟以前 6.0 以下的版本一樣,需要用到權(quán)限的時候系統(tǒng)會自動申請悲龟,就算我們主動申請也是沒用的屋讶。