1. 程式人生 > >Android許可權(Permissions)處理

Android許可權(Permissions)處理

       Android裡面的許可權是一種安全機制。Android許可權主要用於限制應用程式內部某些具有限制性的功能使用,以及應用程式之間的元件訪問。但是呢Android6.0之後,Google對許可權做了一些優化,將一些許可權的申請放在了應用執行的時候去申請(動態獲取許可權),所以Android6.0之後將許可權向分為兩類:Normal Permissions(正常許可權,一般涉及使用者隱私的風險比較小,系統會自動向應用授予該許可權)、Dangerous Permission(敏感許可權,一般是涉及到使用者隱私的,使用者必須明確嚮應用授予該許可權)。

不管是Normal Permissions還是Dangerous permissions許可權,如果我們在程式中需要使用,那麼在AndroidManifest.xml檔案裡面都是需要註冊的。

一,Normal Permissions

       普通許可權涵蓋應用程式需要訪問應用程式沙箱外部資料或資源的區域,但是對於使用者的隱私或其他應用程式的操作幾乎沒有風險。

       如果應用程式在AndroidManifest.xml檔案中註冊了需要的Normal Permissions,系統會在安裝時自動授予應用該許可權。程式使用過程中系統不會提示使用者授予正常許可權,並且使用者無法撤消這些許可權(永久授權)。

Normal Permissions許可權包含:

許可權名 解釋
ACCESS_LOCATION_EXTRA_COMMANDS 允許程式訪問額外的定位提供者指令
ACCESS_NETWORK_STATE 允許程式獲取網路資訊狀態,如當前的網路連線是否有效
ACCESS_NOTIFICATION_POLICY 通知APP通知顯示在狀態列
ACCESS_WIFI_STATE 允許程式獲取當前WiFi接入的狀態以及WLAN熱點的資訊
BLUETOOTH 允許程式連線配對過的藍芽裝置
BLUETOOTH_ADMIN 允許程式進行發現和配對新的藍芽裝置
BROADCAST_STICKY 允許程式收到廣播後快速收到下一個廣播
CHANGE_NETWORK_STATE 允許程式改變網路狀態,如是否聯網
CHANGE_WIFI_MULTICAST_STATE 允許程式改變WiFi多播狀態
CHANGE_WIFI_STATE 允許程式改變WiFi狀態
DISABLE_KEYGUARD 允許程式禁用鍵盤鎖
EXPAND_STATUS_BAR 允許程式擴充套件或收縮狀態列
GET_PACKAGE_SIZE 允許程式獲取應用的檔案大小
INSTALL_SHORTCUT 建立快捷方式
INTERNET 允許程式訪問網路連線,可能產生GPRS流量
KILL_BACKGROUND_PROCESSES 允許程式呼叫killBackgroundProcesses(String).方法結束後臺程序
MANAGE_OWN_CALLS 允許通過自我管理的ConnectionService API管理自己的呼叫的呼叫應用程式
MODIFY_AUDIO_SETTINGS 允許程式修改聲音設定資訊
NFC 允許程式執行NFC近距離通訊操作,用於移動支援
READ_SYNC_SETTINGS 允許程式讀取同步設定,讀取Google線上同步設定
READ_SYNC_STATS 允許程式讀取同步狀態,獲得Google線上同步狀態
RECEIVE_BOOT_COMPLETED 允許程式開機自動執行
REORDER_TASKS 允許程式重新排序系統Z軸執行中的任務
REQUEST_COMPANION_RUN_IN_BACKGROUND 允許伴隨應用在後臺執行
REQUEST_COMPANION_USE_DATA_IN_BACKGROUND 允許伴隨應用在後臺使用資料
REQUEST_DELETE_PACKAGES 允許應用程式請求刪除軟體包
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 應用程式必須擁有該許可權才能使用ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
SET_ALARM 允許程式設定鬧鈴提醒
SET_WALLPAPER 允許程式設定桌面桌布
SET_WALLPAPER_HINTS 允許程式設定桌布建議
TRANSMIT_IR 允許使用裝置的紅外發射器
USE_FINGERPRINT 允許應用使用指紋硬體
VIBRATE 允許程式振動
WAKE_LOCK 允許程式在手機螢幕關閉後後臺程序仍然執行
WRITE_SYNC_SETTINGS 寫入Google線上同步設定

如果程式需要用到Normal Permissions許可權,那麼這些用到的許可權都必須在AndroidManifest.xml中註冊

