編輯:關於Android編程
Android 6.0的權限定義:
從 Android 6.0(API 級別 23)開始,用戶開始在應用運行時向其授予權限,而不是在應用安裝時授予。此方法可以簡化應用安裝過程,因為用戶在安裝或更新應用時不需要授予權限。它還讓用戶可以對應用的功能進行更多控制;例如,用戶可以選擇為相機應用提供相機訪問權限,而不提供設備位置的訪問權限。用戶可以隨時進入應用的“Settings”屏幕調用權限。
系統權限分為兩類:正常權限和危險權限(運行時權限,一下統稱危險權限):
所有危險的 Android 系統權限都屬於權限組。如果設備運行的是 Android 6.0(API 級別 23),並且應用的targetSdkVersion是 23 或更高版本,則當用戶請求危險權限時系統會發生以下行為:
來源:https://developer.android.com/guide/topics/security/permissions.html
現在我們寫一個小例子驗證一下:
一、首先目標版本設置為23
強調,在低於23版本也可以使用V7包來調用檢測權限和申請權限的方法,還能在回調接收到,但是,也會出現回調返回的狀態不准的問題,在下就被這個坑坑了好久。targetSdkVersion如果是23以下,調用ActivityCompat.requestPermissions(),會彈出權限選擇對話框,但是選擇拒絕授權,onRequestPermissionsResult中的返回值卻是PERMISSION_GRANTED,但選擇同意授權,會把應用關閉重新開啟當前activity,而不會調用onRequestPermissionsResult中的方法,所以不要在targetSdkVersion設置為23以下,又把complierSdkversion設置為23,這樣會出現上述的問題。最好的方式是把targetSdkVersion也設置為23,就可以解決。一切完美運行。
詳情請看:
按照如下設置:
android { compileSdkVersion 23 buildToolsVersion "23.0.1" defaultConfig { applicationId "com.qinglin.permissiondemo" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" } }
二、聲明需要的權限
首先,必須在AndroidManifest.xml文件裡面聲明需要的權限,否則在使用到相應的權限的時候強行申請權限會崩潰的,這也許是android系統的保護機制吧。
如果您的應用需要危險權限,則每次執行需要這一權限的操作時您都必須檢查自己是否具有該權限。用戶始終可以自由調用此權限,因此,即使應用昨天使用了相機,它不能假設自己今天仍具有該權限。
要檢查您是否具有某項權限,請調用ContextCompat.checkSelfPermission()方法。例如,以下代碼段顯示了如何檢查 Activity 是否具有在日歷中進行寫入的權限:
// Assume thisActivity is the current activity int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);
請求權限
1、解釋應用為什麼需要權限
在某些情況下,您可能需要幫助用戶了解您的應用為什麼需要某項權限。例如,如果用戶啟動一個攝影應用,用戶對應用要求使用相機的權限可能不會感到吃驚,但用戶可能無法理解為什麼此應用想要訪問用戶的位置或聯系人。在請求權限之前,不妨為用戶提供一個解釋。請記住,您不需要通過解釋來說服用戶;如果您提供太多解釋,用戶可能發現應用令人失望並將其移除。
您可以采用的一個方法是僅在用戶已拒絕某項權限請求時提供解釋。如果用戶繼續嘗試使用需要某項權限的功能,但繼續拒絕權限請求,則可能表明用戶不理解應用為什麼需要此權限才能提供相關功能。對於這種情況,比較好的做法是顯示解釋。
為了幫助查找用戶可能需要解釋的情形,Android 提供了一個實用程序方法,即shouldShowRequestPermissionRationale()。如果應用之前請求過此權限但用戶拒絕了請求,此方法將返回true。
注:如果用戶在過去拒絕了權限請求,並在權限請求系統對話框中選擇了Don't ask again選項,此方法將返回false。如果設備規范禁止應用具有該權限,此方法也會返回false。
如果應用尚無所需的權限,則應用必須調用一個requestPermissions()方法,以請求適當的權限。應用將傳遞其所需的權限,以及您指定用於識別此權限請求的整型請求代碼。此方法異步運行:它會立即返回,並且在用戶響應對話框之後,系統會使用結果調用應用的回調方法,將應用傳遞的相同請求代碼傳遞到requestPermissions()。
以下代碼可以檢查應用是否具備讀取用戶聯系人的權限,並根據需要請求該權限:
// 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. } }
3、處理權限請求響應(onRequestPermissionsResult回調)
當應用請求權限時,系統將向用戶顯示一個對話框。當用戶響應時,系統將調用應用的 onRequestPermissionsResult() 方法,向其傳遞用戶響應。您的應用必須替換該方法,以了解是否已獲得相應權限。回調會將您傳遞的相同請求代碼傳遞給 requestPermissions()。例如,如果應用請求 READ_CONTACTS 訪問權限,則它可能采用以下回調方法:
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case PluginUtil.BAIDU_READ_LOCATION_STATE: { // 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. Toast.makeText(this, "用戶允許此權限", Toast.LENGTH_LONG).show(); } else { if(!ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[0])){ //用戶拒絕了此申請權限,並勾選了不再詢問 ToastUtil.showToast(this,"點擊權限,並打開全部權限"); PluginUtil.gotoAppDetailSettingIntent(this); return; } // permission denied, boo! Disable the // functionality that depends on this permission. Toast.makeText(this, "用戶拒絕此權限", Toast.LENGTH_LONG).show(); } return; } // other 'case' lines to check for other // permissions this app might request }
}
系統顯示的對話框說明了您的應用需要訪問的權限組;它不會列出具體權限。例如,如果您請求 READ_CONTACTS 權限,系統對話框只顯示您的應用需要訪問設備的聯系人。用戶只需要為每個權限組授予一次權限。如果您的應用請求該組中的任何其他權限(已在您的應用清單中列出),系統將自動授予應用這些權限。當您請求此權限時,系統會調用您的 onRequestPermissionsResult() 回調方法,並傳遞 PERMISSION_GRANTED,如果用戶已通過系統對話框明確同意您的權限請求,系統將采用相同方式操作。
權限功能規范化(統一創建一個工具類PermissionUtils)
由於權限的功能會在多個地方反復使用,為了提高復用性,必然需要建立一個工具類,把常用方法寫在這個工具類裡面,方便調用。
以下是我的工具類封裝PermissionUtils,共享給大家:
public class PermissionUtils { private static final String SCHEME = "package"; public static final int BAIDU_READ_LOCATION_STATE = 100; //自定義一個權限獲取碼,用於回調函數中做對應處理 public static void opendPermissionSetting(Context context) { String deviceInfo = getDeviceInfo(); LogUtil.i("hql", "deviceInfo:" + deviceInfo); if ("Xiaomi".equals(Build.MANUFACTURER)) { gotoMiuiPermission(context); } else if ("Meizu".equals(Build.MANUFACTURER)) { gotoMeizuPermission(context); } else if ("HUAWEI".equals(Build.MANUFACTURER)) { gotoHuaweiPermission(context); } else { if (Build.VERSION.SDK_INT >= 23) {// 用於判斷是否為Android 6.0系統以上版本 gotoAppDetailSettingIntent(context); } else { gotoAppSettingIntent(context); } } } public static void needPermission(Activity context, int requestCode, String[] permissions) { try { // 申請一個(或多個)權限,並提供用於回調返回的獲取碼(用戶定義) ActivityCompat.requestPermissions(context, permissions, requestCode); } catch (Exception e) { e.printStackTrace(); LogUtil.e("hql", e.toString()); } } // 判斷權限集合是否都擁有,只要有一個缺少就返回false public static boolean isHasPermission(Context context, String... permissions) { for (String permission : permissions) { if (!isHasPermission(context, permission)) { return false; } } return true; } // 判斷是否擁有權限 private static boolean isHasPermission(Context context, String permission) { Context mContext = context.getApplicationContext(); return ContextCompat.checkSelfPermission(mContext, permission) == PackageManager.PERMISSION_GRANTED; } /** * 跳轉到miui的權限管理頁面 */ public static void gotoMiuiPermission(Context context) { Intent i = new Intent("miui.intent.action.APP_PERM_EDITOR"); ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); i.setComponent(componentName); i.putExtra("extra_pkgname", context.getPackageName()); try { context.startActivity(i); } catch (Exception e) { e.printStackTrace(); gotoAppSettingIntent(context); } } /** * 跳轉到魅族的權限管理系統 */ public static void gotoMeizuPermission(Context context) { Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.putExtra("packageName", BuildConfig.APPLICATION_ID); try { context.startActivity(intent); } catch (Exception e) { e.printStackTrace(); gotoAppSettingIntent(context); } } /** * 華為的權限管理頁面 */ public static void gotoHuaweiPermission(Context context) { try { Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.permissionmanager.ui.MainActivity");//華為權限管理 intent.setComponent(comp); context.startActivity(intent); } catch (Exception e) { e.printStackTrace(); gotoAppSettingIntent(context); } } /** * 打開應用詳情頁面intent */ public static void gotoAppDetailSettingIntent(Context context) { Intent localIntent = new Intent(); localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 9) { localIntent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); localIntent.setData(Uri.fromParts(SCHEME, context.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", context.getPackageName()); } context.startActivity(localIntent); } /** * 打開系統設置界面 */ public static void gotoAppSettingIntent(Context context) { Intent intent = new Intent(Settings.ACTION_SETTINGS);//系統設置界面 context.startActivity(intent); } }
當第一次請求權限申請被拒絕後再進行第二次申請時,對話框中會多出一個 不再詢問 的復選框。如果勾選了該復選框並且拒絕請求,那麼以後將無法再申請該權限。也就是說在調用 requestPermissions() 後,onRequestPermissionsResult() 會立刻被調用並且申請結果為 PERMISSION_DENIED 。 其實這個時候還是有一根救命稻草的。
1、首先需要判斷用戶是否勾選了不再詢問,使用方法:ActivityCompat.shouldShowRequestPermissionRationale(this,permissions[0])。
2、ActivityCompat 位於 support.v7 包中,因為運行時權限是 6.0 的新特性,使用該類可以省略對版本的判斷當權限申請被拒絕並且shouldShowRequestPermissionRationale() 返回 false 就表示勾選了不再詢問。轉到設置界面現在我們唯一能做的就是跳轉到我們 App 的設置界面,讓用戶手動開啟權限了。
應用如果一味要求用戶提供授權,可能會讓用戶無所適從。如果用戶發現應用難以使用,或者擔心應用會濫用其信息,他們可能不願意使用該應用,甚至會將其完全卸載。以下最佳做法有助於避免此類糟糕的用戶體驗。
許多情況下,您可以使用以下兩種方式之一來讓您的應用執行某項任務。您可以將應用設置為要求提供權限才能執行操作。或者,您可以將應用設置為使用 intent,讓其他應用來執行任務。
例如,假設應用需要使用設備相機才能夠拍攝照片。應用可以請求 CAMERA 權限,以便允許其直接訪問相機。然後,應用將使用 Camera API 控制相機並拍攝照片。利用此方法,您的應用能夠完全控制攝影過程,並支持您將相機 UI 整合至應用中。
不過,如果您無需此類完全控制,則可以使用 ACTION_IMAGE_CAPTURE intent 來請求圖像。發送該 intent 時,系統會提示用戶選擇相機應用(如果沒有默認相機應用)。用戶使用選定的相機應用拍攝照片,該相機應用會將照片返回給應用的 onActivityResult() 方法。
同樣,如果您需要撥打電話、訪問用戶的聯系人或要執行其他操作,可以通過創建適當的 intent 來完成,或者您也可以請求相應的權限並直接訪問相應的對象。每種方法各有優缺點。
如果使用權限:
如果使用 intent:
每次您要求權限時,實際上是在強迫用戶作出決定。您應盡量減少提出這些請求的次數。如果用戶運行的是 Android 6.0(API 級別 23)或更高版本,則每次用戶嘗試要求提供權限的新應用功能時,應用都必須中斷用戶的操作並發起權限請求。如果用戶運行的是較早版本的 Android,則在安裝應用時需要為應用的每一權限請求給予授權;如果列表過長或看起來不合適,用戶可能會決定不安裝該應用。為此,您應盡量減少應用需要的權限數。
例如,很多情況下應用可以通過使用 intent 來避免請求權限。如果某項功能並非應用的核心功能,不妨考慮將相關工作交給其他應用來執行,如考慮使用 intent 中所述。
如果用戶運行的是 Android 6.0(API 級別 23)或更高版本,則用戶必須在應用運行時為其授權。如果您的應用一次要求用戶提供多項權限,用戶可能會感到無所適從並因此退出應用。您應根據需要請求權限。
某些情況下,一項或多項權限可能是應用所必需的。在這種情況下,合理的做法是,在應用啟動之後立即要求提供這些權限。例如,如果您運行攝影應用,應用需要訪問設備的相機。在用戶首次啟動應用時,他們不會對提供相機使用權限的要求感到驚訝。但是,如果同一應用還具備與用戶聯系人共享照片的功能,您不應在應用首次啟動時要求用戶提供 READ_CONTACTS 權限,而應等到用戶嘗試使用“共享”功能之後,再要求提供該權限。
如果應用提供了教程,則合理的做法是,在教程結束時請求提供應用的必要權限。
系統在您調用 requestPermissions() 時顯示的權限對話框將說明應用需要的權限,但不會解釋為何需要這些權限。某些情況下,用戶可能會感到困惑。因此,最好在調用 requestPermissions() 之前向用戶解釋應用需要相應權限的原因。
例如,攝影應用可能需要使用位置服務,以便能夠為照片添加地理標簽。通常,用戶可能不了解照片能夠包含位置信息,並且對攝影應用想要了解具體位置感到不解。因此在這種情況下,應用最好在調用 requestPermissions() 之前告知用戶此功能的相關信息。
告知用戶的一種辦法是將這些請求納入應用教程。這樣,教程可以依次顯示應用的每項功能,並在顯示每項功能時解釋需要哪些相應的權限。例如,攝影應用的教程可以演示其“與您的聯系人共享照片”功能,然後告知用戶需要為應用授予權限才能查看用戶的聯系人。然後,應用可以調用 requestPermissions(),要求用戶提供該訪問權限。當然,並非所有用戶都會按照教程操作,因此您仍需在應用的正常操作期間檢查和請求權限
從 Android 6.0(API 級別 23)開始,用戶是在運行時而不是在應用安裝時授予或撤銷應用權限。因此,您應在多種不同條件下測試應用。在低於 Android 6.0 的版本中,您可以認為如果應用得到運行,它就可以得到在應用清單中聲明的全部權限。在新的權限模式中,這一推斷不再成立。
以下提示可幫助您識別在運行 API 級別 23 或更高級別的設備上與權限有關的代碼問題:
$ adb shell pm list permissions -d -g
$ adb shell pm [grant|revoke]...
項目開啟的Activity越多,占的內存越多,我們是不是有時候想當我打開很多界面的時候,我們到底打開多少個Activity,OK現在你的難題解決了,只有把這個Activi
NavigationBar可以手動隱藏,隨著華為榮耀手機有了這個特點後,目前有很多android手機都有該特性。如下截圖所示: 上圖的底部虛擬導航按鈕的左、右邊
引言在我們的應用程序中經常需要提供搜索服務,比如搜索聯系人, 搜索商品信息等等。我們可以自己在布局中自定義我們的搜索框,實現我們的搜索邏輯。但是還有一種更簡單的方法:使用
之前重要負責收集聯系人添加歸屬地,公司這兩個智能分組;網上的實現方法很多,有說通過webserver來查詢,但是網絡這種形式不是長遠之道;畢竟服務器不是你自己的,還有