運(yùn)行時(shí)權(quán)限
此版本引入了一種新的權(quán)限模式埋泵,如今砂竖,用戶可直接在運(yùn)行時(shí)管理應(yīng)用權(quán)限。這種模式讓用戶能夠更好地了解和控制權(quán)限谭跨,同時(shí)為應(yīng)用開發(fā)者精簡(jiǎn)了安裝和自動(dòng)更新過程。用戶可為所安裝的各個(gè)應(yīng)用分別授予或撤銷權(quán)限。
對(duì)于以 Android 6.0(API 級(jí)別 23)或更高版本為目標(biāo)平臺(tái)的應(yīng)用螃宙,請(qǐng)務(wù)必在運(yùn)行時(shí)檢查和請(qǐng)求權(quán)限蛮瞄。要確定您的應(yīng)用是否已被授予權(quán)限,請(qǐng)調(diào)用新增的 checkSelfPermission() 方法谆扎。要請(qǐng)求權(quán)限挂捅,請(qǐng)調(diào)用新增的 requestPermissions() 方法。即使您的應(yīng)用并不以 Android 6.0(API 級(jí)別 23)為目標(biāo)平臺(tái)堂湖,您也應(yīng)該在新權(quán)限模式下測(cè)試您的應(yīng)用闲先。
如需了解有關(guān)在您的應(yīng)用中支持新權(quán)限模式的詳情,請(qǐng)參閱使用系統(tǒng)權(quán)限无蜂。如需了解有關(guān)如何評(píng)估新模式對(duì)應(yīng)用的影響的提示伺糠,請(qǐng)參閱權(quán)限最佳做法。
新增檢查方法 checkSelfPermission()
| 參數(shù) |
permission String:正在檢查的權(quán)限的名稱斥季。
這個(gè)值絕對(duì)不能null训桶。
| 返回 |
| int | PackageManager.PERMISSION_GRANTED如果您有權(quán)限,或者如果沒有酣倾。PackageManager.PERMISSION_DENIED
價(jià)值是PERMISSION_GRANTED或PERMISSION_DENIED舵揭。
新增請(qǐng)求權(quán)限方法 requestPermissions()
| 參數(shù) |
permissions String:請(qǐng)求的權(quán)限。我必須非空并且不是空的躁锡。
requestCode int:特定于應(yīng)用程序的請(qǐng)求代碼以與報(bào)告的結(jié)果匹配午绳。應(yīng)該>=0onRequestPermissionsResult(int, String[], int[])
| 拋出 |
IllegalArgumentException 如果requestCode為負(fù)數(shù)。
官方有個(gè)地方說可以使用ContextWrapper.checkSelfPermission(String) 映之。其實(shí)這個(gè)方法很多地方都能使用到拦焚。
好,Android M 為什么會(huì)新增這兩方法呢.因?yàn)樵贏ndroid 6.0 (API 23) 開始用戶開始在應(yīng)用運(yùn)行時(shí)向其授予權(quán)限,而不是在應(yīng)用安裝時(shí)授予惕医。這種權(quán)限機(jī)制可以讓用戶更好的管理應(yīng)用的權(quán)限耕漱,保障用戶隱私。從此之后...
系統(tǒng)權(quán)限分為兩種
正常權(quán)限
不會(huì)直接給用戶隱私權(quán)帶來風(fēng)險(xiǎn)抬伺。如果您的應(yīng)用在其清單中列出了正常權(quán)限螟够,系統(tǒng)將自動(dòng)授予該權(quán)限。
android.permission.ACCESS LOCATIONEXTRA_COMMANDS
android.permission.ACCESS NETWORKSTATE
android.permission.ACCESS NOTIFICATIONPOLICY
android.permission.ACCESS WIFISTATE
android.permission.ACCESS WIMAXSTATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE NETWORKSTATE
android.permission.CHANGE WIFIMULTICAST_STATE
android.permission.CHANGE WIFISTATE
android.permission.CHANGE WIMAXSTATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND STATUSBAR
android.permission.FLASHLIGHT
android.permission.GET_ACCOUNTS
android.permission.GET PACKAGESIZE
android.permission.INTERNET
android.permission.KILL BACKGROUNDPROCESSES
android.permission.MODIFY AUDIOSETTINGS
android.permission.NFC
android.permission.READ SYNCSETTINGS
android.permission.READ SYNCSTATS
android.permission.RECEIVE BOOTCOMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST INSTALLPACKAGES
android.permission.SET TIMEZONE
android.permission.SET_WALLPAPER
android.permission.SET WALLPAPERHINTS
android.permission.SUBSCRIBED FEEDSREAD
android.permission.TRANSMIT_IR
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE SYNCSETTINGS
com.android.alarm.permission.SET_ALARM
com.android.launcher.permission.INSTALL_SHORTCUT
com.android.launcher.permission.UNINSTALL_SHORTCUT
危險(xiǎn)權(quán)限
會(huì)授予應(yīng)用訪問用戶機(jī)密數(shù)據(jù)的權(quán)限峡钓。如果您列出了危險(xiǎn)權(quán)限妓笙,則用戶必須明確批準(zhǔn)您的應(yīng)用使用這些權(quán)限。
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
閱讀到這里,小伙伴會(huì)有疑問.為什么危險(xiǎn)權(quán)限一組一組能岩。這里解釋一下,比如 permission-group.SMS 中的其中一條READ_SMS被授權(quán)了寞宫。那么在使用其他同組權(quán)限的時(shí)候就不會(huì)去向用戶發(fā)起請(qǐng)求,我們使用checkSelfPermission得到的結(jié)果也是授權(quán)的拉鹃。
兼容性的問題
在Android 6.0 以前只要在AndroidManifest.xml上申明了權(quán)限,應(yīng)用就默認(rèn)帶有權(quán)限.(在原生系統(tǒng)中)辈赋。有些廠商的手機(jī)一樣可以讓用戶關(guān)閉掉權(quán)限鲫忍。那么...在API<23的情況下關(guān)掉權(quán)限.再使用權(quán)限.那么只有死路條.所以。采取良好的方案就是Try 钥屈。
封裝思路
其實(shí)每寫一篇簡(jiǎn)書前我都會(huì)看其他人寫的文章,這樣就可以把每個(gè)人的優(yōu)勢(shì)結(jié)合在一起悟民。我之前做適配的時(shí)候也在Github上找過動(dòng)態(tài)權(quán)限申請(qǐng)的庫(kù),也可以說成是工具類把。我去看了源碼篷就。又一個(gè)聲稱可以在任何地方都可以使用到權(quán)限,后來我就用上了這個(gè)射亏。其實(shí)當(dāng)時(shí)我并不是很理解,因?yàn)闄?quán)限當(dāng)時(shí)我去找資料了是Activity才能發(fā)起了.回調(diào)固然回到Activity,后來我翻看他的源碼...是new 了一個(gè)Activity出來.還有一些工具類是讓你一行代碼調(diào)用.然后在onRequestPermissionsResult也要寫一行工具類的代碼.我覺得這種比較麻煩。后來我就想到了竭业。將使用到的方法功能封裝在一個(gè)Activity里面智润。讓BaseActivity去繼承這個(gè)PermissionRequestActivity。一個(gè)方法使用,給一個(gè)回調(diào)出來,授權(quán)權(quán)限是否成功未辆。
PermissionRequestActivity
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
/**
* Created by 賣火柴的小女孩 - Jc on 2018/8/21.
*/
public class PermissionRequestActivity extends FragmentActivity {
private static final int PERMISSION_REQUEST_CODE = 1088;
private String mPermissionDes;
private CallBack mCallBack;
/**
* 權(quán)限申請(qǐng)使用方法
*
* @param permissionDes 權(quán)限說明
* @param callBack 申請(qǐng)回調(diào)
* @param permissions 申請(qǐng)權(quán)限
*/
protected void requestPermission(String permissionDes, CallBack callBack, @NonNull String... permissions) {
mCallBack = callBack;
mPermissionDes = permissionDes;
if (checkPermission(permissions))
mCallBack.hasPermission();
else {
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
}
}
/**
* 判斷系統(tǒng)版本大于6.0的時(shí)候
*
* @param permissions 申請(qǐng)權(quán)限
* @return
*/
protected boolean checkPermission(@NonNull String... permissions) {
//大于6.0的時(shí)候需要?jiǎng)討B(tài)申請(qǐng)權(quán)限.小于6.0的時(shí)候如果用戶手動(dòng)關(guān)閉權(quán)限,程序即崩 需要做Try處理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return checkSelfPermissions(permissions);
}
return true;
}
/**
* 檢查權(quán)限是否已經(jīng)授權(quán)
*
* @param permissions 申請(qǐng)權(quán)限
* @return
*/
private boolean checkSelfPermissions(@NonNull String... permissions) {
boolean flag = true;
for (String p : permissions) {
if (ActivityCompat.checkSelfPermission(this, p) != PackageManager.PERMISSION_GRANTED) {
flag = false;
break;
}
}
return flag;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
boolean hasAllGranted = true;
for (int i = 0; i < grantResults.length; ++i) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
hasAllGranted = false;
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])) {
showDialogPrompt();
} else {
//權(quán)限申請(qǐng)被拒絕 窟绷,但用戶未選擇'不再提示'選項(xiàng)
mCallBack.lossPermission();
}
break;
}
}
if (hasAllGranted) {
mCallBack.hasPermission();
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
/**
* 由于用戶手動(dòng)關(guān)閉權(quán)限提示。APP需要做人性化提示
*/
private void showDialogPrompt() {
new AlertDialog.Builder(this)
.setTitle("權(quán)限申請(qǐng)")
.setMessage(mPermissionDes)
.setCancelable(false)
.setPositiveButton("去設(shè)置", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//引導(dǎo)用戶至設(shè)置頁(yè)手動(dòng)授權(quán)
getAppSetting();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//引導(dǎo)用戶手動(dòng)授權(quán)鼎姐,權(quán)限請(qǐng)求失敗
mCallBack.lossPermission();
}
}).show();
}
/**
* 跳轉(zhuǎn)設(shè)置 應(yīng)用設(shè)置界面
*
* @return
*/
private Intent getAppSetting() {
Intent localIntent = null;
if (Build.VERSION.SDK_INT >= 9) {
localIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
localIntent.setData(Uri.fromParts("package", getPackageName(), null));
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else if (Build.VERSION.SDK_INT <= 8) {
localIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
localIntent.setAction(Intent.ACTION_VIEW);
localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
localIntent.putExtra("com.android.settings.ApplicationPkgName", getPackageName());
localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
startActivity(localIntent);
return localIntent;
}
/**
* 權(quán)限申請(qǐng)回調(diào)
*/
interface CallBack {
void hasPermission();
void lossPermission();
}
}
使用
requestPermission("獲取手機(jī)信息-獲取手機(jī)號(hào)碼钾麸、IMEI、IMSI權(quán)限\n讀寫手機(jī)存儲(chǔ)-讀寫手機(jī)存儲(chǔ)", new CallBack() {
@Override
public void hasPermission() {
String IMEI = getIMEI(MainActivity.this);
((TextView) findViewById(R.id.tv)).setText(IMEI);
}
@Override
public void lossPermission() {
Toast.makeText(MainActivity.this, "權(quán)限申請(qǐng)被拒絕", Toast.LENGTH_SHORT).show();
}
}, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_EXTERNAL_STORAGE);
代碼講解
其實(shí)代碼不多,只有100多行炕桨。
方法: requestPermission
參數(shù):String permissionDes -- 用于用戶選擇'不再提示'按鈕提示語(yǔ)
CallBack callBack -- 應(yīng)用是否帶有權(quán)限或是否授權(quán)權(quán)限成功
@NonNull String... permissions -- 需要授權(quán)或檢查的權(quán)限
這個(gè)方法一進(jìn)去 我做了一個(gè)判斷 checkPermission(permissions) , 先去檢查是否有權(quán)限,如果有權(quán)限的話.那么就不去授權(quán)權(quán)限了.
方法 :checkPermission() 進(jìn)來判斷是否有權(quán)限的時(shí)候這個(gè)方法有一個(gè)API 就是剛才最上面開始講的checkSelfPermission 這個(gè)需要 Build.VERSION.SDK_INT >= Build.VERSION_CODES.M..所以不滿足這個(gè)條件的都以TRUE返回出去,表明應(yīng)用在6.0以下的設(shè)備默認(rèn)有這些權(quán)限饭尝。但是剛才在兼容性的時(shí)候講過了.會(huì)崩。
接下來檢測(cè)到?jīng)]有權(quán)限,使用了這一段代碼
ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE);
這一行就是向用戶發(fā)起權(quán)限授權(quán)的代碼了.接下來 我們看 onRequestPermissionsResult 献宫。 這個(gè)方法是向用戶請(qǐng)求權(quán)限的回調(diào)結(jié)果钥平。這個(gè)方法里面我做了處理,我檢測(cè)了用戶是否點(diǎn)擊了 ' 不再提示 ' 的按鈕。(有些手機(jī)沒有這個(gè)按鈕,有些手機(jī)有,當(dāng)選擇了此按鈕之后 再次發(fā)起授權(quán)請(qǐng)求,系統(tǒng)將不再向用戶發(fā)起權(quán)限授權(quán)請(qǐng)求.而是直接返回拒絕的信息回來.)那么這時(shí)我們就需要做一個(gè)友好的提示了姊途。如果用戶是由于手動(dòng)點(diǎn)擊了這個(gè)不再提示的按鈕而導(dǎo)致第二次或第n次沒有收到授權(quán)的消息.那我們應(yīng)用需要給用戶一個(gè)提示框showDialogPrompt(),我這里寫了一個(gè)最簡(jiǎn)單的彈窗涉瘾。很多APP都有自己主題的彈窗.主要就是提示用戶。告知用戶 他的權(quán)限被他自己手動(dòng)不再提示了.那么我們給他一個(gè)系統(tǒng)設(shè)置界面的跳轉(zhuǎn)getAppSetting(); 然后這個(gè)方法的Build.VERSION.SDK_INT <= 8 我就不解釋了.有興趣的就自己去百度捷兰。