和以前在安裝 APP 的是就申請了權限不同,Google 在 API 23,也就 6.0 之后加入了動態權限。對于一些敏感的權限,決定權交還給了用戶,不再是強制申請了。因為這個原因,如果 APP 需要支持 6.0 以上的系統,就需要進行一下適配,否則 APP 就會崩潰。
比如,下面撥打電話.。已經在在 AndroidManifest.xml
中申明權限了
<uses-permission android:name="android.permission.CALL_PHONE"/>
這是撥打電話的代碼實現
private void callPhone() {
Intent intent = new Intent(Intent.ACTION_CALL);
Uri data = Uri.parse("tel:" + "12312341234");
intent.setData(data);
startActivity(intent);
}
但是程序如果是在 Android 6.0 的設備中運行的話,一運行就崩潰了,報錯信息如下
java.lang.SecurityException: Permission Denial: ... 后面的信息省略
那么要怎么才能適配呢?
動態權限有哪些?
首先要先知道動態權限有哪些?
Andriod 中的動態權限是按組來分的,下面的表格來自官網
每個組中的權限不用全部申請,申請了其中一個,組中其他權限也就一起申請到了。
怎么申請動態權限?
有個前提,我們的 Activity 比如 MainActivity 不再是繼承于 Acticity
了,而是繼承于 AppCompatActivity
,因為動態權限的一些方法是只有 AppCompatActivity
才有的。
首先是檢測是否已經賦予了權限,可以調用 ContextCompat.checkSelfPermission(Context, permissionName)
方法,通過方法的返回值來判斷
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.lxweimin.com/p/d6b3e16cc1d9
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/1110/3670.html
http://www.lxweimin.com/p/b4a8b3d4f587
http://blog.csdn.net/quan356270259/article/details/50876272
http://blog.csdn.net/u010483016/article/details/50401605
http://www.lxweimin.com/p/e1ab1a179fbb#
http://blog.csdn.net/u011200604/article/details/52874599
http://blog.csdn.net/yangqingqo/article/details/48371123