Android版本适配

目录

判断Android版本号是否大于某版本号

Android6.0适配

Android6.0动态申请权限封装

Android7.0适配

Android8.0适配

Android9.0适配

Android10.0适配 

 


判断Android版本号是否大于某版本号

//我的判断的是否大于android 7.0版本         7.0对应的API是24   且对应N
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //这里做你想做的事
        }

 

Android6.0适配

 

Android M对权限管理系统进行了改版,之前我们的App需要权限,只需在manifest中申明即可,用户安装后,一切申明的权限都可来去自如的使用。但是Android M把权限管理做了加强处理,在manifest申明了,在使用到相关功能时,还需重新授权方可使用。当然,不是所有权限都需重新授权,所以就把这些需要重新授权方可使用的权限称之为运行时权限

权限简介

Android出于系统稳定性以及用户隐私方面的考虑,将应用程序访问权限限制在各自的沙盒内。程序可以随意访问所在沙盒内部的资源或者信息,访问沙盒外部的则必须明确的申请相关访问权限。应用程序所需要的权限需要在AndroidManifest.xml文件中申明。如:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"    
        package="com.hjq.permission">

    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />  

    <application ...>    
    ...  
    </application>

</manifest>

系统权限根据敏感程度分为普通权限和危险权限两类。两类权限都需要在AndroidManifest.xml文件中申明。在Android 5.1 (API level 22) 及其以下,系统在APP安装时要求用户授权所有权限,否则APP不能安装;而在Android 6.0及其以上版本上,系统在APP安装时授权所有普通权限,危险权限需要在使用时动态让用户授权。这使得Android的权限管理更加灵活,用户可以根据需要在设置应用中对应用的各个危险权限授予不同的权限。

普通权限

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

危险权限

并不是所有的危险权限都能申请,有某些权限系统是默认禁止的,目前暂时没有任何办法获取

涉及日历,摄像头,联系人,位置,话筒,电话,传感器,短信,存储

//日历
public static final String[] CALENDAR_GROUP = {Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR};
//摄像头
public static final String[] CAMERA_GROUP = {Manifest.permission.CAMERA};
//联系人
public static final String[] CONTACTS_GROUP = {Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS, Manifest.permission.GET_ACCOUNTS};
//位置
public static final String[] LOCATION_GROUP = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
//话筒
public static final String[] MICROPHONE_GROUP = {Manifest.permission.RECORD_AUDIO};
//电话
public static final String[] PHONE_GROUP = {Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE, Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG, Manifest.permission.ADD_VOICEMAIL, Manifest.permission.USE_SIP, Manifest.permission.PROCESS_OUTGOING_CALLS};
//传感器
public static final String[] SENSORS_GROUP = {Manifest.permission.BODY_SENSORS};
//短信
public static final String[] SMS_GROUP = {Manifest.permission.SEND_SMS, Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS, Manifest.permission.RECEIVE_WAP_PUSH, Manifest.permission.RECEIVE_MMS};
//存储
public static final String[] STORAGE_GROUP = {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};

需要注意

Activity要继承不继承AppCompatActivity不重要,只要能找到ActivityCompat类即可,只要添加一条依赖即可,另外项目中的minSdkVersion大于等于API 23(安卓6.0),可以直接用Activity类中的方法,查看源码ActivityCompat得知,最后还是会调用Activity的方法,只不过做了一些判断,避免低版本Activity使用这些方法导致的崩溃

compile 'com.android.support:appcompat-v7:25.3.1'

危险权限在AndroidManifest.xml文件中也必须申明,否则动态申请会失败

权限常量标识

可以用于判断checkSelfPermission方法返回的数据

也可以用于判断onRequestPermissionsResult方法中的grantResults参数

/**
 * 授权了
 */
public static final int PERMISSION_GRANTED = 0;

/**
 * 拒绝了
 */
public static final int PERMISSION_DENIED = -1;

checkSelfPermission

/**
 * 检测某个权限是否授予
 * @param context           Context对象
 * @param permission        需要检测的权限
 */
ContextCompat.checkSelfPermission(Context context, String permission);
//又或者使用子类的方法
ActivityCompat.checkSelfPermission(Context context, String permission);
//minSdkVersion >= 23 可以直接使用
activity.checkSelfPermission(String permission);

检查是否已经具有了相关权限。任何时候APP都要在执行需要危险权限的操作前去检查是否具有相关权限,即使刚刚执行过这项操作,因为用户很有可能去设置应用中关闭了相关权限

举个栗子,如何判断这个权限有没有被授权

if (ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED){
    
}

requestPermissions

/**
 * 申请相关权限
 * @param activity          Activity对象
 * @param permissions       请求的权限组
 * @param requestCode       本次请求码
 */
ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode);
//minSdkVersion >= 23 可以直接使用
activity.requestPermissions(String[] permissions, int requestCode);

