1、存储权限(分区存储)
Android手机分为外部储存和内部储存
内部储存:/data 目录。一般我们使用getFilesDir() 或 getCacheDir() 方法获取本应用的内部储存路径,读写该路径下的文件不需要申请储存空间读写权限,且卸载应用时会自动删除。
外部储存:/storage 或 /mnt 目录。一般我们使用getExternalStorageDirectory()方法获取的路径来存取文件。
因为不同厂商、系统版本的原因,所以上述的方法并没有一个固定的文件路径。那我们所说的外部储存访问限制是针对getExternalStorageDirectory()路径下的文件
android Q 将外部存储分为
(1)特定目录(App-specific),使用getExternalFilesDir()或 getExternalCacheDir()方法访问。无需权限,且卸载应用时会自动删除。
(2)照片、视频、音频这类媒体文件。使用MediaStore 访问,访问其他应用的媒体文件时需要READ_EXTERNAL_STORAGE权限。
(3)其他目录,使用存储访问框架SAF(Storage Access Framwork)

适配分为两种方式
1、最简单粗暴的方法就是在AndroidManifest.xml中添加 android:requestLegacyExternalStorage="true"来请求使用旧的存储模式
Android Q Beta 3之前都是强制的,但为了给开发者适配的时间才没有强制执行
2、Environment.getExternalStorageDirectory()方法,换成getExternalFilesDir()方法(包括下载的安装包这类的文件)。如果是缓存类型文件,可以放到getExternalCacheDir()路径下。
或者使用MediaStore,将文件存至对应的媒体类型中(图片:MediaStore.Images ,视频:MediaStore.Video,音频:MediaStore.Audio),不过仅限于多媒体文件。
注意:如果应用通过升级安装,那么还会使用以前的储存模式(Legacy View)。只有通过首次安装或是卸载重新安装才能启用新模式(Filtered View)。
所以在适配时,我们的判断代码如下:

// 使用Environment.isExternalStorageLegacy()来检查APP的运行模式if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && !Environment.isExternalStorageLegacy()) {}

这样的好处是你可以在用户升级后,能方便的将用户的数据移动至应用的特定目录。否则你只能通过SAF去移动,这样会非常麻烦。如果你要移动数据注意只适用于Android 10下,所以现在适配反而是一个好时机。当然如果你不需要迁移数据,那适配会更省事。
2、定位权限
为了让用户更好地控制应用对位置信息的访问权限,Android Q 引入了新的位置权限 ACCESS_BACKGROUND_LOCATION。
与现有的 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 权限不同,新权限仅会影响应用在后台运行时对位置信息的访问权。除非应用的某个 Activity 可见或应用正在运行前台服务,否则应用将被视为在后台运行。
谷歌会按照应用的targetSDK作出不同处理:
targetSDK <= P 应用如果请求了ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION权限,Q设备会自动帮你申请ACCESS_BACKGROUND_LOCATION权限。

// 位置权限
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ? new PermissionMeta(2, Manifest.permission.ACCESS_BACKGROUND_LOCATION,"位置权限说明","使用此权限只用来获得您的定位给您更好的服务。") : new PermissionMeta(2, Manifest.permission.ACCESS_FINE_LOCATION,"位置权限说明","使用此权限只用来获得您的定位给您更好的服务。")