二、Dangerous permissions

       Dangerous permissions涵蓋應用程式需要涉及使用者私人資訊的資料或資源的區域,或者可能會影響使用者的儲存資料或其他應用程式的操作。例如,閱讀使用者聯絡人的許可權是一個危險的許可權。如果一個應用程式宣告它需要一個危險的許可權,使用者必須明確授予該應用程式的許可權。在使用者批准許可權之前,您的應用無法提供取決於該許可權的功能。

       當我們需要使用Dangerous permissions的時候要特別注意,我們分兩種情況來說明:

  • targetSdkVersion >= 23 並且 Android手機 >= 6.0:這個時候Dangerous permissions在使用的時候,就需要我們去動態的申請許可權了,比如:當我們需要開啟相機拍攝照片的時候需要我們通過程式碼的方式在需要的地方去申請許可權(關於程式碼的實現方式我們在下文中會提到)。

  • targetSdkVersion < 23 或者 Android手機 < 6.0:和Normal Permissions許可權使用一樣,系統會在應用程式安裝的時候自動授予應用該許可權。

Dangerous Permissions許可權包含(特別注意下那些許可權是屬於同許可權一組的):

許可權名 許可權組 解釋
READ_CALENDAR CALENDAR(日曆組) 允許程式讀取使用者的日程資訊
WRITE_CALENDAR CALENDAR(日曆組) 允許程式寫入日程,但不可讀取
CAMERA CAMERA(相機拍照組) 允許程式訪問攝像頭進行拍照
READ_CONTACTS CONTACTS(聯絡人組) 允許程式訪問聯絡人通訊錄資訊
WRITE_CONTACTS CONTACTS(聯絡人組) 允許程式寫入聯絡人,但不可讀取
GET_ACCOUNTS CONTACTS(聯絡人組) 允許程式訪問賬戶Gmail列表
ACCESS_FINE_LOCATION LOCATION(定位組) 允許程式通過GPS晶片接收衛星的定位資訊
ACCESS_COARSE_LOCATION LOCATION(定位組) 允許程式通過WiFi或移動基站的方式獲取使用者錯略的經緯度資訊
RECORD_AUDIO MICROPHONE(麥克風組) 允許程序錄制聲音通過手機或耳機的麥克
READ_PHONE_STATE PHONE(電話組) 允許程式訪問電話狀態
READ_PHONE_NUMBERS PHONE(電話組) 允許程式讀取裝置的電話號碼
CALL_PHONE PHONE(電話組) 允許程式從非系統撥號器裡撥打電話
ANSWER_PHONE_CALLS PHONE(電話組) 允許程式接聽來電
READ_CALL_LOG PHONE(電話組) 允許程式讀取通話記錄
WRITE_CALL_LOG PHONE(電話組) 允許程式寫入(但是不能讀)使用者的聯絡人資料
ADD_VOICEMAIL PHONE(電話組) 允許程式新增語音郵件系統
USE_SIP PHONE(電話組) 允許程式使用SIP視訊服務
PROCESS_OUTGOING_CALLS PHONE(電話組) 允許程式監視,修改或放棄播出電話
BODY_SENSORS SENSORS(感測器組) 允許應用程式訪問使用者用來測量身體內部情況的感測器資料,例如心率
SEND_SMS SMS(手機簡訊服務組) 允許程式傳送簡訊
RECEIVE_SMS SMS(手機簡訊服務組) 允許程式接收簡訊
READ_SMS SMS(手機簡訊服務組) 允許程式讀取簡訊內容
RECEIVE_WAP_PUSH SMS(手機簡訊服務組) 允許程式接收WAP PUSH資訊
RECEIVE_MMS SMS(手機簡訊服務組) 允許程式接收彩信
READ_EXTERNAL_STORAGE STORAGE(儲存組) 允許程式可以讀取裝置外部儲存空間
WRITE_EXTERNAL_STORAGE STORAGE(儲存組) 允許程式寫入外部儲存,如SD卡上寫檔案

       在介紹Dangerous permissions的時候,提到了許可權組,這個許可權組有啥用呢。同一組的任何一個許可權被授權了,該組的其他許可權也自動被授權了。舉個例子,例如我們動態授予了READ_CALENDAR許可權,這個時候因為READ_CALENDAR是屬於CALENDAR組的,CALENDAR組裡面同時還包含了WRITE_CALENDAR許可權。所以WRITE_CALENDAR許可權也被自動授權了。