申请相关权限。调用这个方法后会弹出一个系统对话框来向用户申请权限,APP不能自定义这个对话框的内容,这也就增加了上面提到的解释说明的必要性。这里还有一点也需要交代一下。从上面危险权限列表中也可以看出,这些权限都是有分组的。如,READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE权限就是属于STORAGE组的。分门别类不仅仅是为了方便容易阅读,组内权限在申请上也是有关联的

  • 在申请组内某个权限时,弹出的系统对话框会显示组名,而不是指明所申请的权限名。如,申请READ_EXTERNAL_STORAGE权限时,系统对话框提示请求“访问sd卡”权限,但不会说明是请求的sd卡读权限

  • 申请权限时,在使用每一条权限时都必须(不是应该)调用requestPermissions()方法来申请权限。如,在已经获取了READ_EXTERNAL_STORAGE权限的情况下,使用WRITE_EXTERNAL_STORAGE权限时依然需要调用requestPermissions()方法来申请,否则就会因为权限问题导致写sd卡失败

经过一定的测试,得到以下结论

  • 第一次安装后请求权限:没有不再询问的选项

  • 被拒绝后再次请求权限,会有不再询问的选项

  • 被拒绝权限且不再询问,后面再请求是不会再弹框

shouldShowRequestPermissionRationale

/**
 * 是否需要向用户解释
 * @param activity          Activity对象
 * @param permission        需要检测的权限
 */
ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission);
//minSdkVersion >= 23 可以直接使用
activity.shouldShowRequestPermissionRationale(String permission);

判断是否需要向用户解释,为什么需要这些权限。有时候用户会不理解应用程序为什么需要这些权限。如,相机应用申请摄像头使用权限用户容易理解,但是相机应用申请地理位置使用权限可能会让用户产生疑惑,因为用户很有能不知道相机需要保存每张照片的拍摄地点。这时候我们就需要做适当的解释说明了。这个方法只有在APP请求过某一权限且用户禁止APP使用该权限的时候返回true。在用户授权了权限和禁止权限时勾选了“Don't ask again”选项的情况下都会返回false。Android官方开发指导还提到一点,为避免给用户带来糟糕的用户体验,这里的解释说明应该是异步的,不要阻塞用户的操作。时下很多适配了6.0的APP在这点上处理的都不尽如人意,有的根本没有解释说明,有的是弹出对话框,用户体验都不是很好

为了帮助查找用户可能需要解释的情形,Android 提供了一个实用程序方法,即 shouldShowRequestPermissionRationale()。如果应用之前请求过此权限但用户拒绝了请求,此方法将返回 true

如果用户在过去拒绝了权限请求,并在权限请求系统对话框中选择了 Don’t ask again 选项,此方法将返回 false。如果设备规范禁止应用具有该权限,此方法也会返回 false

shouldShowRequestPermissionRationale方法的源码

public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity, @NonNull String permission) {
    if (Build.VERSION.SDK_INT >= 23) {
        return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
    }
    return false;
}

下面是不同应用场景调用的结果,已经过一定的测试

  • 之前没有拒绝过此权限的申请(第一次安装后请求权限前调用):false

  • 曾经被拒绝过权限后再调用:true

  • 曾经被拒绝过权限且不再询问后再调用:false

  • 系统不允许任何程序获取该权限:false

  • 查看源码得知安卓6.0以下返回:false

  • 总是允许权限后再次调用:false

由此可以得出一个结论,只有曾经拒绝过才需要向用户解释,这句代码应该在Activity的onRequestPermissionsResult中调用比较合适,调用之前应该需要先判断是否为6.0以上设备

onRequestPermissionsResult

该方法在Activity或Fragment中应该被重写,当用户处理完授权操作时,系统会自动回调该方法

/**
 * Activity处理权限结果回调
 * @param requestCode           权限请求码
 * @param permissions           请求的权限组
 * @param grantResults          请求的结果
 */
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {}

该方法有三个参数,调用requestPermissions请求权限之后的回调

  • int requestCode: 权限请求码,和requestPermissions的同名参数对应

  • String[] permissions: 请求权限组,和requestPermissions的同名参数对应

  • int[] grantResults: 授权结果数组,用于区分上一个参数permissions中的权限有没有被授予,permissions和grantResults两个数组大小是一样的,具体值和上方提到的PackageManager中的两个常量做比较

举个栗子,如何判断请求的这些权限有没有被全部授予

for (int i = 0; i < grantResults.length ; i++) {

    if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
        return false;
    }
}
return true;

如何处理被永久拒绝权限

永久拒绝权限后从授权界面授权再取消授权会恢复到第一次请求的状态,即shouldShowRequestPermissionRationale会返回false,请求的弹窗没有不再询问的选项

