和之前在安裝 APP 的是就申請了權限不一樣,Google 在 API 23,也就 6.0 以後加入了動態權限。對於一些敏感的權限,決定權交還給了用戶,再也不是強制申請了。由於這個緣由,若是 APP 須要支持 6.0 以上的系統,就須要進行一下適配,不然 APP 就會崩潰。html
好比,下面撥打電話.。已經在在 AndroidManifest.xml
中申明權限了java
<uses-permission android:name="android.permission.CALL_PHONE"/>
這是撥打電話的代碼實現android
private void callPhone() { Intent intent = new Intent(Intent.ACTION_CALL); Uri data = Uri.parse("tel:" + "12312341234"); intent.setData(data); startActivity(intent); }
可是程序若是是在 Android 6.0 的設備中運行的話,一運行就崩潰了,報錯信息以下git
java.lang.SecurityException: Permission Denial: ... 後面的信息省略
那麼要怎麼才能適配呢?github
首先要先知道動態權限有哪些?ide
Andriod 中的動態權限是按組來分的,下面的表格來自官網ui
每一個組中的權限不用所有申請,申請了其中一個,組中其餘權限也就一塊兒申請到了。this
有個前提,咱們的 Activity 好比 MainActivity 再也不是繼承於 Acticity
了,而是繼承於 AppCompatActivity
,由於動態權限的一些方法是隻有 AppCompatActivity
纔有的。google
首先是檢測是否已經賦予了權限,能夠調用 ContextCompat.checkSelfPermission(Context, permissionName)
方法,經過方法的返回值來判斷spa
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) { // 已經賦予權限,直接調用撥打電話的代碼 callPhone(); } else { // 沒有賦予權限,那就去申請權限 }
要申請權限可使用 requestPermissions (Activity activity, String[] permissions, int requestCode)
進行申請
private static final int REQUEST_CALL_PHONE = 456; ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE);
因而在界面上就會彈出一個請求撥打電話權限的對話框(這個對話框是系統彈出來的,樣式不可修改)
無論用戶選擇贊成仍是拒絕,都會在 onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)
方法中接收到信息
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 權限請求成功 callPhone(); } else { // 用戶拒絕了 showTipDialog(); } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void showTipDialog() { new AlertDialog.Builder(this) .setMessage("該程序須要電話權限,不然沒法正常運行") .setPositiveButton(android.R.string.ok, null) .create() .show(); }
若是用戶選擇」贊成「,就能夠調用撥打電話的按鈕,同時這個對話框以後都不會出現了。
若是用戶拒絕了,那就沒法調用撥打電話的代碼了。爲了用戶體驗,能夠彈出一個對話框,告知用戶我須要這個權限,沒有這個權限程序沒法正常運行
用戶看到了這個信息,因而再次點擊「撥打電話」的按鈕,程序又進行了權限檢查,彈出申請權限的對話框,這時候的對話框的和以前第一次進行權限申請時彈出的對話框的樣式不一樣,多出了一個「再也不提醒」的勾選項
若是用戶勾選「再也不詢問」,對話框是這樣的
意味着用戶永遠地拒絕撥打電話的權限,同時這個對話框不會再彈出來了,這就形成程序沒法正常運行。還有辦法能夠補救一下,就是跳轉到設置頁面,讓用戶手動開啓權限。Anroid 提供了一個方法 ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)
能夠用來判斷是否選擇了「再也不提示」
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 權限請求成功 callPhone(); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) { // 用戶選擇了「拒絕」 showTipDialog(); } else { // 用戶勾選了「再也不提示」 goToSetting(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void goToSetting() { new AlertDialog.Builder(this) .setMessage("該程序須要電話權限,不然沒法正常運行") .setPositiveButton("去打開權限", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } }) .create() .show(); }
public class MainActivity extends AppCompatActivity { private static final int REQUEST_CALL_PHONE = 456; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.btn_capture).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { callPhoneWithCheck(); } }); } private void callPhone() { Intent intent = new Intent(Intent.ACTION_CALL); Uri data = Uri.parse("tel:" + "12312341234"); intent.setData(data); startActivity(intent); } private void callPhoneWithCheck() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) { // 已經賦予權限 callPhone(); } else { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case REQUEST_CALL_PHONE: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 權限請求成功 callPhone(); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CALL_PHONE)) { // 用戶選擇了「拒絕」 showTipDialog(); } else { // 用戶勾選了「再也不提示」 goToSetting(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private void goToSetting() { new AlertDialog.Builder(this) .setMessage("該程序須要電話權限,不然沒法正常運行") .setPositiveButton("去打開權限", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivity(intent); } }) .create() .show(); } private void showTipDialog() { new AlertDialog.Builder(this) .setMessage("該程序須要電話權限,不然沒法正常運行") .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CALL_PHONE}, REQUEST_CALL_PHONE); } }) .create() .show(); } }
爲了申請一個撥打電話的權限,要寫好多代碼。網上有個一個流行的開源庫 PermissionsDispatcher,採用註解來快速實現以上的邏輯。用法很簡單,就是須要注意到是,寫完代碼是須要編譯一下 project,這樣才能生成相應的代碼。
http://blog.csdn.net/zhangqinghuazhangzhe/article/details/52801202
http://www.jianshu.com/p/d6b3e16cc1d9
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1110/3670.html
http://www.jianshu.com/p/b4a8b3d4f587
http://blog.csdn.net/quan356270259/article/details/50876272
http://blog.csdn.net/u010483016/article/details/50401605
http://www.jianshu.com/p/e1ab1a179fbb#
http://blog.csdn.net/u011200604/article/details/52874599
http://blog.csdn.net/yangqingqo/article/details/48371123