Android 6.0以上 需要運行時申請的權限

http://www.cnblogs.com/tangs/articles/6377347.html

自從Android6.0發布以來,在權限上做出了很大的變動,不再是之前的只要在manifest設置就可以任意獲取權限,而是更加的注重用戶的隱私和體驗,不會再強迫用戶因拒絕不該擁有的權限而導致的無法安裝的事情,也不會再不征求用戶授權的情況下,就可以任意的訪問用戶隱私,而且即使在授權之后也可以及時的更改權限。這就是6.0版本做出的更擁護和注重用戶的一大體現。

一、認知

今天我們就來學習下Android6.0的權限管理。

Android6.0系統把權限分為兩個級別:

一個是Normal Permissions,即普通權限,這類權限不會潛藏有侵害用戶隱私和安全的問題,比如,訪問網絡的權限,訪問WIFI的權限等;

另一類是Dangerous Permissions,即危險權限,這類權限會直接的威脅到用戶的安全和隱私問題,比如說訪問短信,相冊等權限。

但是到底哪些是普通權限和危險權限呢,這里給出分類,大家在使用時以便參考。

1、Normal Permissions (普通權限)

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

INSTALL_SHORTCUT

INTERNET

KILL_BACKGROUND_PROCESSES

MODIFY_AUDIO_SETTINGS

NFC

READ_SYNC_SETTINGS

READ_SYNC_STATS

RECEIVE_BOOT_COMPLETED

REORDER_TASKS

REQUEST_IGNORE_BATTERY_OPTIMIZATIONS

REQUEST_INSTALL_PACKAGES

SET_ALARM

SET_TIME_ZONE

SET_WALLPAPER

SET_WALLPAPER_HINTS

TRANSMIT_IR

UNINSTALL_SHORTCUT

USE_FINGERPRINT

VIBRATE

WAKE_LOCK

WRITE_SYNC_SETTINGS

使用以上權限是不會威脅到用戶安全的,所以這類權限是可以直接的在manifest里面直接的使用,而且在安裝后也會直接的生效了。

2、Dangerous Permissions (危險權限)

SMS(短信)

SEND_SMS

RECEIVE_SMS

READ_SMS

RECEIVE_WAP_PUSH

RECEIVE_MMS

STORAGE(存儲卡)

READ_EXTERNAL_STORAGE

WRITE_EXTERNAL_STORAGE

CONTACTS(聯系人)

READ_CONTACTS

WRITE_CONTACTS

GET_ACCOUNTS

PHONE(手機)

READ_PHONE_STATE

CALL_PHONE

READ_CALL_LOG

WRITE_CALL_LOG

ADD_VOICEMAIL

USE_SIP

PROCESS_OUTGOING_CALLS

CALENDAR(日歷)

READ_CALENDAR

WRITE_CALENDAR

CAMERA(相機)

CAMERA

LOCATION(位置)

ACCESS_FINE_LOCATION

ACCESS_COARSE_LOCATION

SENSORS(傳感器)

BODY_SENSORS

MICROPHONE(麥克風)

RECORD_AUDIO

危險權限和普通權限也有區別,普通權限是單條的權限,而危險權限是以組展示的,也就是說,當你接受一個危險權限時,不但但接受的是界面上展示的這一個權限,而是它所在這個組里面的其他所有訪問權限也將會被自動獲取權限,比如,一旦WRITE_CONTACTS被授權了,App也有READ_CONTACTS和GET_ACCOUNTS的權限了。

值得注意的是,這類權限也是需要在manifest中注冊的。

ok,光說不練不是咱的風格,咱寫東西都是基于自己遇到的問題,然后認真的學習后才記錄下來的。一方面鞏固自己的知識,另一方面也希望能幫助他人提供一點解決方案。

二、實戰

實戰部分分為幾種情況,因為根據我們的目標SDK版本和Android真機版本的不同會有不同的情景,針對普通權限大家都熟悉,就不介紹了,下面一一介紹危險權限的使用情景:

