1. 程式人生 > >Android 6.0動態許可權申請

Android 6.0動態許可權申請

1.Android6.0動態許可權的由來
一直以來,Android的許可權系統都是最大的安全問題之一,因為在進行安裝的時候,所有的許可權都會統一進行請求,你必須允許這些許可權請求才能進行安裝.應用安裝後,就可以在使用者毫不知情的情況下,訪問這些許可權.所以有很多應用利用這一漏洞,偷偷地進行蒐集使用者個人資訊或者其他進行其他用途,可以自行腦補一下.
Android開發團隊顯然也意識到這個問題了,於是重新設計了許可權系統.在最新的Android6.0Marshmallow中,安裝應用的時候不再需要允許任何許可權的請求了.相反,應用程式必須在執行的時候獲取應用的許可權許可.注意:這樣使用者就會意識到應用正在獲取許可權,如果是敏感許可權,就會引起使用者的注意,而不是之前的情況,使用者對應用獲取敏感許可權毫無察覺.

動態許可權的彈出和許可權管理如下圖所示

開啟設定介面找到要管理的應用許可權
我這個應用需要位置和儲存兩個動態許可權,在這裡可以取消掉.取消掉再次開啟也應用如下圖所示:
彈出的動態許可權選擇框

2.Android6.0動態許可權給開發者帶來的問題?
新的動態許可權可能會引起你的恐慌,”我3年前的應用怎麼辦,如果把它安裝在Android6.0的機器上,它是否正常執行,還是會崩潰?”

不必擔心,Android開發團隊已經考慮過這個問題了,如果應用的targetSDKVersion小於23,那麼它就會被假定為並未通過動態許可權系統的測試,它依然會使用之前的許可權系統:在安裝的時候向用戶請求所有需要的許可權,使用者必須接受這些許可權請求才能進行安裝.

結果就是應用依然會像之前那樣執行良好,但是請注意,此時使用者依然可以在安裝之後撤銷賦予應用的許可權,雖然系統會在使用者這麼做的時候發出警告.

下一刻,你可能擔心自己的應用在使用者撤銷許可權之後發生崩潰了.

Android開發團隊給我們送了一個福利,在應用的targetSdkVersion小於23,使用者撤銷了某項許可權之後,我們呼叫需要該項許可權的方法後,不會丟擲任何異常.呼叫的方法什麼也不會做,而返回值則是看情況返回null或者0.

不要高興的太早,儘管呼叫方法不會產生異常,但是你很可能在處理返回值的時候發生問題.(注:這個時候可以體現出對返回值檢查判斷的重要性了).

目前來說,由於新版本的普及問題,發生這種事的機率很小.使用者撤銷許可權後,可能就會意識到將會產生問題,就像系統警告的那樣.但是將來肯定會發生很多使用者撤銷許可權許可的問題,我們必須對應用進行適配以便在新的手機上進行使用.

在你完成對Android6.0動態許可權的適配之前,切勿將應targetSdkVersion設為23,否則將會使你陷入到適配問題中去.

注意在你使用AndroidStudio建立新工程的時候,它會自動地將工程的targetSdkVersion設為最新的版本,因此在你的應用支援動態許可權之前,建議你修改一下targetSdkVersion.

3.獲取動態許可權的核心程式碼.

public class WelcomeActivity extends AppCompatActivity {

    public static final int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1000;
    public static final int PERMISSIONS_REQUEST_LOCATION = 2000;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //判斷targetSDK的版本是否小於23,大於等於23時才會執行該類中的邏輯判斷,也就是檢查手機是不是Android6.0以上的系統
        //先價差是否獲取了儲存許可權
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            //請求儲存和位置許可權
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION},
                    PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
        } else {
            //檢查是否獲取了位置許可權
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
                //申請位置許可權
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        PERMISSIONS_REQUEST_LOCATION);
            }

            if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                    == PackageManager.PERMISSION_GRANTED) {
                startActivity();
            }
        }
    }
    //請求許可權的回撥函式
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //根據請求碼做不同的邏輯處理
        if (requestCode == PERMISSIONS_REQUEST_LOCATION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_DENIED) {
                new AlertDialog.Builder(WelcomeActivity.this)
                        .setTitle("警告!")
                        .setMessage("缺少許可權,不能定位")
                        .setPositiveButton("OK", null)
                        .create().show();
            }
            startActivity();
        }


        if (requestCode == PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startActivity();
            } else {
                new AlertDialog.Builder(WelcomeActivity.this)
                        .setTitle("警告!")
                        .setMessage("缺少許可權,程式即將關閉,請手動開啟許可權。")
                        .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialogInterface, int i) {
//                                finishAll();
                            }
                        }).create().show();
            }
        }
    }

    private void startActivity() {
        Intent intent = new Intent(this,MainActivity.class);
        startActivity(intent);
        finish();
    }
}

程式碼片中有詳細的註釋,在此我就不多做闡述了!

4.預設許可許可權和動態許可權.

預設許可許可權就是指安裝的時候預設許可並且不可撤銷的,我們把它們叫做普通許可權,,這些許可權如下:

android.permission.ACCESS_LOCATION_EXTRA_COMMANDS

android.permission.ACCESS_NETWORK_STATE

android.permission.ACCESS_NOTIFICATION_POLICY

android.permission.ACCESS_WIFI_STATE

android.permission.ACCESS_WIMAX_STATE

android.permission.BLUETOOTH

android.permission.BLUETOOTH_ADMIN

android.permission.BROADCAST_STICKY

android.permission.CHANGE_NETWORK_STATE

android.permission.CHANGE_WIFI_MULTICAST_STATE

android.permission.CHANGE_WIFI_STATE

android.permission.CHANGE_WIMAX_STATE

android.permission.DISABLE_KEYGUARD

android.permission.EXPAND_STATUS_BAR

android.permission.FLASHLIGHT

android.permission.GET_ACCOUNTS

android.permission.GET_PACKAGE_SIZE

android.permission.INTERNET

android.permission.KILL_BACKGROUND_PROCESSES

android.permission.MODIFY_AUDIO_SETTINGS

android.permission.NFC

android.permission.READ_SYNC_SETTINGS

android.permission.READ_SYNC_STATS

android.permission.RECEIVE_BOOT_COMPLETED

android.permission.REORDER_TASKS

android.permission.REQUEST_INSTALL_PACKAGES

android.permission.SET_TIME_ZONE

android.permission.SET_WALLPAPER

android.permission.SET_WALLPAPER_HINTS

android.permission.SUBSCRIBED_FEEDS_READ

android.permission.TRANSMIT_IR

android.permission.USE_FINGERPRINT

android.permission.VIBRATE

android.permission.WAKE_LOCK

android.permission.WRITE_SYNC_SETTINGS

com.android.alarm.permission.SET_ALARM

com.android.launcher.permission.INSTALL_SHORTCUT

com.android.launcher.permission.UNINSTALL_SHORTCUT

這些許可權和以前一樣使用,你無需檢查它們是否被撤銷了

需要申請的動態許可權如下圖所示:
動態許可權

當然不止上面一種實現方法,github上有許多大神開源的封裝庫,可以很方便的實現許可權適配。我推薦兩個庫,大家根據需求選擇:

不足之處請指出,謝謝!