Android m6.0權限問題調用封裝utils類 - Permission in Android M

Android m6.0權限問題調用封裝utils類 - Permission in Android M


1.概述

安卓平臺權限一直有被流氓應用隨便利用詬病, android M的發佈徹底解決了這一問題,取而代之的是,app不得不在運行時一個一個詢問用戶授予權限。

Android 6.0(api23)系統中,做了一些限制, 開發者在使用到每條權限時必須自己調用相關代碼請求.

如果沒有獲得某項權限,直接使用相關功能,則會導致自己程序crash.
見log

可見6.0以後的系統開發者必須對權限適配,否則軟件隨時都可能奔潰,那麼問題來了~

已經發出去的版本或是targetSdkVersion小與23的apk怎麼辦?
廢話,當然會崩了!!!

只要在滿足在Android M上直接使用爲授權的功能,程序必須Crash. targetSdkVersion<23的應用在安裝時系統會默認全部授權應用在manifest中申請的權限,
不要應用這樣你的應用就完事大全了.用戶可以在以下頁面或是其他應用關閉相關權限,然後…你的應用就沒有然後了~
關閉權限頁

2. Android M 權限分類

安卓系統把權限分爲了三類:

  • Normal Permissions

  • Dangerous Permissions

  • Special Permissions

a. Normal Permissions-一般權限

一般權限都是一些系統認爲比較權限的權限,流氓應用就是擁有這些權限也幹不出多大壞事,Normal 權限會在應用安裝是直接授權,
官網解釋:權限如下:

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
FLASHLIGHT
GET_PACKAGE_SIZE
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
SET_ALARM
INSTALL_SHORTCUT
UNINSTALL_SHORTCUT

b. Dangerous Permissions-危險權限

這些權限都是一些敏感性權限,一些廣告平臺或是流氓應用會用這些權限幹一些壞壞的事情,因此係統將這類權限分了幾個類別,
應用每次都要檢測下是否有權限,沒有的化必須彈出對話框申請,只要一個組別中的一個權限得到了授權,整個組的權限都會的到授權.

這部分權限也是我們重點在M系統上關注和適配的部分.
官網權威說明, 具體相關權限見圖:

Dangerous Permission

c. Special Permissions- 特殊權限

SYSTEM_ALERT_WINDOW and WRITE_SETTINGS, 這兩個權限比較特殊,不能通過代碼申請方式獲取,必須得用戶打開軟件設置頁手動打開,才能授權.

There are a couple of permissions that don’t behave like normal and dangerous permissions. SYSTEM_ALERT_WINDOW and WRITE_SETTINGS are particularly sensitive, so most apps should not use them. If an app needs one of these permissions, it must declare the permission in the manifest, and send an intent requesting the user’s authorization. The system responds to the intent by showing a detailed management screen to the user.

特殊權限官網推薦用法

3. 實戰Android m權限申請用法

我們對相關申請方法封裝成了工具類,方便m系統適配隨時調用.

a. 相關配置

compileSdkVersion and targetSdkVersion 設置爲 23開始

b. 調用相關權限

private void testAlertPermission() {
    WindowManager mWindowManager = (WindowManager) getSystemService(
            Context.WINDOW_SERVICE);
    WindowManager.LayoutParams params = new WindowManager.LayoutParams();
    params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
    mWindowManager.addView(new TextView(this), params);
}

c. 權限申請相關代碼

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
               Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

   // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
           Manifest.permission.READ_CONTACTS)) {

    // Show an expanation to the user *asynchronously* -- don't block
    // this thread waiting for the user's response! After the user
    // sees the explanation, try again to request the permission.

     } else {

    // No explanation needed, we can request the permission.

    ActivityCompat.requestPermissions(thisActivity,
            new String[]{Manifest.permission.READ_CONTACTS},
            MY_PERMISSIONS_REQUEST_READ_CONTACTS);

    // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
    // app-defined int constant. The callback method gets the
    // result of the request.
      }
}

requestPermissions方法調用時會彈出以下對話框.當用戶點擊拒絕並且勾選了不再彈出後這個對話框將不會再彈出,會直接拒絕掉該權限:
requestPermissions

1.shouldShowRequestPermissionRationale方法說明

用戶拒絕,或是不在彈出,這個方法會返回false.
返回說明

2.Activity和Fragment的申請方法不一樣,所以我們對方法做了包裝如下:

@TargetApi(Build.VERSION_CODES.M)
public static boolean checkPermission(Object cxt, String permission, int requestCode) {
    if (!checkSelfPermissionWrapper(cxt, permission)) {
        if (!shouldShowRequestPermissionRationaleWrapper(cxt, permission)) {
            requestPermissionsWrapper(cxt, new String[]{permission}, requestCode);
        } else {
            Log.d(TAG, "should show rational");
        }
        return false;
    }
    return true;
}