在介紹使用情景之前,先看下我的開發和真機的Android版本。

我們這里以讀取短信息為例講解整個權限的使用:

1、沒有訪問權限的情況下:

首先我們先來設計下布局,如下:

看下代碼,很簡單,就直接讀取短息:

然后,點擊界面上的“讀取收件箱中的短信”,相信大家都會知道發生什么情況,果然不出意外的程序直接崩潰了,打下日志:

日志中很清晰的告訴我們,這個異常是因為沒有權限而造成的,那么我們就直接給它加上讀取短信的權限來看看吧。

2、在manifest中添加了權限:

在manifest中加了對讀取短信的權限,你應該很高興的等待著總共有多少條短信出現在我們的界面上,但是,事實很讓人崩潰:

再次出現了沒有權限的異常,這是為什么呢?

這里我們先不解決這個問題,先來想象一種實際的情況,假如你現有的APP里面有很多使用到了危險權限,有時候你并完全清楚到底在哪里使用了,但是你的目標版本又是像我的版本一樣指向了6.0,而有可能用戶的手機是6.0以上的版本,那么這時候你的APP就有可能會出現這種,那么在你還沒查清楚有哪些地方使用了危險權限是,該怎么解決呢?

那么你可以這么解決:

修改你build.gradle 中的 targetSdkVersion 目標版本號:

然后手機版本還是6.0以上,來看看結果:

可以了,哈哈,你很高興,確實是可以了。

那么聰明的你或許意識到什么了,是的,以版本23,也就是android6.0位分割線,我們可以得出一個小結論:

當targetSdkVersion >= 23,且真機版本 >= 23時,即使在manifest中添加了相應的危險權限,在沒有做相應的處理時(至于怎么處理后面會講),還時會出現限權的異常,這時manifest中的危險權限并沒有起作用,但是還必須聲明。

當targetSdkVersion < 23,且真機版本 >= 23時,我們并沒有做任何的相關處理,就得到了想要的訪問權限,這說明在manifest中申請的危險權限起作用了。

我們在來看另外一種情況,就是,假如我的手機比較舊,還沒更新6.0的系統,這種情況下又該是什么情況呢?

這次我們用個4.4.4版本的模擬機

目標targetSdkVersion 為21 來看看結果:

也是可以的,0條信息是因為我的模擬機上沒短信,這個數字多少和我們沒有關系。假如targetSdkVersion 為23呢,來看看結果:

很清晰的看出,我們又得到了正確的結果。

由此我們也得到了一個小結論:

當我們的真機系統版本 < 23時,不管我們的targetSdkVersion 值是否大于23,都不會影響我們在manifest里面申請的權限,也就是說這時候真機的系統版本在起著主導作用。

由上面的幾條結論,我們應該很清晰的知道了訪問權限在真機中的使用狀況,但是我們的手機在升級,版本也會越來越高,因此我們現在的應用不可能一直只支持低版本的使用也不考慮兼顧高版本。所以現在APP權限升級是必然的趨勢。

那么現在回來解決上面遺留的問題,當真機和目標版本都大于6.0時出現的權限異常我們該怎么解決呢?

主要分為三個步驟:

1:檢查是否擁有權限

2:假如沒有權限,則申請權限

3:處理權限回調

下面我們分別來看看這幾個步驟。

1:檢查是否擁有權限

檢查是否已擁有了權限,可以使用ContextCompat.checkSelfPermission(Context context, String permission);

checkSelfPermission方法中有兩個參數,分別是上下文,以及所申請的權限。

如果有權限,請讓它直接去讀取短信信息。如果沒有權限則去申請。

2:申請權限

申請權限則是使用:

public static void requestPermissions(final Activity activity,final String[] permissions, final int requestCode) {}

requestPermissions方法中需要三個參數,當前的activity,所申請的權限,可以是多個,最后就是請求碼,既然有請求碼說明它會有一個回調,也就是我們下面要講的處理回調。

3:處理權限回調

