###什麼時候用
這是在Android 6.0中加入的功能,就是說用戶可以在軟件使用過程中對於某一項權限進行申請。但是,並不是所有的權限都需要申請,權限可以分爲兩類,一類是普通權限,一類是危險權限。總之,在Api>23的手機軟件,在運行危險權限時候,需要進行動態的申請,也就是運行時申請。如圖示危險權限:
###動態申請時候所必須知道的
方法一:
ContextCompat.checkSelfPermission();
參數一:context 對象
參數二:String perimisson 權限名,比如:Manifest.permission.WRITE_EXTERNAL_STORAGE。
作用:檢查程序有沒有某個權限。
返回值:PERMISSION_GRANTED(其實是0),表示有某個權限,PERMISSION_DENIED(其實是-1)表示沒有某個權限。
方法二:
作用:完成請求某個權限的功能
ActivityCompat.requeestPermissions();
參數一:activity
參數二:String[] permissions 權限數組,表明可以一次申請多個權限
參數三:requestCode 請求碼,全局唯一的並且>=0
說明:使用這個方法時候,還有一個回調方法onRequestPermissionsResult
但是用上面的請求時候,這個回調方法只能在Activity中有效果,哪怕你是在Fragment中發起的請求權限,那麼在Fragment中,動態申請權限時候,一般採用
MineStudentFragment.this.requestPermissions( new String[]{permission}, permissionCode);
這樣的話,回調也會在相應的Fragment中實現了
方法三:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {}
說明:這是一個回調方法,在發起動態請求的時候,最後結果就會到這個方法
參數一:發起權限請求時候的請求碼
參數二:permissions 是你發起請求的時候填寫的權限數組
參數三:表示申請權限後,每個權限的結果,具體如圖:
假設自己發起如下的權限申請,下面,打印結果:
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA,Manifest.permission.CALL_PHONE
,Manifest.permission.USE_FINGERPRINT,Manifest.permission.USE_SIP,Manifest.permission.SEND_SMS,Manifest.permission.WRITE_EXTERNAL_STORAGE
,Manifest.permission.WRITE_CALENDAR,Manifest.permission.ADD_VOICEMAIL
},1);
可以發現,是一一對應的,而且,申請成功的,對應的grantResults爲0 ,反之,爲-1
###必須注意的幾點
1、你想動態申請某個權限時候,必須在清單文件中也加上,一方面,這樣Android6.0
以下的也具有了這個權限,另一方面,你不在清單文件中加上,在Android 6.0版本往
上,根本不會彈窗詢問用戶,會默認的返回-1,也就是申請權限沒有通過。
2、假設有三個權限,用戶在選擇的時候,第三個沒有允許,那麼在一次申請權限的話,
第三個還會再問一遍,除非用戶勾選了禁止後不在詢問的選項。而用戶允許的權限,也
不會在彈窗詢問了。用戶在設置中,手動給權限了,那麼在運行代碼的時候,也是不在
詢問。默認的就返回爲-1了
3、假設不加版本判斷,在低版本上也運行動態申請代碼的部分,運行到這裏的時候,也
是會執行,但是不會去彈窗詢問用戶,並且結果也是會反饋到
onRequestPermissionsResult中,但是,只要是在清單文件中註冊了,就會設置
成0
4、
如果你嫌棄麻煩的話,不如將都動態申請的權限都添加進去,它在動態申請的時候,都會檢測,有的不會再
彈出來申請,沒有的就會彈窗申請。
5
建議在權限申請回調裏面,添加grantResults.length ==0 ,然後 return ,多加一層判斷 ,可以防止空指針問題
##第三方庫PermissionsDispatcher
翻譯過來:權限調度,
針對於android 6.0權限建立的一個庫
https://github.com/permissions-dispatcher/PermissionsDispatcher
導入依賴
dependencies {
compile("com.github.hotchemi:permissionsdispatcher:${latest.version}") {
// if you don't use android.app.Fragment you can exclude support for them
exclude module: "support-v13"
}
annotationProcessor "com.github.hotchemi:permissionsdispatcher-processor:${latest.version}"
}
具體他版本請參考github庫
###如何使用
上述圖片來自博客:http://blog.csdn.net/u012939909/article/details/53896357
發現是通過註解的形式來完成的。
####(1)@RuntimePermissions
需要你添加在需要申請的Activity或者fragment上
####(2)@NeedsPermission
比如方法needCamrea()方法裏面需要完成調用相機拍照功能,那麼就要在此方法上添加 這個註解
####(3)@OnShowRationale
需要你自己定義一個方法,這個方法的作用是向用戶解釋爲啥需要這個權限,當然了只有第一次請求權限被用戶拒絕了,下次請求權限之前會調用
####(4)@OnPermissionDenied
當用戶拒絕了權限請求時候,需要告訴用戶爲什麼的方法上
####(5)@OnNeverAskAgain
當用戶選中了授權窗口中不在詢問的複選框之後並且拒絕了權限請求時候調用的方法,一般可以向用戶解釋爲何申請,或者在此彈出權限請求對話框。
##例子
@RuntimePermissions
public class MainActivity extends AppCompatActivity{
@NeedsPermission(Manifest.permission.CAMERA)
void showCamera() {
getSupportFragmentManager().beginTransaction()
.replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance())
.addToBackStack("camera")
.commitAllowingStateLoss();
}
//此方法需要權限,只有賦予了權限之後,這個方法再回去執行
@OnShowRationale(Manifest.permission.CAMERA)
void showRationaleForCamera(final PermissionRequest request) {
new AlertDialog.Builder(this)
.setMessage(R.string.permission_camera_rationale)
.setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
.setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
.show();
}
@OnPermissionDenied(Manifest.permission.CAMERA)
void showDeniedForCamera() {
Toast.makeText(this, R.string.permission_camera_denied, Toast.LENGTH_SHORT).show();
}
@OnNeverAskAgain(Manifest.permission.CAMERA)
void showNeverAskForCamera() {
Toast.makeText(this, R.string.permission_camera_neverask, Toast.LENGTH_SHORT).show();
}
然後,你需要編譯Build一下,然後會生成這個類的專屬 Dispatcher,然後在調用
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button_camera).setOnClickListener(v -> {
// NOTE: delegate the permission handling to generated method
MainActivityPermissionsDispatcher.showCameraWithPermissionCheck(this);
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// NOTE: delegate the permission handling to generated method
MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
}
##用戶拒絕了權限,且不再詢問
在調用 requestPermissions() 後,onRequestPermissionsResult() 會立刻被調用並且申請結果爲 PERMISSION_DENIED 。系統默認的就返回不通過
那麼只能讓應用跳轉到權限設置界面,然後讓用戶手動開啓
可以先自定義彈窗,詢問用戶是否開啓權限,允許之後:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
採用上述辦法,跳轉到權限設置的界面,然後讓用戶手動給予這個應用權限
##第三方權限庫RxPermission
https://github.com/tbruyelle/RxPermissions
專門配合RxJava思想
示例寫法
if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
} else {
ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {
} else {
ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
}
if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)) {
} else {
ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.READ_PHONE_STATE}, 2);
}
if ((ContextCompat.checkSelfPermission(GudleActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)) {
} else {
ActivityCompat.requestPermissions(GudleActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 3);
}
對於結果處理
switch (requestCode) {
case 0:
Log.d("xlj","得到的數據是:"+grantResults[0]+"對應的權限是"+permissions[0]);
break;
case 1:
Log.d("xlj","得到的數據是:"+grantResults[0]+"對應的權限是"+permissions[0]);
break;
case 2:
Log.d("xlj","得到的數據是:"+grantResults[0]+"對應的權限是"+permissions[0]);
break;
case 3:
Log.d("xlj","得到的數據是:"+grantResults[0]+"對應的權限是"+permissions[0]);
break;
}