Android 權限管理
大部分介紹都是開發文檔上面的,下面是鏈接
https://developer.android.com/training/permissions/requesting?hl=zh-cn
我也是一個Android新手,今天寫程序的時候感覺關於Activity中的權限管理和事情是一件很麻煩且重復的事情,然后自己就抽離出一個權限管理的工具,
我采用的是繼承的方式解決的,雖然這樣會有一些不妥的地方,但是感覺整體上自己使用還是滿足的,當然管理權限管理這方面網上有很多的框架,但是我
還有學過,有時間在研究吧,我就直接短時間的抽離出來,方便自己在現階段的程序代碼編寫。沒有解決版本兼容性問題。
我們還是看開發文檔上的說明吧,
檢查權限
如果應用需要一項危險權限,那么每次執行需要該權限的操作時,您都必須檢查自己是否具有該權限。
從 Android 6.0(API 級別 23)開始,用戶可隨時從任何應用撤消權限,即使應用以較低的 API 級別為目標平台也是如此。因此,即使應用昨天使用了相機,也不能認為它今天仍具有該權限。
要檢查您是否具有某項權限,請調用 ContextCompat.checkSelfPermission() 方法。例如,以下代碼段展示了如何檢查 Activity 是否具有向日歷寫入數據的權限:
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR)
!= PackageManager.PERMISSION_GRANTED) {
// 當前權限 android.permission.WRITE_CALENDAR沒有許可。
}
如果應用具有此權限,該方法將返回 PERMISSION_GRANTED,並且應用可以繼續操作。如果應用不具備此權限,該方法將返回 PERMISSION_DENIED,且應用必須明確要求用戶授予權限。
請求權限
當您的應用從 checkSelfPermission() 收到 PERMISSION_DENIED 時,您需要提示用戶授予該權限。Android 為您提供了幾種可用來請求權限的方法(如 requestPermissions()),如下面的代碼段所示。調用這些方法時,會顯示一個無法自定義的標准 Android 對話框。
如何向用戶顯示此內容取決於設備的 Android 版本以及應用的目標版本,如權限概覽中所述。
解釋為什么應用需要權限
在某些情況下,您需要幫助用戶理解為什么您的應用需要某項權限。例如,如果用戶啟動一款攝影應用,用戶或許不會對該應用請求使用相機的權限感到驚訝,但用戶可能不理解為什么該應用想要訪問用戶的位置或聯系人。在您的應用請求權限之前,不妨考慮向用戶提供解釋。請注意,您肯定不希望您的解釋讓用戶不勝其煩;如果您提供太多解釋,用戶可能會覺得這款應用很麻煩,因而將其移除。
您可以使用的一種方法是只在用戶之前拒絕過該權限請求的情況下才提供解釋。Android 提供了一種實用程序方法,即 shouldShowRequestPermissionRationale()。如果用戶之前拒絕了該請求,該方法將返回 true;如果用戶之前拒絕了某項權限並且選中了權限請求對話框中的不再詢問選項,或者如果設備政策禁止該權限,該方法將返回 false。
如果用戶不斷嘗試使用需要某項權限的功能,但一直拒絕權限請求,這或許意味着,用戶不理解為什么應用需要該權限才能提供這項功能。在這種情況下,顯示解釋或許是不錯的做法。
應用權限最佳做法中提供了更多建議,指導您如何在請求權限時創造良好的用戶體驗。
在必要時請求成為默認處理程序
有些應用依賴於訪問與通話記錄和短信有關的敏感用戶信息。如果您想請求特定於通話記錄和短信的權限,並將應用發布到 Play 商店,則必須在請求這些運行時權限之前提示用戶將應用設置為“默認處理程序”以獲得核心系統功能。
如需詳細了解默認處理程序(包括有關如何向用戶顯示默認處理程序提示的指南),請參閱有關僅在默認處理程序中使用的權限的指南。
請求您需要的權限
如果您的應用還不具備它需要的權限,那么它必須調用一個 requestPermissions() 方法來請求相應權限。您的應用將傳遞它想要的權限,以及您指定用於識別此權限請求的整數請求代碼。此方法異步運行。它會立即返回結果,並且在用戶響應提示后,系統會利用相應結果來調用應用的回調方法,在調用過程中傳遞的請求代碼與應用傳遞給 requestPermissions() 的請求代碼相同。
以下代碼將檢查應用是否具有讀取用戶聯系人的權限。如果沒有該權限,它會檢查是否應顯示需要該權限的解釋,如果不需要解釋,它會請求該權限:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an explanation 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; 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.
}
} else {
// Permission has already been granted
}
系統顯示的提示說明了您的應用需要訪問的權限組,而不是具體權限。
注意:當您的應用調用 requestPermissions() 時,系統會向用戶顯示一個標准對話框。您的應用無法配置或更改該對話框。如果您需要向用戶提供任何信息或解釋,您應該在調用 requestPermissions() 之前這樣做,如解釋為什么應用需要權限中所述。
處理權限請求響應
當用戶響應您應用的權限請求時,系統會調用您應用的 onRequestPermissionsResult() 方法,在調用過程中向其傳遞用戶響應。您的應用必須替換該方法,以查明是否已向其授予相應權限。在回調過程中傳遞的請求代碼與傳遞給 requestPermissions() 的請求代碼相同。例如,如果應用請求 READ_CONTACTS 訪問權限,則它可能采用以下回調方法:
@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;
}
// other 'case' lines to check for other
// permissions this app might request.
}
}
系統顯示的對話框說明了您的應用需要訪問的權限組,它不會列出具體權限。例如,如果請求 READ_CONTACTS 權限,系統對話框只會說明您的應用需要訪問設備的聯系人。用戶只需要針對每個權限組授予一次權限。如果您的應用請求該組中的其他任何權限(已在您的應用清單中列出),系統會自動授予這些權限。當您請求權限時,系統會調用您的 onRequestPermissionsResult() 回調方法並傳遞 PERMISSION_GRANTED,就像用戶通過系統對話框明確同意了您的請求時的處理方式一樣。
注意:您的應用仍需要明確請求它需要的每項權限,即使用戶已授予同一組中的其他權限。此外,權限分組在將來的 Android 版本中可能會發生變化。您的代碼不應依賴於特定權限屬於或不屬於同一組的假設。
權限的分類
大家可以參考這個文章
https://www.jianshu.com/p/ccea3c2f9cfa
下面是代碼部分
package ***;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.util.ArrayList;
import java.util.List;
/**
* @author shaoyayu
* 權限管理工具,
* 負責權限的申請和管理
*/
public abstract class AuthorityManagementActivity extends AppCompatActivity {
private static final String TAG = "AuthorityManagementActivity";
/**
* 所有權限
*/
protected List<String> authority = new ArrayList<>();
/**
* 已經授權的權限
*/
protected List<String> grantedPermissions = new ArrayList<>();
/**
* 沒有授權的權限
*/
protected List<String> noGrantedPermissions = new ArrayList<>();
/**
* 申請的權限被用戶用戶不提醒的拒絕,需要重新申請
*/
protected List<String> deniedPermission = new ArrayList<>();
protected static final int CODE_AUTHORITY = 0x00010;
/**
* 初始化布局
* @param savedInstanceState
*/
@Override
protected final void onCreate(@Nullable Bundle savedInstanceState) {
//提供初始化的布局
super.onCreate(savedInstanceState);
initActivity(savedInstanceState);
}
/**
* 子類在這個地方初始化activity
* @param savedInstanceState
*/
protected abstract void initActivity(Bundle savedInstanceState);
/**
* 初始權限,發現沒有申請的權限直接申請
* @param authority
*/
protected void setAuthority(String authority[]){
for (int i = 0; i < authority.length; i++) {
this.authority.add(authority[i]);
}
/**
* 初始換權限
*/
initAuthority();
};
/**
* 分類權限
*/
private void initAuthority(){
for (int i = 0; i < authority.size(); i++) {
if (ActivityCompat.checkSelfPermission(this, this.authority.get(i)) == PackageManager.PERMISSION_GRANTED) {
//提出授權
this.grantedPermissions.add(authority.get(i));
}else {
//用戶尚未授權
this.noGrantedPermissions.add(authority.get(i));
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
authority.get(i))) {
/**
* 權限已經在上一次申請的時候被永久的駁回
* 解釋一下權限需求的重要性,打開應用詳情去設置權限給予
* 我采用的是對話框的形式顯示和解釋給用戶
*/
deniedPermission.add(authority.get(i));
} else {
//沒有被駁回的權限,在這里我會采用統一的方式去申請
}
}
}
requestForAccess(noGrantedPermissions);
}
/**
* 初始化權限,但不申請
* @param authority
*/
@SuppressLint("LongLogTag")
protected void initAuthority(String[] authority){
for (int i = 0; i < authority.length; i++) {
this.authority.add(authority[i]);
Log.i(TAG,"初始化的權限有:"+authority[i]);
}
for (int i = 0; i < this.authority.size(); i++) {
if (ActivityCompat.checkSelfPermission(this, this.authority.get(i)) == PackageManager.PERMISSION_GRANTED) {
//提出授權
this.grantedPermissions.add(this.authority.get(i));
Log.i(TAG,"已經授權的權限:"+this.authority.get(i));
}else {
//用戶尚未授權
this.noGrantedPermissions.add(this.authority.get(i));
Log.i(TAG,"尚未授權的權限有:"+this.authority.get(i));
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
this.authority.get(i))) {
/**
* 權限已經在上一次申請的時候被永久的駁回
* 解釋一下權限需求的重要性,打開應用詳情去設置權限給予
* 我采用的是對話框的形式顯示和解釋給用戶
*/
deniedPermission.add(this.authority.get(i));
Log.i(TAG,"永久駁回的權限有:"+this.authority.get(i));
} else {
//沒有被駁回的權限,在這里我會采用統一的方式去申請
}
}
}
}
/**
* 取出已經授權得權限
* @return
*/
protected List<String> getGrantedPermissions(){
return this.grantedPermissions;
}
protected List<String> getNoGrantedPermissions(){
return this.noGrantedPermissions;
}
public List<String> getDeniedPermission() {
return deniedPermission;
}
/**
* 申請權限
*/
protected void requestForAccess(List<String> atu){
String[] aut = new String[atu.size()];
for (int i = 0; i < atu.size(); i++) {
aut[i] = atu.get(i);
}
if (aut.length!=0){
//沒有權限,向用戶請求權限
ActivityCompat.requestPermissions(this, aut,
AuthorityManagementActivity.CODE_AUTHORITY);
}
}
/**
* 權限回調
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode,permissions,grantResults);
//從新定義申請的權限和未申請的權限
this.grantedPermissions.clear();
this.noGrantedPermissions.clear();
this.deniedPermission.clear();
if (requestCode==AuthorityManagementActivity.CODE_AUTHORITY){
for (int i = 0; i < permissions.length; i++) {
//從新定義授權的情況
if (grantResults[i]==PackageManager.PERMISSION_GRANTED){
//同意授權
grantedPermissions.add(permissions[i]);
}else {
noGrantedPermissions.add(permissions[i]);
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
permissions[i])) {
/**
* 權限已經在上一次申請的時候被永久的駁回
* 解釋一下權限需求的重要性,打開應用詳情去設置權限給予
* 我采用的是對話框的形式顯示和解釋給用戶
*/
deniedPermission.add(permissions[i]);
} else {
//沒有被駁回的權限,在這里我會采用統一的方式去申請
}
}
}
}
authorizationProcessing();
}
/**
* 子類需要在這個地方寫,授權后的回調
*/
abstract void authorizationProcessing();
/**
* 打開應用詳情設置授權,
* 然用戶在這里授權
* 記住當前的額報名是需要可以取值為當前應用的包名
* 否則會出錯
*/
protected void openAppSettings(){
Intent localIntent = new Intent();
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (Build.VERSION.SDK_INT >= 9) {
localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
localIntent.setData(Uri.fromParts("package", getPackageName(), null));
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
}
startActivity(localIntent);
}
}
大家有什么好的建議或者意見的話,留言我會去學習的,謝謝。。。。