*Android6.0簡介:*
Android6.0稱爲Marshmallow(棉花糖),在2015年Google的I/O大會上正式發佈,至今已經有一年多了,所佔市場份額在2.3%以上,更新了比較多的模塊,當然主要是對Android系統進行了優化,對於我們碼農來說主要是應用權限的管理。
*主要更新模塊*
電源管理
在原有的電源管理的基礎上加入了兩種新的狀態:- App Standby—應用待機狀態
當系統檢測到設備不充電,且用戶沒有直接或間接啓動該應用時,該應用進入應用待機狀態,而反之(當應用被激活或者設備在充電時)系統將該應用移出應用待機狀態; - Doze 系統休眠狀態
當系統檢測到設備不充電,且設備靜止滅屏一段時間會進入休眠狀態,而如此週期性檢測,狀態不改變則會進入更長的休眠狀態,一直到進入飽和休眠狀態;
- App Standby—應用待機狀態
規範化App Link (應用程序鏈接)
*技術點:隱式Intent
鼓勵應用程序間關聯,弱化瀏覽器的使用:比如說用戶點擊淘寶的廣告,優先考慮跳轉到淘寶APP(如果用戶安裝了淘寶APP),而不是瀏覽器廣告。*指紋識別(統一API)
6.0以前手機廠商自己研發(良莠不齊),6.0以後Android提供API,廠商只需要提供硬件支持即可。- 應用權限管理:運行時權限
*5.0以前,只需要在manifest清單文件中註冊聲明即可;
5.0以後,用戶可以在安裝時關閉某些權限;
6.0及以後,對於一些隱私權限會在第一時間提示用戶是否授權(類Iphone);*
運行時權限開發應用
Android6.0對權限進行了分類,分爲:
1. Normal Premission:普通權限
2. Dangerous Permission/Group: 危險權限/組
*危險權限分組:比如當用戶授予應用讀sdk權限,則該應用同時擁有寫的權限,而不會再次詢問用戶*
新增API
- ContextCompact.checkSelfPermission() 檢測是否擁有權限
- ActivityCompact.requestPermission() 申請授權
- onRequestPermissionsResult() 用戶是否授權
- ActivityCompat.shouldShowRequestPermissionRationale() 權限解釋(用戶拒絕後出現)
使用流程:
- 在manifest清單文件中添加權限(適配6.0以下)
- 檢查權限(危險)
- 申請授權
危險權限查看:
- 官網查看
- 命令行查看:控制檯輸入下面命令即可查看危險權限組
adb shell pm list permissions -d -g
下面是危險權限組:
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
下面是正常權限:
ACCESS_LOCATION_EXTRA_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
代碼實戰:讀寫SD卡和撥打電話權限
- 首先在清單文件中添加:
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
6.0以前,我們可以直接在點擊事件裏調用下面的代碼實現撥打電話的功能:
startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+10086)));
6.0以後這樣寫不行,程序會閃退,報一下錯誤,我們可以專門寫一個方法來實現:
首先我們可以把撥打電話的代碼進行封裝:
/**
* 通過隱式Intent去撥打電話
*/
private void doCallPhone() {
startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+10086)));
}
在AndroidStudio中doCallPhone()方法裏面的代碼會被劃上紅色波浪線,不要擔心,這裏並不影響編譯和運行,只是提示你這裏有危險權限需要檢查是否授權。
然後需要檢查是否授權,有授權就可以直接撥打電話,如果沒有就需要申請授權。
/**
* 6.0後撥打電話
*/
private void callPhone(){
//當沒有授權時
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){
//申請權限
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE},1);
}else {
doCallPhone();
}
}
在申請授權時requestPermission()方法的第二個參數可以同時申請多個權限,第三個參數是請求碼。
在沒有授權的情況下只是申請授權是不夠的,還需要監測申請授權是否成功,成功就可以直接調用撥打電話的doCallPhone()方法,不成功提示用戶或者其他邏輯,下面我們來看代碼:
/**
* 處理申請授權是否成功
* @param requestCode 請求碼
* @param permissions 權限數組
* @param grantResults 授權結果數組
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
//打電話權限回調
if (grantResults[0]==PackageManager.PERMISSION_GRANTED){
doCallPhone();
}else {
//提示用戶權限未被授予
Log.d("MainActivity","未授予撥打電話權限");
}
break;
}
}
由於前面只有一個權限的申請,所以就只使用了grantResults[0],如果是多個權限可以使用foreach遍歷,具體寫法下面的封裝中會提及;至此,一個撥打電話的授權就介紹了。如果大家覺得授權很麻煩,目前可以暫時修改app的buildgradle配置文件中targetSdkVersion爲小於23即可,而編譯版本可以不變,如下:
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.jack.justforandroid6"
minSdkVersion 15
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
...
}
當然這只是懶方法,躲得了初一,躲不過十五,所以有眼光的攻城獅們,會爲了我們的應用有更頑強的生命力,也是要順應時代趨勢,堅定地進行運行時權限的適配。
這裏只是一個授權申請就寫了這麼多代碼,那當有很多權限需要用戶授權呢?
那接下來我們就來封裝授權邏輯。
在項目開發中,我們的Activity一般都是繼承自BaseActivity,那麼我們的運行時權限是否能放到BaseActivity中呢?
Why not ?
那麼接下我們就來實踐下:
既然每次我們都要判斷每一個危險權限是否被授權,那我們就在BaseActivity封裝一個方法來判斷某權限是否已經授權,如下
/**
* 判斷是否已經授權
* @param permissions 權限數組(String...就是數組類型)
* @return
*/
public boolean hasPermission(String... permissions){
for (String permission:permissions){
if (ContextCompat.checkSelfPermission(this,
permission)!= PackageManager.PERMISSION_GRANTED){
return false;
}
}
return true;
}
然後就是沒有授權時申請權限了,我們來寫個常量類用作申請授權的請求碼:
/**
* 權限常量相關
* Created by Jack on 16/12/1.
*/
public class Constants {
//讀SD卡權限請求碼
public static final int WRITE_EXTERNAL_CARD=0x01;
//寫SD卡權限請求碼
public static final int READ_EXTERNAL_CARD=0x01;
//寫打電話權限請求碼
public static final int CALL_PHONE_CARD=0x02;
}
接着我們來實現申請權限的封裝:
/**
* 權限申請方法
*/
public void requestPermission(int code,String... permissions){
ActivityCompat.requestPermissions(this,
permissions,code);
}
在這裏申請權限後就是要監聽用戶是否授予權限了,來看下如何封裝:
/**
* 授權回調處理
* @param requestCode
* @param permissions
* @param grantResults
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (checkPermissionResult(grantResults)) {
switch (requestCode) {
case Constants.WRITE_EXTERNAL_CARD:
//處理寫sd卡授權回調
doWriteExternalCard();
break;
case Constants.CALL_PHONE_CARD:
//處理寫sd卡授權回調
doCallPhone();
break;
}
}else {
Toast.makeText(getApplicationContext(), "權限已拒絕", Toast.LENGTH_SHORT).show();
}
}
/**
* 檢測請求結果碼判定是否授權
* @param grantResults
* @return
*/
private boolean checkPermissionResult(int[] grantResults) {
if (grantResults!=null) {
for (int result : grantResults) {
if (result==PackageManager.PERMISSION_GRANTED){
return true;
}
}
}
return false;
}
/**
可以看到這裏我們首先判斷用戶是否授予了我們權限,然後再通過請求碼區分不同的權限請求,然後調用了一個空方法,等待子類去重寫。這裏爲什麼用空方法呢,在BaseActivity我們無法預知具體的授權邏輯,所以在BaseActivity中只是一些空方法,當然如果業務允許,也是可以執行一些邏輯,子類中調用super.doCallPhone()(其他方法同理)方法就好了然後在子類中重寫就好了,看代碼:
/**
* 默認的打電話邏輯
*/
public void doCallPhone() {
//這裏父類並沒有執行任何邏輯,但可以加一些通用的邏輯,然後讓子類去重寫
}
那麼接下就是看集成自BaseActivity的Activity中如何調用、如何重寫來完成運行時權限的適配了:在點擊事件中調用撥打電話的邏輯判定和實現;
/**
* 撥打電話
*/
private void callPhone(){
if (hasPermission(Manifest.permission.CALL_PHONE)){
doCallPhone();
}else {
requestPermission(Constants.CALL_PHONE_CARD,Manifest.permission.CALL_PHONE);
}
}
如上代碼:首先就是調用BaseActivity判斷是否已經擁有權限,如果有授權就執行通過隱式Intent啓動撥打電話程序,否則申請用戶授權。
因爲父類已經實現了onRequestPermissionsResult()的回調,所以子類就只需要重寫父類的doCallPhone()方法,然後去具體實現打電話的邏輯:
/**
* 子類具體去實現打電話的邏輯
*/
@Override
public void doCallPhone() {
startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:"+10086)));
}
至此,我們所有的封裝都完成的了,相信大家看過這篇博客心中就有思路了,遇到Android6.0就再不慌了。
這裏我們沒有使用任何第三方庫,自己封裝完成所有運行時權限的邏輯,當然現在也有封裝的很好的第三方庫,例如:PremissionGen,AndPremission,但是PremissionGen的在監聽用戶是否授權時對失敗的回調沒有成功,不知什麼情況,有興趣的朋友可以去了解下,但是邏輯他就是這麼個邏輯,理就是這麼個理,你弄懂了這個理,就可以直接去使用第三方庫了。