3、设备唯一标识符
在Android10中, 系统不允许普通App请求android.permission.READ_PHONE_STATE权限, 故新版App需要取消该动态权限的申请。
设备 ID
在Q设备上通过((TelephonyManager)getActivity().getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId()
获得设备ID,会返回空值(targetSDK<=P)或者报错(targetSDK==Q)。且官方所说的READ_PRIVILEGED_PHONE_STATE权限只提供给系统app,
所以当前获取设备唯一ID的方式为使用SSAID, 若获取为空的话则使用UUID.randomUUID().toString()获得一个随机ID并存储起来, 该ID保证唯一, 但App卸载重装之后就会改变

String androidId = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {String deviceId = android.provider.Settings.Secure.getString(context.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID)
}else {String deviceId = ((TelephonyManager)getActivity().getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId()
}

具体实现:
SystemInfoUtils.getDeviceId(context)已处理,所以保持不变即可,但是如果项目中其他地方或者SDK调用getDeviceId()需要处理,可根据上面代码进行适配,ANDROID_ID替换DeviceId即可
IMEI 等设备信息
从 Android10 开始普通应用不再允许请求权限 android.permission.READ_PHONE_STATE。而且,无论你的 App 是否适配过 Android Q(既 targetSdkVersion 是否大于等于 29),均无法再获取到设备 IMEI 等设备信息。
受影响的 API

  • Build.getSerial();
  • TelephonyManager.getImei();
  • TelephonyManager.getMeid();
  • TelephonyManager.getDeviceId();
  • TelephonyManager.getSubscriberId();
  • TelephonyManager.getSimSerialNumber();
    如果您的 App 希望在 Android 10 以下的设备中仍然获取设备 IMEI 等信息,可按以下方式进行适配:
 <uses-permission
-         android:name="android.permission.READ_PHONE_STATE"
-         android:maxSdkVersion="28" />

4、minSDK警告
在 Android Q 中,当用户首次运行以 Android 6.0(API 级别 23)以下的版本为目标平台的任何应用时,Android平台会向用户发出警告。如果此应用要求用户授予权限,则系统会先向用户提供调整应用权限的机会,然后才会允许此应用首次运行。
谷歌要求运行在Q设备上的应用targetSDK>=23,不然会向用户发出警告。
5、EditText默认不获取焦点,不自动弹出键盘
该问题出现在 targetSdkVersion >= Build.VERSION_CODES.P 情况下,且设备版本为Android P以上版本,解决方法在onCreate中加入如下代码,可获得焦点,如需要弹出键盘可延迟一下:

mEditText.post(() -> {mEditText.requestFocus();mEditText.setFocusable(true);mEditText.setFocusableInTouchMode(true);
});

6、安装APK Intent及其它共享文件相关Intent
/**

  • 自Android N开始,是通过FileProvider共享相关文件,但是Android Q对公有目录 File API进行*了限制,只能通过Uri来操作,
  • 从代码上看,又变得和以前低版本一样了,只是必须加上权限代码Intent.FLAG_GRANT_READ_URI_PERMISSION
    */
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){//适配Android Q,注意mFilePath是通过ContentResolver得到的,上述有相关代码Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(Uri.parse(mFilePath) ,"application/vnd.android.package-archive");intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);startActivity(intent);}

7、Region.Op相关异常:
java.lang.IllegalArgumentException: Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed
当 targetSdkVersion >= Build.VERSION_CODES.P 时调用 canvas.clipPath(path, http://Region.Op.XXX); 引起的异常,参考源码如下:

@Deprecated
public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {checkValidClipOp(op);return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
}private static void checkValidClipOp(@NonNull Region.Op op) {if (sCompatiblityVersion >= Build.VERSION_CODES.P&& op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {throw new IllegalArgumentException("Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");}
}

我们可以看到当目标版本从Android P开始,Canvas.clipPath(@NonNull Path path, @NonNull Region.Op op) ; 已经被废弃,而且是包含异常风险的废弃API,只有 Region.Op.INTERSECT 和 Region.Op.DIFFERENCE 得到兼容
具体实现:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {canvas.clipPath(path);
} else {canvas.clipPath(path, Region.Op.XOR);// REPLACE、UNION 等
}

8、相册选择图片,通过URI获取Bitmap

具体实现:
选择相册,通过Uri获取Bitmap

if (resultCode == RESULT_OK) {Uri uri = data.getData();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {bitmap = BitmapUtils.getBitmapFromUri(getActivity(), uri);} else {bitmap = BitmapUtils.decodeBitmapFromPath(BaiTiaoBitmapUtils.getRealFilePath(getActivity(), uri));}updateCameraPic(bitmap, null);
}// android 10 通过uri加载图片
public static Bitmap getBitmapFromUri(Context context, Uri uri) {try {ParcelFileDescriptor parcelFileDescriptor =context.getContentResolver().openFileDescriptor(uri, "r");FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor);parcelFileDescriptor.close();return image;} catch (Exception e) {e.printStackTrace();}return null;
}public static Bitmap decodeBitmapFromPath(String path) {BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = 2;Bitmap bitmap = BitmapFactory.decodeFile(path, bitmapOptions);ByteArrayOutputStream baos = new ByteArrayOutputStream();int options = 50;bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);while (baos.toByteArray().length / 1024 > 300) {// 循环判断如果压缩后图片是否大于300kb继续压缩baos.reset();options -= 5;if (options < 6) {//为了防止图片大小一直达不到200kb,options一直在递减,当options<0时,下面的方法会报错// 也就是说即使达不到200kb,也就压缩到10了bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);break;}
// 这里压缩options%,把压缩后的数据存放到baos中bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);}LogUtils.e("===图片压缩后显示大小===" + baos.toByteArray().length / 1024 + " kb");return BitmapFactory.decodeByteArray(baos.toByteArray(), 0, baos.toByteArray().length);}public static String getRealFilePath(final Context context, final Uri uri) {if (null == uri) return null;final String scheme = uri.getScheme();String data = null;if (scheme == null)data = uri.getPath();else if (ContentResolver.SCHEME_FILE.equals(scheme)) {data = uri.getPath();} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);if (null != cursor) {if (cursor.moveToFirst()) {int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);if (index > -1) {data = cursor.getString(index);}}cursor.close();}}return data;
}

Android 10.0(Q api=29)适配相关推荐

  1. 怎么更新android 10.0,Android 10.0(Q OS)系统升级计划Androi

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Android 10.0(Q OS)系统升级计划 Android 10.0 系统升级计划: 系列 型号 升级计划 Galaxy S10 SM-G9730 ...

  2. android10 三星升级计划,Android 10.0(Q OS)系统升级计划Androi

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Android 10.0(Q OS)系统升级计划 Android 10.0 系统升级计划: 系列 型号 升级计划 Galaxy S10 SM-G9730 ...

  3. 升级android10机型,Android 10.0(Q OS)系统升级计划Androi

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Android 10.0(Q OS)系统升级计划 Android 10.0 系统升级计划: 系列 型号 升级计划 Galaxy S10 SM-G9730 ...

  4. android版本怎么升级10,Android 10.0(Q OS)系统升级计划Androi

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 Android 10.0(Q OS)系统升级计划 Android 10.0 系统升级计划: 系列 型号 升级计划 Galaxy S10 SM-G9730 ...

  5. 小老弟!听说你在搞Android 10.0 适配,看这篇就妥了!

    点击上方"何俊林",马上关注,每天早上8:50准时推送 真爱,请置顶或星标 转载自公众号:刘望舒,作者: 吃猫猫的鱼 地址:https://juejin.im/post/5cad5 ...

  6. Android应用安装apk版本升级,适配Android 8.0和Android 10.0下载安装,shell命令安装APK

    shell命令安装 /*** 安装apk** @param path apk文件路径*/ public void installAPK(String path) {Log.i(TAG, "i ...

  7. Android 10.0 行为变更(一)针对所有 API 级别的应用

    非 SDK 接口限制 官方明确指出:目标是在限制使用非 SDK 接口之前确保有可用的公开替代方案. 反射或JNI必须要有替代方案! 如果您不打算以 Android Q 为目标平台,那么其中一些变更可能 ...

  8. android pie 官方壁纸,Android 10.0 内置壁纸提取,带你提前进入Q时代

    原标题:Android 10.0 内置壁纸提取,带你提前进入Q时代 3月14日早晨谷歌推出了期待已久的Android Q的首个测试版本,这是Android系统推出以来的第十个大版本.在这个手机行业经过 ...

  9. 还在期待安卓9.0吗?Android 10.0要来了

    目前,美国 Google公司的 AndroidP (安卓9.0),已经正式全面推出有几个多月了.众多手机品牌厂商也都在积极的进行更新适配 Android 9.0 系统(修改UI界面也算是二次开发,嗯) ...

最新文章

  1. YOLO v1到YOLO v4(下)
  2. KNN学习之图像分类与KNN原理
  3. 03-MySQL多表操作
  4. 如何用点云对车辆和行人进行识别分类?这是MIT学生的总结
  5. docker安装pocbox(漏洞测试验证辅助平台)
  6. ABP理论学习之Web API控制器(新增)
  7. 轻松掌握IP子网划分的概念和操作方法
  8. 【C语言】输入一个正整数,判断其是否为素数
  9. devops开发运维一体化_进阶 | 中国电信系统集成公司:100% 应用 DevOps 理念,做好企业级行业应用...
  10. VS Code 1.37 发布,多个图标迎来全新设计
  11. 前端开源项目周报0214
  12. 用MVC做可拖拽的留言板,利用 Jquery模板 -- JsRender
  13. Javascript 调用MSAgent(调用office助手显示动画)
  14. three.js 文本_使用Three-bmfont-text在Three.js中创建文本
  15. matlab编写多目标测试函数SCH, ZDT, MOP, DTLZ
  16. 谈谈令你印象深刻的技术问题
  17. React 调用手机,电脑摄像头扫描识别二维码
  18. Flink 如何分流数据
  19. [北力电子] 无人机4G图传数传一体 pixhawk mavlink GSLINK 720P
  20. 02 C语言使用队列实现缓存模块QueueBuffer

热门文章

  1. vivo分屏_科技怎样应用在生活中?vivo分屏+Jovi语音助手=高效学习体验
  2. 为何选择机器人工程专业?
  3. compare和comparaTo的区别
  4. 消除恐惧回应指责 谷歌再次深入解读引发争议的AI打电话
  5. w ndows7安装包如何安装到电脑上,微软win7专业版如何安装
  6. 001_雅思写作之wonder women单词总结
  7. blueJ连接mysql_快速建立Servlet和JSP的运行、调试和编译环境_MySQL
  8. 86-交换机与VLAN:办公室太复杂,我要回学校
  9. 背单词系统Python
  10. 怎么样下载浓情中秋月饼宣传产品介绍PPT模板