2020年4月補充
一個簡單好用的權(quán)限庫 —— PermissionGrantor
—— https://github.com/dfqin/PermissionGrantor
一啄寡、運行時權(quán)限檢查 (Permissions at Run Time)
從 Android 6.0(API 級別 23)開始风秤,用戶開始在應用運行時向其授予權(quán)限,而不是在應用安裝時授予芳绩。
系統(tǒng)權(quán)限分為兩類:普通權(quán)限 (Normal Permissions),危險權(quán)限(Dangerous permissions)
- 敏感權(quán)限在運行時每次調(diào)用都需要進行檢查是否得到授權(quán)。
- 用戶可以隨時進入應用的“Settings”屏幕決定授予任一申請的權(quán)限宰睡,就是用戶可以隨便開隨便關(guān)。
比如气筋,用戶可以選擇為相機應用提供相機訪問權(quán)限拆内,而不提供設(shè)備位置的訪問權(quán)限。
二宠默、權(quán)限分類
系統(tǒng)權(quán)限分為兩類:普通權(quán)限 (Normal Permissions)麸恍,危險權(quán)限(Dangerous permissions)
正常權(quán)限:
不會直接給用戶隱私權(quán)帶來風險。如果您的應用在其清單中列出了正常權(quán)限搀矫,系統(tǒng)將自動授予該權(quán)限抹沪。危險權(quán)限:
會授予應用訪問用戶機密數(shù)據(jù)的權(quán)限。如果您的應用在其清單中列出了正常權(quán)限瓤球,系統(tǒng)將自動授予該權(quán)限融欧。如果您列出了危險權(quán)限,則用戶必須明確批準您的應用使用這些權(quán)限冰垄。
注意:不管你的權(quán)限是 正常權(quán)限 還是 危險權(quán)限蹬癌,都需要在清單列表將其列出來权她。不過虹茶,該聲明的影響因系統(tǒng)版本和應用的目標 SDK 級別的不同而有所差異:
—— 對于普通權(quán)限,即只要在 AndroidManifest 聲明隅要,用戶安裝了應用就會獲取到權(quán)限
—— 如果設(shè)備運行的是 Android 5.1 或更低版本蝴罪,或者應用的目標 SDK 為 22 或更低:如果您在清單中列出了危險權(quán)限,則用戶必須在安裝應用時授予此權(quán)限步清;如果他們不授予此權(quán)限要门,系統(tǒng)根本不會安裝應用
虏肾。
—— 如果設(shè)備運行的是 Android 6.0 或更高版本,或者應用的目標 SDK 為 23 或更高:應用必須在清單中列出權(quán)限欢搜,并且它必須在運行時請求其需要的每項危險權(quán)限封豪。用戶可以授予或拒絕每項權(quán)限,且即使用戶拒絕權(quán)限請求炒瘟,應用仍可以繼續(xù)運行有限的功能
吹埠。
.
.
.
運行時權(quán)限的具體使用
1、為什么危險權(quán)限每次調(diào)用都要進行申請疮装?
第一肯定因為他是敏感的缘琅。
第二就是因為隨時可以在設(shè)置里面關(guān)閉這個權(quán)限,所以每次都需要申請廓推。
比如拍照刷袍,這是危險權(quán)限,你昨天早上獲取了拍照的權(quán)限樊展,但是用戶可以在昨天下午就把這個權(quán)限給關(guān)掉呻纹,你不能在今天就想當然以為你擁有了拍照權(quán)限,所以每次運行都檢查是必要的专缠,
2居暖、使用
2.1、怎么申請運行時動態(tài)申請權(quán)限藤肢?
要檢查您是否具有某項權(quán)限太闺,請調(diào)用 [ContextCompat.checkSelfPermission()](https://developer.android.com/reference/android/support/v4/content/ContextCompat.html?hl=zh-cn#checkSelfPermission(android.content.Context, java.lang.String))
方法。例如嘁圈,以下代碼段顯示了如何檢查 Activity 是否具有撥號的權(quán)限:
if (ContextCompat.checkSelfPermission(CallActivity.this,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED)
checkSelfPermission
checkSelfPermission會得到一個int類型的返回值
PackageManager.PERMISSION_GRANTED
常量表示已經(jīng)授權(quán)
PackageManager.PERMISSION_DENIED
常量表示 未申請省骂,此時可向用戶進行權(quán)限申請。
2.2最住、如何得知申請結(jié)果钞澳?
在onRequestPermissionsResult
回調(diào)方法的得知結(jié)果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUESTCODE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callNum();
} else {
}
return;
}
}
}
.
.
.
到此為止,我們來利用上面涉及代碼做一個撥號的小demo
public class CallActivity extends AppCompatActivity {
private static final int REQUESTCODE = 6001;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call);
findViewById(R.id.mTvCall).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 動態(tài)申請權(quán)限 ContextCompat.checkSelfPermission()
if (ContextCompat.checkSelfPermission(CallActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// 沒有獲取權(quán)限涨缚,那么就申請權(quán)限
ActivityCompat.requestPermissions(CallActivity.this,
new String[]{Manifest.permission.CALL_PHONE},
REQUESTCODE);
} else {
// 已經(jīng)有權(quán)限轧粟,那么就撥打電話
callNum();
}
}
});
}
private void callNum() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "10010");
intent.setData(data);
try {
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUESTCODE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callNum();
} else {
}
return;
}
}
}
}
清單文件權(quán)限
<uses-permission android:name="android.permission.CALL_PHONE"/>
代碼非常簡單,就是每次撥號都動態(tài)檢查是否具有撥號權(quán)限脓魏。
我們會發(fā)現(xiàn)情況如下
- 1兰吟、當我們授予過權(quán)限,那么下次再撥號的時候茂翔,就不會再彈出是否授權(quán)的提示框
- 2混蔼、當我們授予權(quán)限,然后又手動到 設(shè)置 把該app的撥號權(quán)限關(guān)閉珊燎,那么再次點擊撥號的時候會再次彈出 授權(quán)提示框 惭嚣。
- 3遵湖、當我們我們點擊拒絕授予權(quán)限,下次再點擊撥號的時候晚吞,會多出一個“不在提示(Don‘t ask again)”的選框延旧,如果我們勾選“不在提示”,那么下次點擊撥號就不會彈出是否授權(quán)的提示框
gif如下
2.3槽地、當被拒接授權(quán)之后的再次請求申請 shouldShowRequestPermissionRationale()
我們在運行時申請權(quán)限垄潮,比如撥打電話,用戶的選擇很簡單闷盔,要么同意弯洗,要么拒絕。
如果用戶同意了逢勾,那就萬事大吉牡整,但是如果用戶拒絕了,而且是“不再提示”的那種拒絕溺拱,那么問題就來了逃贝,你再點擊撥號就什么反應都沒有了。
對于這種情況迫摔,其實google也已經(jīng)幫我們想到了沐扳,通過shouldShowRequestPermissionRationale()
方法來解決抡草。
shouldShowRequestPermissionRationale()
袋毙。如果應用之前請求過此權(quán)限但用戶拒絕
了請求,此方法將返回 true
搂擦。根據(jù)這個特點纱烘,我們將代碼修改一下:
public class CallActivity extends AppCompatActivity {
private static final int REQUESTCODE = 6001;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_call);
findViewById(R.id.mTvCall).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 動態(tài)申請權(quán)限 ContextCompat.checkSelfPermission()
if (ContextCompat.checkSelfPermission(CallActivity.this,
Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// shouldShowRequestPermissionRational 解釋為什么需要這和權(quán)限
if (ActivityCompat.shouldShowRequestPermissionRationale(CallActivity.this,
Manifest.permission.CALL_PHONE)) {
// shouldShowRequestPermissionRationale 返回true杨拐,代表之前該權(quán)限已經(jīng)被拒絕,那么針對拒絕做處理擂啥,解釋為什么需要這個權(quán)限
new AlertDialog.Builder(CallActivity.this)
.setMessage("app需要開啟權(quán)限才能使用此功能")
.setPositiveButton("設(shè)置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
// 跳轉(zhuǎn)到app設(shè)置
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + getPackageName()));
startActivity(intent);
}
})
.setNegativeButton("取消", null)
.create()
.show();
} else {
// 彈出權(quán)限申請框框
ActivityCompat.requestPermissions(CallActivity.this,
new String[]{Manifest.permission.CALL_PHONE},
REQUESTCODE);
}
} else {
callNum();
}
}
});
}
private void callNum() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "10010");
intent.setData(data);
try {
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUESTCODE: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callNum();
} else {
}
return;
}
}
}
}
運行g(shù)if如下
我們發(fā)現(xiàn)哄陶,當我們拒絕授權(quán)此權(quán)限之后,再次執(zhí)行到申請權(quán)限的代碼的時候哺壶,根據(jù)shouldShowRequestPermissionRationale()返回為true屋吨,我們彈出提示框解釋為什么需要這個權(quán)限,并且提供了跳轉(zhuǎn)到設(shè)置開始權(quán)限的地方山宾,這樣問題就得到了解決至扰。
3、注意
1塌碌、抽取的callNum方法執(zhí)行startActivity(intent)這句代碼的時候渊胸,會報
Call requires permission which may be rejected by user: code should explicitly check to see if permission is available (with `checkPermission`) or explicitly handle a potential `SecurityException
這樣的編譯錯誤旬盯。
其實就是說這句帶嗎有可能因為未授權(quán)拋異常台妆,我們可以利用系統(tǒng)的提示的代碼處理一下翎猛,
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
startActivity(intent);
但是很顯然,沒必要接剩,因為我們前面已經(jīng)處理好了切厘。直接try...catch一下就好
2、環(huán)境說明
機器
android 7.1.1
環(huán)境
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "com.amqr.permiss6"
minSdkVersion 14
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
三懊缺、第三方庫 PermissionsDispatcher
hotchemi/PermissionsDispatcher
就此文時間當前最新版本的2.3.1
步驟1疫稿、
project 的gradle的dependencies配置
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
步驟2、
主module配置如下
apply plugin: 'android-apt'
apt 'com.github.hotchemi:permissionsdispatcher-processor:2.3.1'
compile 'com.github.hotchemi:permissionsdispatcher:2.3.1'
步驟三
安裝PermissionsDispatcher插件(安裝方法類似普通插件安裝方法)
安裝后怎么使用鹃两?
清單文件寫上必要的權(quán)限遗座,比如
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
然后右鍵generate…
選擇
最后勾選對應權(quán)限,選擇需要的方法俊扳,寫上名字途蒋,即可自動生成代碼
生成的代碼和簡單使用
@RuntimePermissions
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// storageNeedPer 原本涉及到純粹權(quán)限的代碼,我們放到storageNeedPer這個方法里面
MainActivityPermissionsDispatcher.storageNeedPerWithCheck(MainActivity.this);
}
@NeedsPermission({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void storageNeedPer() {
// 那些權(quán)限涉及到存儲權(quán)限的馋记,寫在這里
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
//權(quán)限請求回調(diào)号坡,提示用戶之后,用戶點擊“允許”或者“拒絕”之后調(diào)用此方法
}
@OnShowRationale({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void storageNeedShowRat(final PermissionRequest request) {
// 解釋為什么需要這個權(quán)限
}
@OnPermissionDenied({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void storageDenied() {
// 如果用戶不授予某權(quán)限時調(diào)用的方法,
}
@OnNeverAskAgain({Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE})
void storageAsk() {
//如果用戶選擇了讓設(shè)備“不再詢問”梯醒,而調(diào)用的方法
}
}
本文大概就到這里宽堆,github上關(guān)于權(quán)限的庫有很多,大家可以自行查找茸习,還有詳細的一些權(quán)限組啊之類的東西畜隶,權(quán)限的細節(jié)資料,大家也可以自行g(shù)oogle号胚。
本來權(quán)限就已經(jīng)被說了很久的事情代箭,也有很多優(yōu)秀的文章,此文是因為看到清單軟件一條信息誤標為完成涕刚,哈哈哈哈嗡综,只是為了完成清單。
四杜漠、注意點
- 申請權(quán)限是app會進入后臺(界面上沒體現(xiàn))
(彈出權(quán)限申請框時极景,其實這個這個時候程序會進入后臺,這個我們打印一下application的生命周期就知道了驾茴,如果程序涉及到手勢盼樟,需要根據(jù)這個特點可能需要調(diào)整下)
.
.
.
參考:
google官方文章
Android 6.0+ 運行時權(quán)限探索