加入以下代码引导用户去系统设置界面开启权限

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.fromParts("package", context.getPackageName(), null));
startActivity(intent);

安卓8.0权限适配

在 Android 8.0 之前,如果应用在运行时请求权限并且被授予该权限,系统会错误地将属于同一权限组并且在清单中注册的其他权限也一起授予应用。

对于针对 Android 8.0 的应用,此行为已被纠正。系统只会授予应用明确请求的权限。然而,一旦用户为应用授予某个权限,则所有后续对该权限组中权限的请求都将被自动批准。

例如,假设某个应用在其清单中列出 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE。应用请求 READ_EXTERNAL_STORAGE,并且用户授予了该权限。如果该应用针对的是 API 级别 24 或更低级别,系统还会同时授予 WRITE_EXTERNAL_STORAGE,因为该权限也属于同一 STORAGE 权限组并且也在清单中注册过。如果该应用针对的是 Android 8.0,则系统此时仅会授予 READ_EXTERNAL_STORAGE;不过,如果该应用后来又请求 WRITE_EXTERNAL_STORAGE,则系统会立即授予该权限,而不会提示用户

Android 8.0 之前的版本,同一组的任何一个权限被授权了,组内的其他权限也自动被授权,但是Android 8.0之后的版本,需要更明确指定所使用的权限,并且系统只会授予申请的权限,不会授予没有组内的其他权限,这意味着,如果只申请了外部存储空间读取权限,在低版本下(API < 26)对外部存储空间使用写入操作是没有问题的,但是在高版本(API >= 26)下是会出现问题的,解决方案是需要两个将读和写的权限一起申请

Android6.0动态申请权限封装

这是封装的工具类

import android.app.Activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;

import java.util.ArrayList;
import java.util.List;

/**
 * 6.0权限工具类
 */

public class PermissionsUtils {

    private final int mRequestCode = 100;//权限请求码
    public static boolean showSystemSetting = true;

    private PermissionsUtils() {
    }

    private static PermissionsUtils permissionsUtils;
    private IPermissionsResult mPermissionsResult;

    public static PermissionsUtils getInstance() {
        if (permissionsUtils == null) {
            permissionsUtils = new PermissionsUtils();
        }
        return permissionsUtils;
    }

    public void chekPermissions(Activity context, String[] permissions, @NonNull IPermissionsResult permissionsResult) {
        mPermissionsResult = permissionsResult;

        if (Build.VERSION.SDK_INT < 23) {//6.0才用动态权限
            permissionsResult.passPermissons();
            return;
        }

        //创建一个mPermissionList,逐个判断哪些权限未授予,未授予的权限存储到mPerrrmissionList中
        List<String> mPermissionList = new ArrayList<>();
        //逐个判断你要的权限是否已经通过
        for (int i = 0; i < permissions.length; i++) {
            if (ContextCompat.checkSelfPermission(context, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
                mPermissionList.add(permissions[i]);//添加还未授予的权限
            }
        }

        //申请权限
        if (mPermissionList.size() > 0) {//有权限没有通过,需要申请
            ActivityCompat.requestPermissions(context, permissions, mRequestCode);
        } else {
//说明权限都已经通过,可以做你想做的事情去
            permissionsResult.passPermissons();
            return;
        }


    }

    //请求权限后回调的方法
    //参数: requestCode  是我们自己定义的权限请求码
    //参数: permissions  是我们请求的权限名称数组
    //参数: grantResults 是我们在弹出页面后是否允许权限的标识数组,数组的长度对应的是权限名称数组的长度,数组的数据0表示允许权限,-1表示我们点击了禁止权限

    public void onRequestPermissionsResult(Activity context, int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        boolean hasPermissionDismiss = false;//有权限没有通过
        if (mRequestCode == requestCode) {
            for (int i = 0; i < grantResults.length; i++) {
                if (grantResults[i] == -1) {
                    hasPermissionDismiss = true;
                }
            }
            //如果有权限没有被允许
            if (hasPermissionDismiss) {
                if (showSystemSetting) {
                    showSystemPermissionsSettingDialog(context);//跳转到系统设置权限页面,或者直接关闭页面,不让他继续访问
                } else {
                    mPermissionsResult.forbitPermissons();
                }
            } else {
                //全部权限通过,可以进行下一步操作。。。
                mPermissionsResult.passPermissons();
            }
        }

    }


    /**
     * 不再提示权限时的展示对话框
     */
    AlertDialog mPermissionDialog;

    private void showSystemPermissionsSettingDialog(final Activity context) {
        final String mPackName = context.getPackageName();
        if (mPermissionDialog == null) {
            mPermissionDialog = new AlertDialog.Builder(context)
                    .setMessage("已禁用权限,请手动授予")
                    .setPositiveButton("设置", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            cancelPermissionDialog();

                            Uri packageURI = Uri.parse("package:" + mPackName);
                            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
                            context.startActivity(intent);
                            context.finish();
                        }
                    })
                    .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            //关闭页面或者做其他操作
                            cancelPermissionDialog();
                            //mContext.finish();
                            mPermissionsResult.forbitPermissons();
                        }
                    })
                    .create();
        }
        mPermissionDialog.show();
    }

    //关闭对话框
    private void cancelPermissionDialog() {
        if (mPermissionDialog != null) {
            mPermissionDialog.cancel();
            mPermissionDialog = null;
        }

    }


    public interface IPermissionsResult {
        void passPermissons();

        void forbitPermissons();
    }


}

 那具体的使用方法如下

 //需要的权限
        final String[] permiss = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE};
            //3、
            //权限通过
                PermissionsUtils.getInstance().chekPermissions(MainActivity.this, permiss, new PermissionsUtils.IPermissionsResult() {
                    @Override
                    public void passPermissons() {

                        imageSdv.setImageURI(Uri.parse("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1992085767,102835282&fm=27&gp=0.jpg"));

                    }

                    @Override
                    public void forbitPermissons() {
                        Toast.makeText(MainActivity.this, "抱歉,您未通过权限了", Toast.LENGTH_SHORT).show();
                    }
                });

 重写权限监听方法

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