處理權限回調,需要在Activity中重寫onRequestPermissionsResult方法:

然后在方法內判斷用戶是授權了該權限組還是拒絕授權,如果授權則就去獲取短信信息,否則,在這里我只是顯示了一個toast提示框。

這里再次說明下,權限組內只要有一個被授權,其他的權限也就有了權限,這也是為什么直接使用grantResults[0] == PackageManager.PERMISSION_GRANTED的原因。

ok,下面來具體的界面顯示:

我們可以看到,當我們第一次點擊讀取短信時,它會先檢查該應用是否有權限,如果沒有,就去申請,這里在界面上對應的就是顯示一個授權的對話框,第一次我們選擇了拒絕授權,然后在回調里面就會對應先打印了我們的一個toast消失提醒我們拒絕了授權,但是當我們再次需要讀取短信時,它還會去申請授權,這時我們允許授權,然后我們就看到了,在顯示短信條數的TextView顯示了短信的條數。(這里0條是因為的用的模擬器沒有短信,這不是重點。)

值得提醒的事,當我們第一次選擇拒絕授權時,當再次點擊讀取短信時,這時在授權對話框中會多一個“不再提醒”的提示,當我們在拒絕了授權,并選擇不再提醒時,那么會出現什么情況呢?請看演示:

當多次拒絕并選擇不提提醒,那么下次再去讀取就不會在去申請授權,而是直接在回調中說明用戶已拒絕授權。

那么這時候假如用戶出于某種需要必須得給應用授權該怎么做呢,其實很簡單,在回調中,提醒用戶去“設置”里面手動給應用授權,或是發個廣播打開設置界面等等都可,這里和我顯示的提醒“權限已被拒絕”基本一樣,只需在稍微優化即可,這里不在演示。

其實到這里已經差不多講完,但是,有一個方法我們可以留一下,那就是shouldShowRequestPermissionRationale,這個方法默認返回false,但當用戶在上一次已經拒絕過這個權限申請時,再次需要申請該權限時,就會返回ture,它的寓意是你已經拒絕了一次,結果又彈出個授權框,你需要給我一個解釋,為什么要授權,也就是說對多次授權這個權限做出解釋,以便用戶知道為什么必須授權了才能夠完成他操作。

下面,來看看它的使用:

我這里就簡單的彈出個對話框,說明下為什么要用這個權限,然后再次去調用這個申請的權限的方法了,大家可以同回調的方法一起封裝下,可以更好的應用。

看下界面操作:

講到這里基本差不地講完了,這里只是講了單個申請權限,多個一起也是可以的,大家可以自己試試,基本是一樣的操作,另外在說明一點,可能我們一個應用里,需要多出的使用到危險權限,這樣就造成我們需要多次重寫一樣的代碼,很不便利,所以網上也就出現了很多關于權限框架的開源代碼,大家可以自行的使用。

關于打電話的一個完整代碼:

callPhoneIntent =newIntent(Intent.ACTION_DIAL);

callPhoneIntent.setData(uri);//如果有權限(實際這個方法只是檢測你的APP是否使用了某個權限,但是不能檢測是否被限制了)if(ContextCompat.checkSelfPermission(SkipWebActivity.this, Manifest.permission.CALL_PHONE) ==PackageManager.PERMISSION_GRANTED) {

