主要參考Hongyang大神的這篇文章
//Android 6.0 運行時權限處理完全解析
http://blog.csdn.net/lmj623565791/article/details/50709663
概念
Android 6.0 (API level 23) 引入了運行時權限這個機制, 在之前的Android版本中, 開發(fā)者在AndroidManifest.xml中聲明所有的權限, 用戶在安裝apk時進行確認是否同意, 只有在同意后才能成功安裝應用, 這就造成了一些權限濫用的問題, 比如很多app會聲明去訪問手機聯(lián)系人, 位置定位等用戶敏感信息的權限. 而實際上, 這些app本不應該去具有這些權限.
針對這種情況, Android 6.0 把權限劃分為普通權限和危險權限, 對于普通權限, 比如訪問網(wǎng)絡, 和之前的機制是一樣的. 對于危險權限, 一方面要求開發(fā)者在AndroidManifest.xml中進行聲明, 讓用戶在安裝應用時進行確認, 另一方面, 在app運行時, 當用到特定功能時, 比如要獲取用戶的位置信息, Android 系統(tǒng)還會再次談出一個對話框詢問用戶是否同意這一行為, 這也就是運行時權限這個名字的來源.
代碼框架
MainActivity.java
以獲取位置數(shù)據(jù)為例, 正常的代碼流程是:
private static final int PERMISSION_REQUEST_LOCATION = 0;
private void getGPSInfo() {
//檢查是否用戶之前是否同意過這個運行時權限
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//之前沒有同意過這個對話框的話, requestPermissions()會觸發(fā)系統(tǒng)以異步的方式彈出一個對話框進行確認, 回調(diào)在onRequestPermissionsResult()中.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION},
PERMISSION_REQUEST_LOCATION);
} else {
//之前同意過這個對話框的話, 下一步去實際獲取位置數(shù)據(jù).
getCityByGpsInfo();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
JLog.i();
if (requestCode == PERMISSION_REQUEST_LOCATION) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//用戶在系統(tǒng)對話框中選擇同意的話, 下一步去實際獲取位置數(shù)據(jù).
getCityByGpsInfo();
} else {
// Permission Denied
Toast.makeText(MainActivity.this, "location Permission Denied", Toast.LENGTH_SHORT).show();
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
//去實際獲取用戶的位置數(shù)據(jù)
private void getCityByGpsInfo() {
double latitude = 0.0;
double longitude = 0.0;
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Location gpsLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
JLog.i("gpsLocation: " + gpsLocation);
if (gpsLocation != null) {
latitude = gpsLocation.getLatitude();
longitude = gpsLocation.getLongitude();
} else {
Location networkLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
JLog.i("networkLocation: " + networkLocation);
if (networkLocation != null) {
latitude = networkLocation.getLatitude();
longitude = networkLocation.getLongitude();
} else {
JLog.i("gps和network都沒有獲取到位置信息.");
return;
}
}
}
機型適配的問題
- 在Android 6.0 以下的手機上, 因為沒有運行時權限這個機制, 執(zhí)行ActivityCompat.checkSelfPermission() 都會返回0, 也就是PackageManager.PERMISSION_GRANTED.
- 在國內(nèi)的某些機型上, 比如Oppo Android 6.0以上的手機, 它把運行時權限這個機制進行了修改. 執(zhí)行ActivityCompat.checkSelfPermission()會立刻返回0, 把系統(tǒng)的運行時權限彈框移到了真正獲取位置數(shù)據(jù)的API內(nèi)部, 也就是在locationManager.getLastKnownLocation()內(nèi)部, 提供了一個默認彈窗, 并且是以阻塞UI的方式. 如果用戶選擇拒絕的話, 這個API返回null. 如果用戶選擇同意的話, 下次再調(diào)用locationManager.getLastKnownLocation() API的時候也就不會再次彈窗讓用戶進行確認了.
---DONE.---