1. 程式人生 > >Android6.0許可權適配的那些坑

Android6.0許可權適配的那些坑

記錄一下自己在專案中做6.0適配時遇到的一些坑,希望大家可以少走一些彎路。

1.首先你需要把targetSdkVersion升級到23

2.主要API

public int checkSelfPermission (String permission)

被授權函式返回PackageManager.PERMISSION_GRANTED,否則返回PackageManager.PERMISSION_DENIED 。

public final void requestPermissions(@android.support.annotation.NonNull java.lang.String[] permissions,int requestCode)

為應用程式申請許可權,系統會彈出對話方塊,詢問使用者是否給予應用授權該許可權,使用者可以選擇允許或拒絕。開發者可以在Activity的 public void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) 方法中處理使用者選擇後的回撥,包括允許和拒絕。

public boolean shouldShowRequestPermissionRationale (String permission)

它的意思是是否需要彈框像使用者解釋為什麼需要申請這個許可權。它的使用場景是,應用沒有該許可權,而且在使用者不能直觀的看出該許可權和當前功能的關係。比如,我們做一個相機應用,毫無疑問需要相機許可權,我們不需要再單獨向用戶解釋為什麼需要相機許可權,但是該應用還需要定位許可權來為相片新增標籤,對於對科技不是很瞭解的使用者來說,他們很難理解照相為什麼還需要定位許可權,這時候就應該想想怎麼向用戶解釋清楚了。

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

}

許可權申請的回撥。

3.使用相容庫來相容舊版

對於第2條提到的程式碼在android 6.0以上執行沒問題,但是23 api之前就不行了,因為沒有那些方法。v4相容庫已對這個做過相容,可以用以下方法代替:

ContextCompat.checkSelfPermission()

被授權函式返回PackageManager.PERMISSION_GRANTED,否則返回PackageManager.PERMISSION_DENIED ,在所有版本都是如此。

ActivityCompat.requestPermissions()

這個方法在M之前版本呼叫,OnRequestPermissionsResultCallback 直接被呼叫,帶著正確的 PERMISSION_GRANTED或者 PERMISSION_DENIED 。

ActivityCompat.shouldShowRequestPermissionRationale()

在M之前版本呼叫,永遠返回false。

4.許可權和許可權組

大家都知道,並不是所有的許可權都需要動態申請,只是一些系統規定的危險許可權,許可權被分組了:
許可權組
同一組的任何一個許可權被授權了,其他許可權也自動被授權。例如,一旦WRITE_CONTENTS被授權了,APP也有READ_CONTACTS和GET_ACCOUNTS了。

但是:某些手機廠商比如小米,對於許可權組好像有自己的策略,在測試當中,我們發現,當給應用賦予了CALL_PHONE 許可權後,並沒有獲得READ_PHONE_STATE許可權,測試手機的系統是MIUI 8.6.7.14開發版

5.應用必須的許可權及使用時的許可權

  • 對於應用的必須許可權,比如讀取手機狀態等,我們的策略是在啟動頁中進行申請。如果使用者允許,則繼續執行,如果拒絕,則退出應用。

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...省略無關程式碼
    
        PermissionUtil.getInstance().requestNecessaryPermissions(this, new OnPermissionListener() {
            @Override
            public void onDenied() {
                MyApplication.getInstance().exit(true);
            }
    
            @Override
            public void onGranted() {
                init();
            }
        });
    }
    
  • 如果是某些具體功能才會用到的許可權,比如拍照功能,我們的策略是在需要用到的時候再進行申請(當然是在相機工具類中統一處理的,如果你們沒有封裝,那就只好每個入口都要判斷了)

    /**
    * 啟動相機功能
    *
    * @param activity
    */
    public static void startCamera(final Activity activity) {
        PermissionUtil.getInstance().requestCamera(activity, new    OnPermissionListener() {
                @Override
                public void onDenied() {
    
                }
    
                @Override
                public void onGranted() {
                    start(activity);
            }
        });
    

    }

6.在APP使用過程中,從設定中更改許可權

特別提醒:在APP使用過程中,從設定中更改許可權,應用程式會被重啟。或者說,會被系統Kill掉然後重新啟動一個新的,當然它會記住當前的任務棧。所以再切回到我們的APP的時候,還是會停留在你去設定之前的頁面,但是程序號已經變了#→_→#

你需要做的是:

  • 在BaseActivity中檢查必須的許可權,如果是拒絕狀態,直接跳到啟動頁,重新申請。之後就是啟動頁的流程了。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
    ...省略無關程式碼
    
        if (!PermissionUtil.getInstance().checkNecessaryPermissions(this)) {
            PermissionUtil.getInstance().clearFragmentManagerInsideFragments(this);
            finish();
            mIsFinishing = true;
            return;
        }
    
    
        init();
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        PermissionUtil.getInstance().onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    

    }

  • 清理fragment。

    App的某些頁面,如果存在一個或者多個Fragment時,尤其是V4.Fragment,在修改許可權回到該頁面時,如果沒有許可權該頁面會finish掉自己,但是某些手機會出現Fragment 依舊存在,而且生命週期會正常執行,有兩個問題:1.getActivity為空; 2.API請求需要手機資訊許可權,如果後臺拒絕了這個許可權,則會崩掉。所以Activity onCreate時進行許可權判斷,如果沒有則將FragmentManager中的Fragment全部移除,禁止進行一系列的操作。

    /**
    * 清理 FragmentManager 中的 Fragment。<br/>
    * 解決在系統設定中更改許可權後,App 被 kill 掉重啟時的 Fragment 狀態錯誤問題。
    *
    * @param activity
    */
    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public void clearFragmentManagerInsideFragments(Activity activity) {
        if (activity instanceof FragmentActivity) {
            FragmentManager manager = ((FragmentActivity) activity).getSupportFragmentManager();
            int count = manager.getBackStackEntryCount();
            List<Fragment> list = manager.getFragments();
            int fragmentCount = list == null ? 0 : list.size();
            if (list != null) {
            for (Fragment fragment : list) {
                manager.beginTransaction().remove(fragment).commit();
            }
        }
    
    }
    

    }