private static void requestPermissionsWrapper(Object cxt, String[] permission, int requestCode) {
    if (cxt instanceof Activity) {
        Activity activity = (Activity) cxt;
        ActivityCompat.requestPermissions(activity, permission, requestCode);
    } else if (cxt instanceof Fragment) {
        Fragment fragment = (Fragment) cxt;
        fragment.requestPermissions(permission, requestCode);
    } else {
        throw new RuntimeException("cxt is net a activity or fragment");
    }
}

3. 權限可以一次申請多個

如圖一次可以申請多個權限,但是用戶還是一個一個授權.我們對該請求也做了封裝:
multi multi

@TargetApi(23)
private static boolean checkSelfPermissionWrapper(Object cxt, String permission) {
    if (cxt instanceof Activity) {
        Activity activity = (Activity) cxt;
        return ActivityCompat.checkSelfPermission(activity,
                permission) == PackageManager.PERMISSION_GRANTED;
    } else if (cxt instanceof Fragment) {
        Fragment fragment = (Fragment) cxt;
        return fragment.getActivity().checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED;
    } else {
        throw new RuntimeException("cxt is net a activity or fragment");
    }
}

private static String[] checkSelfPermissionArray(Object cxt, String[] permission) {
    ArrayList<String> permiList = new ArrayList<>();
    for (String p : permission) {
        if (!checkSelfPermissionWrapper(cxt, p)) {
            permiList.add(p);
        }
    }

    return permiList.toArray(new String[permiList.size()]);
}

d. 權限返回處理

在activity或fragment 中重寫onRequestPermissionsResult,用戶處理相關權限後會回調該方法,當活取到相關應用後可以繼續原來的邏輯.

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case PermissionUtils.PERMISSION_REQUEST_CODE:
            if (PermissionUtils.verifyPermissions(grantResults)) {
                // Permission Granted
                // do you action
            } else {
                // Permission Denied
                Toast.makeText(this, "WRITE_CONTACTS Denied", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

public static boolean verifyPermissions(int[] grantResults) {
    // At least one result must be checked.
    if (grantResults.length < 1) {
        return false;
    }

    // Verify that each required permission has been granted, otherwise return false.
    for (int result : grantResults) {
        if (result != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

4. 特殊權限的申請

以前特殊權限說明地方已經支出,該類權限需求intent到具體的設置頁面,讓用戶手動打開,才能授權.
同樣重寫onActivityResult方法,返回該頁面時做回調處理.

sp

*系統彈出權限,相關代碼實例:

/**
 * 檢測系統彈出權限
 * @param cxt
 * @param req
 * @return
 */
@TargetApi(23)
public static boolean checkSettingAlertPermission(Object cxt, int req) {
    if (cxt instanceof Activity) {
        Activity activity = (Activity) cxt;
        if (!Settings.canDrawOverlays(activity.getBaseContext())) {
            Log.i(TAG, "Setting not permission");

            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + activity.getPackageName()));
            activity.startActivityForResult(intent, req);
            return false;
        }
    } else if (cxt instanceof Fragment) {
        Fragment fragment = (Fragment) cxt;
        if (!Settings.canDrawOverlays(fragment.getActivity())) {
            Log.i(TAG, "Setting not permission");

            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + fragment.getActivity().getPackageName()));
            fragment.startActivityForResult(intent, req);
            return false;
        }
    } else {
        throw new RuntimeException("cxt is net a activity or fragment");
    }

    return true;
}

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PermissionUtils.PERMISSION_SETTING_REQ_CODE) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (Settings.canDrawOverlays(this)) {
                    // do something
                } else {
                    Toast.makeText(this, "not has setting permission", Toast.LENGTH_LONG).show();
                    finish();
                }
            }
        }
    }

*系統設置權限代碼

    /**
     * WRITE_SETTINGS 權限
     * @param cxt
     * @param req
     * @return
     */
    @TargetApi(23)
    public static boolean checkSettingSystemPermission(Object cxt, int req) {
        if (cxt instanceof Activity) {
            Activity activity = (Activity) cxt;
            if (!Settings.System.canWrite(activity)) {
                Log.i(TAG, "Setting not permission");

                Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
                intent.setData(Uri.parse("package:" + activity.getPackageName()));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                activity.startActivityForResult(intent, req);
                return false;
            }
        } else if (cxt instanceof Fragment) {
            Fragment fragment = (Fragment) cxt;
            if (!Settings.System.canWrite(fragment.getContext())) {
                Log.i(TAG, "Setting not permission");

                Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
                intent.setData(Uri.parse("package:" + fragment.getContext().getPackageName()));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                fragment.startActivityForResult(intent, req);
                return false;
            }
        } else {
            throw new RuntimeException("cxt is net a activity or fragment");
        }

        return true;
    }

結語

Android 6.0系統權限管理是安卓系統的一大進步,爲安卓手機用戶提供了一個安全乾淨系統前提,鑑於google對未授權應用的奔潰方式處理,
安卓開發者應當儘早適配6.0系統,提示軟件體驗.

相關權限檢測及申請代碼已封裝到了utils類中,源代碼已提交到GitHub, 歡迎下載交流學習~

License

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