三、動態申請許可權

       上文中我們也提到了,當我們targetSdkVersion >= 23 並且 Android手機 >= 6.0(兩個條件要同時成立)的時候,如果我們想使用Dangerous permissions裡面的許可權的話,是需要通過程式碼去動態申請許可權的。

       動態申請許可權相關的一些函式:

    /**
     * 檢查指定的許可權是否授權(Context物件呼叫)
     */
    (Context).checkSelfPermission(@NonNull String permission);

    /**
     * 在沒有授權的情況下,有些時候可能需要提示給使用者為什麼需要改許可權,就通過該函式來實現。
     * 關於shouldShowRequestPermissionRationale的返回值問題,我們分三種情況
     * 1. 第一次開啟App時 -> false
     * 2. 上次彈出許可權點選了禁止(但沒有勾選“下次不在詢問”) -> true
     * 3. 上次選擇禁止並勾選:下次不在詢問 -> false
     */
    (Activity或者Fragment).shouldShowRequestPermissionRationale(@NonNull String permission);

    /**
     * 申請指定的許可權(Activity或者Fragment物件呼叫)
     * @param permissions 許可權列表,可以同時申請多個許可權
     * @param requestCode 該次許可權申請對應的requestCode。和 onRequestPermissionsResult()回撥函式裡面的requestCode對應
     */
    (Activity或者Fragment).requestPermissions(@NonNull String[] permissions, int requestCode);


    /**
     * 處理請求許可權的響應,當用戶對請求許可權的dialog做出響應之後,系統會回撥該函式(Activity或者Fragment中重寫)
     * @param requestCode 申請許可權對應的requestCode
     * @param permissions 許可權列表
     * @param grantResults 許可權列表對應的返回值,判斷permissions裡面的每個許可權是否申請成功
     */
    onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                               @NonNull int[] grantResults);

       動態申請許可權分為以下幾個步驟:

  1. 明確我們頁面需要哪些許可權。
  2. 檢查需要的許可權是否授權 -> checkSelfPermission()。
  3. 如果沒有授權,則判斷是否需要向用戶解釋為何申請許可權。-> shouldShowRequestPermissionRationale() 返回true則可能需要彈出框解釋下。(大部分情況下都會直接跳過這一步,不做這一步的處理)
  4. 申請許可權。-> requestPermissions()。
  5. 處理申請的結果資訊。-> 回撥函式onRequestPermissionsResult()。

       關於動態申請許可權網上的開源庫也很多,這裡我們就簡單的介紹其中一種PermissionGen。對應GitHub專案地址:https://github.com/lovedise/PermissionGen

       PermissionGen的使用也是非常簡單的。我們先定義一個BaseAppActivity。在BaseAppActivity的onRequestPermissionsResult()方法裡面呼叫PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);

ublic abstract class BaseAppActivity extends AppCompatActivity {

    protected Context  mContext;
    protected Activity mActivity;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        mActivity = this;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);
    }
}

       PermissionGen申請許可權,

Activity中

PermissionGen.with(MainActivity.this)
    .addRequestCode(100)
    .permissions(
        Manifest.permission.READ_CONTACTS,
        Manifest.permission.RECEIVE_SMS,
        Manifest.permission.WRITE_CONTACTS)
    .request();

Fragment中

PermissionGen.needPermission(ContactFragment.this, 100, 
    new String[] {
        Manifest.permission.READ_CONTACTS, 
        Manifest.permission.RECEIVE_SMS,
        Manifest.permission.WRITE_CONTACTS
    }
);

       PermissionGen結果確認。

成功結果

@PermissionSuccess(requestCode = 100)
public void doSomething(){
    Toast.makeText(this, "Contact permission is granted", Toast.LENGTH_SHORT).show();
}

失敗結果

@PermissionFail(requestCode = 100)
public void doFailSomething(){
    Toast.makeText(this, "Contact permission is not granted", t.LENGTH_SHORT).show();
}

       我們以一個具體的例項來做一個簡單的說明,比如我們在Activity中申請攝像頭許可權。注意哦,我這裡是繼承的BaseAppActivity,BaseAppActivity中重寫了onRequestPermissionsResult(),裡面呼叫了PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);。

public class PermissionGenActivity extends BaseAppActivity {

    public static void startUp(Context context) {
        context.startActivity(new Intent(context, PermissionGenActivity.class));
    }

    private static final int CAMERA_PERMISSION = 100;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission_gen);
        //申請Manifest.permission.CAMERA許可權
        PermissionGen.with(mActivity).addRequestCode(CAMERA_PERMISSION).permissions(Manifest.permission.CAMERA).request();
    }

    @PermissionSuccess(requestCode = CAMERA_PERMISSION)
    public void requestPhotoSuccess() {
        //申請許可權成功
    }

    @PermissionFail(requestCode = CAMERA_PERMISSION)
    public void requestPhotoFail() {
        onBackPressed();
    }
}

       關於Android許可權(Permissions)處理,有以下幾個總結點:

  • 不管是Normal Permissions還是Dangerous Permission對應的許可權都必須在AndroidManifest.xml檔案裡面註冊。

  • 只有當targetSdkVersion >= 23 並且 Android手機 >= 6.0兩個條件同時滿足時,Dangerous Permission對應的許可權才需要動態申請。否則還是和Normal Permissions使用一樣會在程式安裝的時候就獲取到許可權。

  • Dangerous Permission許可權裡面某個組裡面一個許可權被授權了,那麼這個組裡面其他的許可權也跟著授權了。


       文章中涉及到的DEMO下載地址