startActivity(callPhoneIntent);

}else{//當多次拒絕并選擇不提提醒,那么下次再去讀取就不會在去申請授權,而是直接在回調中說明用戶已拒絕授權if(ActivityCompat.shouldShowRequestPermissionRationale(SkipWebActivity.this,Manifest.permission.CALL_PHONE)){

showRequestPermission();

}else{//只被拒絕過一次該權限的申請ActivityCompat.requestPermissions(SkipWebActivity.this,newString[]{Manifest.permission.CALL_PHONE},REQUEST_CALLPHONE_PERMISSION);

}

}/*** 申請打電話的權限得的對話框,之前被勾選不再提醒申請權限*/privatevoidshowRequestPermission(){newAlertDialog.Builder(SkipWebActivity.this)

.setTitle("申請權限")

.setMessage("請求權限撥打電話")

.setPositiveButton("同意",newDialogInterface.OnClickListener() {

@OverridepublicvoidonClick(DialogInterface dialogInterface,inti) {//申請權限ActivityCompat.requestPermissions(SkipWebActivity.this,newString[]{Manifest.permission.CALL_PHONE},REQUEST_CALLPHONE_PERMISSION);

}

})

.setNegativeButton("拒絕",newDialogInterface.OnClickListener() {

@OverridepublicvoidonClick(DialogInterface dialogInterface,inti) {//拒絕授權}

})

.show();

}

@OverridepublicvoidonRequestPermissionsResult(intrequestCode, @NonNull String[] permissions, @NonNullint[] grantResults) {if(requestCode ==REQUEST_CALLPHONE_PERMISSION){if(grantResults[0] ==PackageManager.PERMISSION_GRANTED){

startActivity(callPhoneIntent);

}else{

T.showS("拒絕了打電話權限");

}

}super.onRequestPermissionsResult(requestCode, permissions, grantResults);

}

工具類的使用:https://github.com/googlesamples/easypermissions

例子,添加的依賴如下:

//6.0運行時權限工具類compile 'pub.devrel:easypermissions:0.4.2'

publicclassTestActivityextendsAppCompatActivityimplementsEasyPermissions.PermissionCallbacks{/*** 請求相機與存取文件權限的請求碼*/privatestaticfinalintRP_CAMERA_AND_STORAGE = 1;

@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

methodRequiresTwoPermission();

}/*** 請求兩個權限*/privatevoidmethodRequiresTwoPermission() {

String[] perms={Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};if(EasyPermissions.hasPermissions(this, perms)) {

Toast.makeText(this,"已經有這些權限了,該干嘛干嘛吧",Toast.LENGTH_SHORT).show();

}else{//缺少某些權限,請求權限EasyPermissions.requestPermissions(this, "請求相機與存取文件的權限",RP_CAMERA_AND_STORAGE, perms);

}

}

@OverridepublicvoidonRequestPermissionsResult(intrequestCode, @NonNull String[] permissions, @NonNullint[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);//結果轉發給EasyPermissions來處理EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults,this);

}/*** 權限同意的回調

*@paramrequestCode

*@paramperms*/@OverridepublicvoidonPermissionsGranted(intrequestCode, Listperms) {

Log.e("tag", "授權了:" + requestCode + ":" +perms.size());if(requestCode ==RP_CAMERA_AND_STORAGE){

Toast.makeText(this,"用戶已經同意些權限了,該干嘛干嘛吧",Toast.LENGTH_SHORT).show();

}

}/*** 權限被拒絕的回調

*@paramrequestCode

*@paramperms 代表拒絕的權限*/@OverridepublicvoidonPermissionsDenied(intrequestCode, Listperms) {

Log.e("tag", "拒絕了:" + requestCode + ":" +perms.size());if(requestCode ==RP_CAMERA_AND_STORAGE){

Toast.makeText(this,"用戶拒絕了某些權限了",Toast.LENGTH_SHORT).show();//檢查是否有永久的權限列表中至少有一個權限是永久的被拒絕(用戶點擊“永不再問”)。if(EasyPermissions.somePermissionPermanentlyDenied(this, perms)) {newAppSettingsDialog.Builder(this, "為了您能正常使用,請開啟權限!")

.setTitle("提示")

.setPositiveButton("去設置")

.setNegativeButton("取消",null)

.setRequestCode(RP_CAMERA_AND_STORAGE)

.build()

.show();

}

}

}

@OverridepublicvoidonActivityResult(intrequestCode,intresultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if(requestCode ==RP_CAMERA_AND_STORAGE) {//在用戶從應用程序設置界面返回后做一些事情Toast.makeText(this, "用戶設置了什么", Toast.LENGTH_SHORT) .show();

}

}

}

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容