这样动态申请权限是不是就很简单了

Android7.0适配

1.安装时解析错误

2.应用间共享文件

可以参考Android 7.0脱坑指南,非常的全面

Android8.0适配

1.运行时权限修复授权整组bug

2.通知适配

3.不允许安装外部APK,需要开启权限​​​​​​​

4.透明主题的Activity不能旋转屏幕

可以参考Android 8.0适配指北,非常的全面

Android9.0适配

1.Http请求失败

2.前台服务​​​​​​​

3.从非activity地方启动Activity失败

可以参考Android 9.0 适配指南,非常的全面

Android10.0适配

1.Scoped Storage(分区存储)

2.深色主题​​​​​​​

可以参考Android 10.0 适配攻略,非常的全面

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android是一个开源的移动操作系统,由Google开发。它的各个版本都有不同的特性和适配要求。下面是Android各个版本适配情况: 1. Android 1.0:这是Android的首个正式版本,发布于2008年。它主要适配了当时的早期智能手机。 2. Android 1.5 Cupcake:这个版本引入了许多新特性,包括虚拟键盘、文本选择和复制功能等。它的适配要求相对较低,可以在较旧的设备上运行。 3. Android 1.6 Donut:这个版本增加了搜索框和快捷方式等功能。它的适配要求与Cupcake相似。 4. Android 2.0/2.1 Eclair:这个版本引入了许多新特性,包括支持多点触控、HTML5视频播放和Live壁纸等。它的适配要求相对较高,需要较新的设备支持。 5. Android 2.2 Froyo:这个版本引入了许多新特性,包括支持移动热点和Adobe Flash等。它的适配要求与Eclair相似。 6. Android 2.3 Gingerbread:这个版本引入了许多新特性,包括支持NFC和下载管理器等。它的适配要求相对较高,需要较新的设备支持。 7. Android 4.0 Ice Cream Sandwich:这个版本引入了许多新特性,包括全新的用户界面和面部解锁等。它的适配要求相对较高,需要较新的设备支持。 8. Android 4.1/4.2/4.3 Jelly Bean:这个版本引入了许多新特性,包括Google Now和通知增强等。它的适配要求与Ice Cream Sandwich相似。 9. Android 4.4 KitKat:这个版本引入了许多新特性,包括透明状态栏和打印支持等。它的适配要求相对较高,需要较新的设备支持。 10. Android 5.0/5.1 Lollipop:这个版本引入了许多新特性,包括Material Design和多用户支持等。它的适配要求相对较高,需要较新的设备支持。 11. Android 6.0 Marshmallow:这个版本引入了许多新特性,包括指纹识别和运行时权限等。它的适配要求相对较高,需要较新的设备支持。 12. Android 7.0/7.1 Nougat:这个版本引入了许多新特性,包括分屏模式和通知增强等。它的适配要求相对较高,需要较新的设备支持。 13. Android 8.0/8.1 Oreo:这个版本引入了许多新特性,包括自适应图标和通知渠道等。它的适配要求相对较高,需要较新的设备支持。 14. Android 9 Pie:这个版本引入了许多新特性,包括手势导航和应用程序切片等。它的适配要求相对较高,需要较新的设备支持。 15. Android 10:这个版本引入了许多新特性,包括暗黑模式和系统级录屏等。它的适配要求相对较高,需要较新的设备支持。 16. Android 11:这个版本引入了许多新特性,包括聊天气泡和无线Android Auto等。它的适配要求相对较高,需要较新的设备支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值