今天仍是圍繞着最近面試的一個熱門話題Android 6.0權限適配來總結學習,其實Android 6.0權限適配咱們公司是在今年5月份纔開始作,算是比較晚的吧,不過如今Android 6.0以上設備愈來愈多了,因此Android 6.0 權限適配是必不可少的工做,這裏主要介紹一下咱們公司是如何作Android 6.0權限適配的。html
權限管理相關博客:面試
根據上面博客咱們很清楚的知道,Android的權限其實就是爲了程序之間更加的安全的訪問,因此權限有等級之分,好比:Normal 低風險權限 、Dangerous 高風險權限等,雖然有這種安全意識,可是這些權限只會在安裝的時候被詢問一次,一旦安裝以後,若是app申請了高風險權限的話,並且大部分用戶在安裝的時候不多去關注這些權限列表,再加上不少Android市場都有靜默安裝的功能用戶更加感知不到任何權限提示,就這樣app就有可能會在後臺作一些對用戶帶來傷害的事情。以下圖所示:安全
鑑於6.0以前的版本權限管理相對不那麼安全,因此Android 6.0 採用新的權限模型,只有在須要權限的時候,才告知用戶是否受權,是在runtime時候受權,而不是在原來安裝的時候 ,同時默認狀況下每次在運行時打開頁面時候,須要先檢查是否有所須要的權限申請。這樣的用戶的自主性提升不少,好比用戶能夠給APP賦予攝像的權限,也能夠使用權限。app
先看下app module的build.gradle配置ide
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.whoislcj.rxpermissions"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
}
因爲Android 6.0 以上的權限變成了運行時權限,也就是說在須要使用某個權限的時候必須動態去申請使用,直接訪問直接致使app崩潰。學習
其實判斷是不是須要運行時權限的標記就是targetSDKVersion,當targetSDKVersion<23的時候,僅在安裝時賦予權限,使用時將不被提醒,當targetSDKVersion≥23的時候纔會使用新的運行時權限規則。全部在最先碰見因權限未適配的致使的崩潰的時候,咱們團隊採用的解決辦法是將targetSDKVersion人爲的降到小於23,這樣就變成了仍是默認使用權限,可是這種並非Google所推薦使用的。gradle
compileSdkVersion 24
buildToolsVersion "24.0.2"
defaultConfig {
applicationId "com.whoislcj.rxpermissions"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
檢查是否擁有使用權ui
public boolean isGranted(String permission) { return !isMarshmallow() || isGranted_(permission); }
判斷是不是Android 6.0以上this
private boolean isMarshmallow() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; }
是否申請了該使用權限spa
private boolean isGranted_(String permission) { int checkSelfPermission = ActivityCompat.checkSelfPermission(this, permission); return checkSelfPermission == PackageManager.PERMISSION_GRANTED; }
ContextCompat.checkSelfPermission
,主要用於檢測某個權限是否已經被授予,方法返回值爲PackageManager.PERMISSION_DENIED
或者PackageManager.PERMISSION_GRANTED
。當返回DENIED就須要進行申請受權了。
private void requestPermission(String permission, int requestCode) { if (!isGranted(permission)) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { } else { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } } else { //直接執行相應操做了 } }
shouldShowRequestPermissionRationale主要用於給用戶一個申請權限的解釋,該方法只有在用戶在上一次已經拒絕過你的這個權限申請。也就是說,用戶已經拒絕一次了,你又彈個受權框,你須要給用戶一個解釋,爲何要受權,則使用該方法。requestCode這個須要在處理的回調的時候 一一對應的。
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == CAMERA) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { String jpgPath = getCacheDir() + "test.jpg"; takePhotoByPath(jpgPath, 2); } else { // Permission Denied Toast.makeText(MainActivity.this, "您沒有受權該權限,請在設置中打開受權", Toast.LENGTH_SHORT).show(); } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
public class MainActivity extends AppCompatActivity { private static final int CAMERA = 2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.request_permission).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { requestPermission(Manifest.permission.CAMERA, CAMERA); } }); } /** * 拍照,返回拍照文件的絕對路徑 */ private String takePhotoByPath(String filePath, int requestCode) { File file = new File(filePath); startActivityForResult(getTakePhotoIntent(file), requestCode); return file.getPath(); } private Intent getTakePhotoIntent(File file) { if (file.exists()) { file.delete(); } try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); } Uri uri = Uri.fromFile(file); Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); return intent; } public boolean isGranted(String permission) { return !isMarshmallow() || isGranted_(permission); } private boolean isGranted_(String permission) { int checkSelfPermission = ActivityCompat.checkSelfPermission(this, permission); return checkSelfPermission == PackageManager.PERMISSION_GRANTED; } private boolean isMarshmallow() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M; } //shouldShowRequestPermissionRationale主要用於給用戶一個申請權限的解釋,該方法只有在用戶在上一次已經拒絕過你的這個權限申請。也就是說,用戶已經拒絕一次了,你又彈個受權框,你須要給用戶一個解釋,爲何要受權,則使用該方法。 private void requestPermission(String permission, int requestCode) { if (!isGranted(permission)) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { } else { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } } else { //直接執行相應操做了 } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == CAMERA) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { String jpgPath = getCacheDir() + "test.jpg"; takePhotoByPath(jpgPath, 2); } else { // Permission Denied Toast.makeText(MainActivity.this, "您沒有受權該權限,請在設置中打開受權", Toast.LENGTH_SHORT).show(); } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
本篇總結學習了Android 6.0的運行時權限及如何適配的問題,可是這個並非咱們公司目前最終的解決辦法,從上面能夠看出實現起來仍是蠻麻煩的,申請權限和處理回調在不一樣的地方代碼可讀性相對較差,咱們最終的解決方案是採用RxJava+RxPermission的方式解決,下一篇將介紹一下如何使用RxPermission解決Android 6.0 權限適配問題。