1. 程式人生 > >Android 6.0 許可權問題解決方法

Android 6.0 許可權問題解決方法

最近在瀏覽 Andorid bus  時無意間看到Andorid 6.0 許可權問題   現在6.0 慢慢普及 相信過不了多久 大家都會做6.0的適配 但是 6.0對許可權尤其是使用者隱私方面做了嚴格的限制   如果還是和以前一樣 直接在配置檔案中新增之後  就以為 萬事大吉  這就錯了    於是為了給更多忙的沒時間的朋友傳播解決方法   就無恥的把前輩的成果借取過來  (主要是自己懶不想在網上論壇帖子裡面找了 )  方便自己以後遇到可以順利解決   

http://www.apkbus.com/blog-705730-60306.html

以上鍊接是原文章  

概述

安卓平臺許可權一直有被流氓應用隨便利用詬病, android M的釋出徹底解決了這一問題,取而代之的是,app不得不在執行時一個一個詢問使用者授予許可權。

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

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

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

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

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

關閉許可權頁

Android M 許可權分類

安卓系統把許可權分為了三類:

  • Normal Permissions

  • Dangerous Permissions

  • Special Permissions

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

Dangerous Permissions-危險許可權

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

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

Dangerous Permission

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.

實戰Android m許可權申請用法

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

相關配置

compileSdkVersion and targetSdkVersion 設定為 23開始

呼叫相關許可權

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);
}

許可權申請相關程式碼

// 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

shouldShowRequestPermissionRationale方法說明

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

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");
    }
}

許可權可以一次申請多個

如圖一次可以申請多個許可權,但是使用者還是一個一個授權.我們對該請求也做了封裝: 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()]);
}

許可權返回處理

在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;
}

特殊許可權的申請

以前特殊許可權說明地方已經支出,該類許可權需求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();
                }
            }
        }
    }