不得不說的Android6.0新特性

*Android6.0簡介:*

Android6.0稱爲Marshmallow(棉花糖),在2015年Google的I/O大會上正式發佈,至今已經有一年多了,所佔市場份額在2.3%以上,更新了比較多的模塊,當然主要是對Android系統進行了優化,對於我們碼農來說主要是應用權限的管理


*主要更新模塊*

  • 電源管理
    在原有的電源管理的基礎上加入了兩種新的狀態:

    1. App Standby—應用待機狀態
      當系統檢測到設備不充電,且用戶沒有直接或間接啓動該應用時,該應用進入應用待機狀態,而反之(當應用被激活或者設備在充電時)系統將該應用移出應用待機狀態;
    2. Doze 系統休眠狀態
      當系統檢測到設備不充電,且設備靜止滅屏一段時間會進入休眠狀態,而如此週期性檢測,狀態不改變則會進入更長的休眠狀態,一直到進入飽和休眠狀態;
  • 規範化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卡和撥打電話權限

  1. 首先在清單文件中添加:
<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的在監聽用戶是否授權時對失敗的回調沒有成功,不知什麼情況,有興趣的朋友可以去了解下,但是邏輯他就是這麼個邏輯,理就是這麼個理,你弄懂了這個理,就可以直接去使用第三方庫了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章