Android6.0权限系统

vue在调用摄像头扫码(vue-qrcode-reader) 在浏览器端html中有一款依赖可以直接调用摄像头扫码,。分别提供vue-qrcode-reader用来识别二维码。 阅读详情

Android6.0权限系统

Android权限系统是一个非常重要的安全问题,因为它只有在安装时会询问一次。一旦软件本安装之后,应用程序可以在用户毫不知情的情况下使用这些权限来获取所有的内容。

很多坏蛋会通过这个安全缺陷来收集用户的个人信息并使用它们来做坏事的情况就不足为奇了。

Android团队也意识到了这个问题。在经过了7年后,权限系统终于被重新设置了。从Anroid 6.0(API Level 23)开始,应用程序在安装时不会被授予任何权限,取而代之的是在运行时应用回去请求用户授予对应的权限。这样可以让用户能更好的去控制应用功能。例如,一个用户可能会同一个拍照应用使用摄像头的权限,但是不同授权它获取设备位置的权限。用户可以在任何时候通过去应用的设置页面来撤销授权。

image

注意:上面请求权限的对话框不会自动弹出。开发者需要手动的调用。如果开发者调用了一些需要权限的功能,但是用户又拒绝授权的话,应用就会crash。

系统权限被分为两类,normal和dangerous:

  • Normal Permissions不需要用户直接授权,如果你的应用在清单文件中声明了Normal Permissions`,系统会自动授权该权限。
  • Dangerous Permissions可以让应用获取用户的私人数据。如果你的应用在清单文件中申请了Dangerous Permissions,那就必须要用户来授权给应用。

Normal Permissions:
Normal Permission是当用户安装或更新应用时,系统将授予应用所请求的权限,又称为PROTECTION_NORMAL(安装时授权的一类基本权限)。该类权限只需要在manifest文件中声明即可,安装时就授权,不需要每次使用时进行检查,而且用户不能取消以上授权。

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

下图为Dangerous permissions and permission groups:

image

在所有的Android版本中,你的应用都需要在manifest文件中声明normal和dangerous权限。然而声明所影响的效果会因系统版本和你应用的target SDK lever有关:

  • 如果设备运行的是Android 5.1或者之前的系统,或者应用的targetSdkVersion是22或者之前版本:如果你在manifest中声明了dangerous permission,用户需要在安装应用时授权这些权限。如果用户不授权这些权限,系统就不会安装该应用。(我试了下发现即使targetSdkVersion是22及以下,在6.0的手机上时,如果你安装时你不同意一些权限,也仍然可以安装的)
  • 如果设备运行的是Android 6.0或者更高的系统,并且你应用的targetSdkVersion是23或者更高:应用必须在manifest文件中申请这些权限,而且必须要在运行时对所需要的dangerous permission申请授权。用户可以统一或者拒绝授权,并且及时用户拒绝了授权,应用在无法使用一些功能的情况下也要保证能继续运行。

也就是说新的运行时权限仅当我们设置targetSdkVersion是23及以上时才会起作用。
如果你的targtSdkVersion低于23,那将被认为该应用没有经过android 6.0的测试,当该应用被安装到了6.0的手机上时,仍然会使用之前的旧权限规则,在安装时会提示所有需要的权限(这样做是有道理的,不然对于之前开发的应用,我们都要立马去修改让它适应6.0,来不及的话就导致6.0的手机都无法使用了,显然Android开发团队不会考虑不到这种情况),当然用户可以在安装的界面不允许一些权限,那当程序使用到了这些权限时,会崩溃吗?答案是在Android 6.0及以上的手机会直接crash,但是在23之前的手机上不会crash。

所以如果你的应用没有支持运行时权限的功能,那千万不要讲targetSdkVersion设置为23,否则就麻烦了。

注意:从Android 6.0(API Level 23)开始,即使应用的targetSdkVersion是比较低的版本,但是用户仍然可以在任何时候撤销对应用的授权。所以不管应用的targetSdkVerison是什么版本,你都要测试你的应用在不能获取权限时能不能正常运行。

下面介绍下如何使用Android Support Library来检查和请求权限。Android框架在6.0开始也提供了相同的方法。然而使用support包会比较简单,因为这样你就不需要在请求方法时判断当前的系统版本。(后面说的这几个类都是android.support.v4中的)

检查权限

如果应用需要使用dangerous permission,在任何时候执行需要该权限的操作时你都需要检查是否已经授权。用户可能会经常取消授权,所以即使昨天应用已经使用了摄像头,这也不能保证今天仍然有使用摄像头的权限。

为了检查是否可以使用该权限,调用ContextCompat.checkSelfPermission()。
例如:

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

如果应用有该权限,该方法将返回PackageManager.PERMISSION_GRANTED,应用可以进行相关的操作。如果应用不能使用该权限,该方法将返回PERMISSION_DENIED,这是应用将必须要向用户申请该权限。

申请使用权限

如果应用需要使用清单文件中申明的dangerous permission,它必须要向用户申请来授权。Android提供了几种申请授权的方法。使用这些方法时将会弹出一个标准的系统对话框,该对话框不能自定义。

说明为什么应用需要使用这些权限

在一些情况下,你可能需要帮助用力理解为什么需要该权限。例如,一个用户使用了一个照相的应用,用户不会奇怪为什么应用申请使用摄像头的权限,但是用户可能会不理解为什么应用需要获取位置或者联系人的权限。在请求一个权限之前,你需要该用户一个说明。一定要切记不要通过说明来压倒用户。如果你提供了太多的说明,用户可能会感觉沮丧并且会卸载它。

一个你需要提供说明的合适时机就是在用户之前已经不同意授权该权限的情况下。如果一个用户继续尝试使用需要权限的功能时,但是之前确禁止了该权限的请求,这就可能是因为用户不理解为什么该功能需要使用该权限。在这种情况下,提供一个说明是非常合适的。

为了能找到用户可能需要说明的情况,android提供了一个工具类方法ActivityCompat.shouldShowRequestPermissionRationale().。如果应用之前申请了该权限但是用户拒绝授权后该方法会返回true。(在Android 6.0之前调用的时候会直接返回false)

注意:如果用户之前拒绝了权限申请并且选择了请求权限对话框中的Don’t ask again选项,该方法就会返回false。如果设备策略禁止了该应用使用该权限,该方法也会返回false。(我测试的时候发现请求权限的对话框中并没有Don’t asdk again这一项)
image

申请需要的权限

如果应用没有所需的权限时,应用必须调用ActivityCompat.requestPermissions (Activity activity,
String[] permissions,
int requestCode)
方法来申请对用的权限。参数传递对应所需的权限以及一个整数型的request code来标记该权限申请。 该方法是异步的:该方法会立即返回,在用户响应了请求权限的对话框之后,系统会调用对用的回调方法来通知结果,并且会传递在reqeustPermissions()方法中的request code。(在Android 6.0之前调用的时候会直接去调用onRequestPermissionsResult()的回调方法)
如图:
image

下面是检查是否读取联系人权限,并且在必要时申请权限的代码:

// 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()之前处理。

处理请求权限的的结果

如果应用申请权限,系统会显示一个对话框。当用户相应后,系统会调用应用中的onRequestPermissionsResult (int requestCode,
String[] permissions,
int[] grantResults)
方法并传递用户的操作结果。在应用中必须要重写该方法来查找授权了什么权限。该回调方法会传递你在requestPermisssions()方法中传递的request code。直接在Activity或者Fragment中重写onRequestPermissionsResult()方法即可。例如,申请READ_CONTACTS的权限可能会有下面的回到方法:

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

系统提示的对话框会描述应用所需的permission groud。它不会列出特定的权限。例如,如果你申请了READ_CONTACTS权限,系统的对话框只会说你的应用需要获取设备的联系人信息。用户只需要授权每个permission group一次。如果你应用需要申请其他任何一个在该permission group中的权限时,系统会自动授权。在申请这些授权时,系统会像用户明确通过系统对话框统一授权时一样去调用onRequestPermissionsResult()方法并且传递PERMISSION_GRANTED参数。

注意:虽然用户已经授权了同一permission group中其他的任何权限,但是应用仍然需要明确申请每个需要的权限。例外,permission group中的权限在以后可能会发生变化。

例如,假设在应用的manifest文件中同时声明了READ_CONTACTS和WRITE_CONTACTS权限。如果你申请READ_CONTACTS权限而且用户同意了该权限,如果你想继续申请WRITE_CONTACTS权限,系统不会与用户有任何交互就会直接进行授权。

如果用户拒绝了一个权限申请,你的应用进行合适的处理。例如,你的应用可能显示一个对话框来表明无法执行用户请求的需要该权限的操作。

如果系统向用户申请权限授权,用户选择了让系统以后不要再申请该权限。 在这种情况下,应用在任何时间调用reqeustPermissions()方法来再次申请权限时,系统都会直接拒绝该请求。系统会直接调用onRequestPermissionResult()回调方法并且传递PERMISSION_DENIED参数,和用户明确拒绝应用申请该权限时一样。 这就意味着在你调用requestPermissions()方法是,你无法确定是否会和用户有直接的交互操作。

示例代码:


final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;

private void insertDummyContactWrapper() {
    List<String> permissionsNeeded = new ArrayList<String>();

    final List<String> permissionsList = new ArrayList<String>();
    if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION))
        permissionsNeeded.add("GPS");
    if (!addPermission(permissionsList, Manifest.permission.READ_CONTACTS))
        permissionsNeeded.add("Read Contacts");
    if (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS))
        permissionsNeeded.add("Write Contacts");

    if (permissionsList.size() > 0) {
        if (permissionsNeeded.size() > 0) {
            // Need Rationale
            String message = "You need to grant access to " + permissionsNeeded.get(0);
            for (int i = 1; i < permissionsNeeded.size(); i++)
                message = message + ", " + permissionsNeeded.get(i);
            showMessageOKCancel(message,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                                    REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
                        }
                    });
            return;
        }
        requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
                REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
        return;
    }

    insertDummyContact();
}

private boolean addPermission(List<String> permissionsList, String permission) {
    if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
        permissionsList.add(permission);
        // Check for Rationale Option
        if (!shouldShowRequestPermissionRationale(permission))
            return false;
    }
    return true;
}

上面讲到的都是Activity中的使用方法,那Fragment中怎么授权呢?
如果在Fragment中使用,用v13包中的FragmentCompat.requestPermissions()和FragmentCompat.shouldShowRequestPermissionRationale()。

在Fragment中申请权限,不要使用ActivityCompat.requestPermissions, 直接使用Fragment.requestPermissions方法,
否则会回调到Activity的onRequestPermissionsResult。但是虽然你使用Fragment.requestPermissions方法,也照样回调不到Fragment.onRequestPermissionsResult中。这是Android的Bug,详见,Google已经在23.3.0修复了该问题,所以要尽快升级。

所以升级到23.3.0及以上就没问题了。如果不升级该怎么处理呢?就是在Activity.onRequestPermissionsResult方法中去手动调用每个Fragment的方法(当然你要判断下权限个数,不然申请一个权限的情况下会重复调用).

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    if (fragments != null) {
        for (Fragment fragment : fragments) {
            fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

我简单的写了一个工具类:

public class PermissionUtil {
    /**
     * 在调用需要权限的功能时使用该方法进行检查。
     *
     * @param activity
     * @param requestCode
     * @param iPermission
     * @param permissions
     */
    public static void checkPermissions(Activity activity, int requestCode, IPermission iPermission, String... permissions) {
        handleRequestPermissions(activity, requestCode, iPermission, permissions);
    }

    public static void checkPermissions(Fragment fragment, int requestCode, IPermission iPermission, String... permissions) {
        handleRequestPermissions(fragment, requestCode, iPermission, permissions);
    }

    public static void checkPermissions(android.app.Fragment fragment, int requestCode, IPermission iPermission, String... permissions) {
        handleRequestPermissions(fragment, requestCode, iPermission, permissions);
    }

    /**
     * 在Actvitiy或者Fragment中重写onRequestPermissionsResult方法后调用该方法。
     *
     * @param activity
     * @param requestCode
     * @param permissions
     * @param grantResults
     * @param iPermission
     */
    public static void onRequestPermissionsResult(Activity activity, int requestCode, String[] permissions,
                                                  int[] grantResults, IPermission iPermission) {
        requestResult(activity, requestCode, permissions, grantResults, iPermission);

    }

    public static void onRequestPermissionsResult(Fragment fragment, int requestCode, String[] permissions,
                                                  int[] grantResults, IPermission iPermission) {
        requestResult(fragment, requestCode, permissions, grantResults, iPermission);
    }

    public static void onRequestPermissionsResult(android.app.Fragment fragment, int requestCode, String[] permissions,
                                                  int[] grantResults, IPermission iPermission) {
        requestResult(fragment, requestCode, permissions, grantResults, iPermission);
    }

    public static <T> void requestPermission(T t, int requestCode, String... permission) {
        List<String> permissions = new ArrayList<>();
        for (String s : permission) {
            permissions.add(s);
        }
        requestPermissions(t, requestCode, permissions);
    }

    /**
     * 在检查权限后自己处理权限说明的逻辑后调用该方法,直接申请权限。
     *
     * @param t
     * @param requestCode
     * @param permissions
     * @param <T>
     */
    public static <T> void requestPermission(T t, int requestCode, List<String> permissions) {
        if (permissions == null || permissions.size() == 0) {
            return;
        }
        requestPermissions(t, requestCode, permissions);
    }

    public static boolean checkSelfPermission(Context context, String permission) {
        if (context == null || TextUtils.isEmpty(permission)) {
            throw new IllegalArgumentException("invalidate params: the params is null !");
        }

        context = context.getApplicationContext();

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            int result = ContextCompat.checkSelfPermission(context, permission);
            if (PackageManager.PERMISSION_DENIED == result) {
                return false;
            }
        }

        return true;
    }

    private static <T> void handleRequestPermissions(T t, int requestCode, IPermission iPermission, String... permissions) {
        if (t == null || permissions == null || permissions.length == 0) {
            throw new IllegalArgumentException("invalidate params");
        }
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            Activity activity = getActivity(t);
            List<String> deniedPermissions = getDeniedPermissions(activity, permissions);
            if (deniedPermissions != null && deniedPermissions.size() > 0) {

                List<String> rationalPermissions = new ArrayList<>();
                for (String deniedPermission : deniedPermissions) {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
                            deniedPermission)) {
                        rationalPermissions.add(deniedPermission);
                    }
                }

                boolean showRational = false;
                if (iPermission != null) {
                    showRational = iPermission.showRational(requestCode);
                }

                if (rationalPermissions.size() > 0 && showRational) {
                    if (iPermission != null) {
                        iPermission.onRational(requestCode, deniedPermissions);
                    }
                } else {
                    requestPermissions(t, requestCode, deniedPermissions);
                }
            } else {
                if (iPermission != null) {
                    iPermission.onGranted(requestCode);
                }
            }
        } else {
            if (iPermission != null) {
                iPermission.onGranted(requestCode);
            }
        }
    }

    @Nullable
    private static <T> Activity getActivity(T t) {
        Activity activity = null;
        if (t instanceof Activity) {
            activity = (Activity) t;
        } else if (t instanceof Fragment) {
            activity = ((Fragment) t).getActivity();
        } else if (t instanceof android.app.Fragment) {
            activity = ((android.app.Fragment) t).getActivity();
        }
        return activity;
    }

    @TargetApi(Build.VERSION_CODES.M)
    private static <T> void requestPermissions(T t, int requestCode, List<String> deniedPermissions) {
        if (deniedPermissions == null || deniedPermissions.size() == 0) {
            return;
        }
        // has denied permissions
        if (t instanceof Activity) {
            ((Activity) t).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        } else if (t instanceof Fragment) {
            ((Fragment) t).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        } else if (t instanceof android.app.Fragment) {
            ((android.app.Fragment) t).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);
        }
    }

    private static List<String> getDeniedPermissions(Context context, String... permissions) {
        if (context == null || permissions == null || permissions.length == 0) {
            return null;
        }
        List<String> denyPermissions = new ArrayList<>();
        for (String permission : permissions) {
            if (!checkSelfPermission(context, permission)) {
                denyPermissions.add(permission);
            }
        }
        return denyPermissions;
    }


    private static <T> void requestResult(T t, int requestCode, String[] permissions,
                                          int[] grantResults, IPermission iPermission) {
        List<String> deniedPermissions = new ArrayList<>();
        for (int i = 0; i < grantResults.length; i++) {
            if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
                deniedPermissions.add(permissions[i]);
            }
        }
        if (deniedPermissions.size() > 0) {
            if (iPermission != null) {
                iPermission.onDenied(requestCode);
            }
        } else {
            if (iPermission != null) {
                iPermission.onGranted(requestCode);
            }
        }
    }

}

interface IPermission {
    void onGranted(int requestCode);

    void onDenied(int requestCode);

    void onRational(int requestCode, List<String> permissions);

    /**
     * 是否需要提示用户该权限的作用,提示后需要再调用requestPermission()方法来申请。
     *
     * @return true 为提示,false为不提示
     */
    boolean showRational(int requestCode);
}

使用方法:

public class MainFragment extends Fragment implements View.OnClickListener {
    private Button mReqCameraBt;
    private Button mReqContactsBt;
    private Button mReqMoreBt;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main, container, false);
        findView(view);
        initView();
        return view;
    }

    private void findView(View view) {
        mReqCameraBt = (Button) view.findViewById(R.id.bt_requestCamera);
        mReqContactsBt = (Button) view.findViewById(R.id.bt_requestContacts);
        mReqMoreBt = (Button) view.findViewById(R.id.bt_requestMore);
    }

    private void initView() {
        mReqCameraBt.setOnClickListener(this);
        mReqContactsBt.setOnClickListener(this);
        mReqMoreBt.setOnClickListener(this);
    }


    public static final int REQUEST_CODE_CAMERA = 0;
    public static final int REQUEST_CODE_CONTACTS = 1;
    public static final int REQUEST_CODE_MORE = 2;

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

    public void requestCamera() {
        PermissionUtil.checkPermissions(this, REQUEST_CODE_CAMERA, mPermission, Manifest.permission.CAMERA);
    }

    public void requestReadContacts() {
        PermissionUtil.checkPermissions(this, REQUEST_CODE_CONTACTS, mPermission, Manifest.permission.READ_CONTACTS);
    }

    public void requestMore() {
        PermissionUtil.checkPermissions(this, REQUEST_CODE_MORE, mPermission, Manifest.permission.READ_CONTACTS, Manifest.permission.READ_CALENDAR, Manifest.permission.CALL_PHONE);
    }

    private void showPermissionTipDialog(final int requestCode, final List<String> permissions) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage("I want you permissions");
        builder.setTitle("Hello Permission");
        builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                PermissionUtil.requestPermission(MainFragment.this, requestCode, permissions);
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });
        builder.create().show();
    }

    public IPermission mPermission = new IPermission() {
        @Override
        public void onGranted(int requestCode) {
            Toast.makeText(getActivity(), "onGranted :" + requestCode, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDenied(int requestCode) {
            Toast.makeText(getActivity(), "onDenied :" + requestCode, Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onRational(int requestCode, List<String> permission) {
            showPermissionTipDialog(requestCode, permission);
        }

        @Override
        public boolean showRational(int requestCode) {
            switch (requestCode) {
                case REQUEST_CODE_MORE:
                    return true;

                default:
                    break;
            }
            return false;
        }
    };

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.bt_requestCamera:
                requestCamera();
                break;
            case R.id.bt_requestContacts:
                requestReadContacts();
                break;
            case R.id.bt_requestMore:
                requestMore();
                break;
        }
    }
}

参考:

更多文章请参考AndroidNote,欢迎Start

Android 10.0 系统设置开启始终在后台运行的权限 android 6.0系统中保活机制,所以在系统内存不够的时候,后台运行的app有可能会被系统杀掉 所以为了让app不能系统杀掉保持永久运行 就必须要增加权限,把app 添加到保活白名单里面 或者授予后台运行的权限 接下来看Settings的源码 REQUEST_IGNORE_BATTERY_OPTIMIZATIONS 权限就是忽略被电池优化的权限 在普通app里面也可以申请这个权限 申请完这个权限后就不会被优化 可以一直在后台运行 在Settings的源码中 RequestIgnoreBatteryOpt 阅读详情

相关推荐

【硬件】通俗易懂的讲解晶体管(三极管和MOS管)的工作原理

晶体管是一个简单的元器件,可用于构建许多有趣的项目。在本文中,我将用通俗易懂的语言给您讲解晶体管的工作原理,以便您可以在电路设计中更好的使用静态管。一旦你学习这些基础知识,对以后的设计和使用来说,将会变得非常容易。我们将重点介绍两种最常见的晶体管:双极型晶体管(三极管)和MOSFET。晶体管的工作原理其实是类似于电子开关。它可以打开和关闭电路。一个简单的思考方法是将晶体管视为无源的继电器。晶体管类似于继电器,从某种意义上说,您可以使用它来打开和关闭某些东西。但晶体管也可以部分导通,一般在放大电路中使用,这部

1万+

android M 及以上版本将应用添加至忽略电池优化

/** * 忽略电池优化 */ private void ignoreBatteryOptimization(Activity activity) { if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.M){ PowerManager powerManager = (Po...

snow_lyGirl 7784

单片机通信总述——理论部分(CAN、串口、SPI、I2C等)

并行通信:传输原理:数据各个位同时传输;优点:速度快; 缺点:占用引脚资源多。是指使用 8、16、32 及 64 根或更多的数据线(有多少信号为就需要多少信号位)进行传输的通讯方式,可以同一时刻传输多个数据位的数据。串行通信: 传输原理:数据按位顺序传输;优点:占用引脚资源少; 缺点:速度相对较慢。是指设备之间通过一根数据信号线,地线以及控制信号线,按数据位形式一位一位地传输数据的通讯方式,同一时刻只能传输一位(bit)数据。 单工: 数据传输只支持数据在一个方向上传输。信息只能单方向传输的工作方式,一

qq_42475191的博客 1万+

安卓忽略省电优化

android 忽略电量优化

qq_39495120的博客 1811

程序设计二(面向对象)_实训10_复杂类的实现_ArrayList类的实现

第1关:构造函数与析构函数的实现

2401_83406306的博客 822

pwscf与wannier90 Hands-On实战训练(一)——费米面计算为例

系列文章目录 Quantum Espresso Hands-On实战训练(三)——DOS计算 文章目录系列文章目录计算过程pwscf直接得到等能面pwscf和wannier90得到等能面pwscf直接得到等能面1. scf2. nscf3. 后处理pwscf和wannier90得到等能面1. Kpoints2. scf3. nscf4. pw2wan及wannier90.x5. XCrySDen可视化 计算过程 pwscf直接得到等能面 pwscf和wannier90得到等能面 pwscf直接得到等能面

weixin_45973682的博客 5073

【Android】【后台运行-1】请求电池优化

先上代码 AndroidManifest.xml中申请权限 <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> Activity中实现: override fun onCreate(savedInstanceState: Bundle?) { ...... if (!isIgnoringBatteryOptimizations()) { re

凡人的专栏 5109

uniapp [全端兼容] - 详细实现引入使用 Google 谷歌地图显示及搜索城市地点,提供地图标点、地址搜索、定位导航、中文显示及汉化、地图打位置标点显示、搜索框搜索功能、省市区详细经纬度数据等

uniapp,h5移动端网页,微信小程序,Android,苹果ios,app,nvue,谷歌地图,官方,谷歌官方文档,googlemap,申请key,uniApp谷歌地图,详细使用教程,无法访问,集成谷歌地图,app引入谷歌地图,选点定位,地图导航,位置搜索,城市搜索,文字地点模糊查询,地点省市区详细数据,经纬度信息,markers地图标记点,一直报错怎么办,接入google地图不显示报错,谷歌地图插件,vue3,vue2,组件,Key申请流程,谷歌key怎么申请,api文档,网站打不开,用什么插件,在u

🏆 前端领域优质创作者 1万+

Android基础——适配安卓6.0新权限系统

安卓6.0新权限系统分类 在安卓6.0版本以后,新的权限系统出现了,为了更好的保护用户的安全,新的权限系统需要开发者在代码中手动申请,所以为了适配6.0权限系统,我们不得不学习权限系统 安卓6.0新权限系统分类有两种 普通权限(normal):这个权限类型并不直接威胁到用户的隐私,可以直接在manifest清单里注册,系统会帮我们默认授权的 危险权限 (dangerous):这个可以直接给...

点击置顶文章查看博客目录(全站式导航) 5093

在SAP系统中,公司间关联交易(Intercompany Transactions)或内部往来主要有三种经典的实现模式。这些模式的选择取决于企业的业务复杂度、自动化需求以及对实时性的要求

SAP系统中公司间关联交易的三种实现模式分析:1)手工记账模式(FB60/FB70)适用于低频交易,灵活性高但易出错;2)SD/MM跨公司销售采购模式(VA01/VF01/ME21N)适用于真实业务流转,业务财务集成度高;3)自动清算模式(F.13/ICR)适合高频交易集团企业,支持实时自动对账。关键成功要素包括正确配置贸易伙伴字段、统驭科目及专用凭证类型。建议根据企业规模、业务复杂度及自动化需求选择合适模式,其中大型集团优先考虑自动对账方案以确保合并报表准确性。

金牌架构师 561

Android实现TextToSpeech控件实现语音播报--朗读文字 (附完整源码)

Android实现TextToSpeech控件实现语音播报--朗读文字 (附完整源码)

希望我的博客,能帮上你解决学习中工作中所遇到的问题 1121

Android 6.0前后的权限系统介绍、使用、建议

Android 6.0前后的权限系统介绍、使用、建议前言(以后前言部分属于我家领导的!)今日金句:如果爱请深爱~核心内容为什么需要权限,权限的能做什么,怎么做Android权限系统分类Android6.0之前的系统权限介绍Android6.0之后的系统权限介绍Android6.0之后系统权限直接与间接使用Android6.0之后系统权限使用建议具体描述为什么需要权限,权限的能做什么,怎么做权限是A

DennisJu的专栏 1389

【AUTOSAR-COM】-10.5-APP CAN Frame/PDU“发送周期不准“的影响因素

一个Tx PDU在COM模块中的数据流; Tx APP CAN Frame/PDU"发送周期不准"的影响因素;

「汽车电子助手」的博客 2390

探讨Android 6.0及以上新权限系统的检测与处理

从Google官方文档可知,Android系统升级到6.0后,它的权限系统被重新设计。相比原来新安装的APP系统会一次性授予所有权限和用户无法管理APP权限的不足,新的权限系统不再允许新安装的APP一次性获得所有权限,APP必须在运行时一个一个地询问用户授予权限,甚至有时候都不会主动申请用户授权,开发者不得不自己去检测和请求用户授予来获得权限。那么当我们的APP部署到Android 6.0以上系统的终端时,某些功能没有获得相关权限或者用户拒绝授予权限又会出现什么问题呢?

老蒋也疯狂 1万+

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Android6.0权限系统以及工具类的实现

Android6.0权限系统 Android权限系统是一个非常重要的安全问题,因为它只有在安装时会询问一次。一旦软件本安装之后,应用程序可以在用户毫不知情的情况下使用这些权限来获取所有的内容。 很多坏蛋会通过这个安全缺陷来收集用户的个人信息并使用它们来做坏事的情况就不足为奇了。 Android团队也意识到了这个问题。在经过了7年后,权限系统终于被重新设置了。从Anroid 6

栀夏暖阳的博客 529

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Android M 新的运行时权限开发者需要知道的一切 (安卓6.0新权限系统)

来源:http://www.mamicode.com/info-detail-1015135.html 标签:android   开发人员    本文来自微凉一季的博客http://jijiaxin89.com/2015/08/30/Android-s-Runtime-Permission/ 更多文章点击跳转微凉一季 tags: 翻译,这是一篇译文。 andr

HAPP NEW JAVA 2070

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Android 6.0 权限系统

转载请标明出处:  http://blog.csdn.net/lmj623565791/article/details/50709663;  本文出自:【张鸿洋的博客】 一、概述 随着Android 6.0发布以及普及,我们开发者所要应对的主要就是新版本SDK带来的一些变化,首先关注的就是权限机制的变化。对于6.0的几个主要的变化,查看查看官网的这篇文章http://devel

qq_25875475的博客 337

Android6.0的权限系统

Android6.0棉花糖,app将不会在安装的时候授予权限。取而代之,app不得不在运行的时候一个一个询问用户授予权限。 只有在把 targetSdkVersion 23  以上的版本才会出现。 PROTECTION_NORMAL类权限 用户安装或者更新应用是,系统将授予应用所请求的PROTECTION_NORMAL的所有权限(安装时授予的一类基本权限)。这类权限包

san 878

android 6.0权限系统

1:android 6.0(版本号23)以后,权限系统发生了变化,有些权限决定给了用户,需要用户同意后才能获取此权限 2:Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等...

luoj_616的专栏 232

android 6.0权限系统总结和封装

在运行时请求权限从 Android 6.0(API 级别 23)开始,用户开始在应用运行时向其授予权限,而不是在应用安装时授予。此方法可以简化应用安装过程,因为用户在安装或更新应用时不需要授予权限。它还让用户可以对应用的功能进行更多控制;例如,用户可以选择为相机应用提供相机访问权限,而不提供设备位置的访问权限。用户可以随时进入应用的“Settings”屏幕调用权限。系统权限分为两类:正常权限和危险权

qq402164452的博客 671

Android 6.0新权限系统权限管理

一、在Android6.0后,Android的权限系统进行了全新的升级。 新的权限系统再我们设置targetSDKVersion=23及以上时生效,如果我们设置的是小于23的,则仍然使用之前的 权限系统。 新版的权限申请仍然有俩种: 第一种:和23之前的权限系统相同,仍然是在AndroidManifest.xml清单文件中进行申请这些权限分别是 android.permission.AC

qq_36071384的博客 465

Android 6.0权限适配

Android 6.0权限适配

u012400555的博客 498

组件化封装思想实战Android App总结十三项目适配Android 6.0权限系统

一、适配Android 6.0 1、为什么要适配6.0呢? 因为Android 6.0推出了新的权限机制。在以前需要申请Android权限的时候,只需要在mainfest中申请所需要的权限即可,在安装的时候,默认会授予我们相应的权限。但是在6.0以后,权限不仅仅需要在mainfest中进行注册,而且还需要在用到的时候,动态地向用户进行申请。如果用户授予我们对应的权限了,才可以去走正常的逻辑,如...

nicolelili1的专栏 275
上一篇: Instant Run详解
下一篇: 注解使用
CharonChui
博客等级 码龄12年 66粉丝 134原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值