android 11 [api 30] 适配指南

转眼之间就该适配Android 11,记得距离上次写 Android 10 适配指南才过去不久. 行为变更:所有应用 这些变化,影响所有的应用,不管你的应用的targetSdkVersion是多少,只要是运行在Android 11 上都会影响,应该格外的关注: 数据访问审核 为了让应用及其依赖项访问用户私密数据的过程更加透明,Android 11 引入了数据访问审核功能。借助此流程得出的见解,您可以更好地识别和纠正可能出现的意外数据访问。 您的应用可以注册 AppOpsManager.OnOpNotedCallback 实例,该实例可在每次发生以下任一事件时执行相应操作: 应用的代码访问私密数据。为了帮助您确定应用的哪个逻辑部分调用了事件,您可以按归因标记审核数据访问。 依赖库或 SDK 中的代码访问私密数据。 数据访问审核是在发生数据请求的线程上调用的。这意味着,如果应用中的第三方 SDK 或库调用访问私密数据的 API,您的 OnOpNotedCallback 可以调用数据访问审核检查有关该调用的信息。通常,此回调对象可以通过查看应用的当前状态(例如当前线程的堆栈轨迹)以判断调用是来自您的应用还是来自 SDK。 单次授权 在 Android 11 中,每当应用请求与位置信息、麦克风或摄像头相关的权限时,面向用户的权限对话框会包含仅限这一次选项。如果用户在对话框中选择此选项,系统会向应用授予临时的单次授权。 在申请与位置信息、麦克风或摄像头相关的权限时,系统会自动提供一个单次授权的选项,只供这一次权限获取。然后用户下次打开app的时候,系统会再次提示用户授予权限。这个影响应该不大,只要我们每次使用的时候都去判断权限,没有就去申请即可。放一张新版本权限获取样式: JobScheduler API 调用限制调试 JobScheduler任务调度器,可以在设备空闲时做一些任务处理。Android11中如果你设置为debug模式(debuggable 清单属性设置为 true),超出速率限制的JobScheduler API调用将返回 RESULT_FAILURE。这个有什么用呢?应该可以帮助我们发现一些性能问题,感兴趣的可以自己试试。 顺便提下,Jetpack组件WorkManager也是用到了JobScheduler,不熟悉的同学可以去了解下,JobScheduler是由SystemServer进程启动的一个系统服务,所以才可以有这么大的权限。 应用使用情况统计信息 为了更好地保护用户,Android 11 将每个用户的应用使用情况统计信息存储在凭据加密存储空间中。 UsageStatsManager是Android提供统计应用使用情况的服务。通过这个服务可以获取指定时间区间内应用使用统计数据、组件状态变化事件统计数据以及硬件配置信息统计数据。 比如queryAndAggregateUsageStats方法,可以获取指定时间区间内使用统计数据,以应用包名为键值进行数据合并。 但是在Android 11 设备中,系统和任何应用都无法访问该数据,除非 isUserUnlocked() 返回 true,这发生在出现以下某种情况之后: 用户在系统启动后首次解锁其设备。 用户在设备上切换到自己的帐号。 无障碍操作 在Android手机上有个预安装的屏幕阅读服务,叫做TalkBack,为视力障碍人士或者视力状态不佳的老年人提供。那我们应用为了让这个阅读器能够读懂你的自定义view操作,必须给与自定义控件定义处理程序,包括点击,长按等操作。原来版本可能对于OnTouchListener也支持无障碍触摸事件,而在Android 11中,必须专门制定点击或者长按事件才行了 class TriSwitch(context: Context) : Switch(context) { // 0, 1, or 2....

九月 15, 2020 · 4 分钟 · LengYue

Android 声明式 UI 框架 Litho 初探 —— Sections API

在最开始入门介绍中,我们曾经用SingleComponentSection完成了一个简单的列表,当时的做法是使用 for 构造出了多个子Component。其实在 Litho 中提供了一个性能更好的方式,专门处理这种数据(这种数据其实就是类似于Android 中的adapter 与其绑定的数据)。 Litho中专门处理这种模板与列表支持的组件叫做 DataDiffSection的使用 DataDiffSection。下面用DataDiffSection我们重构一下之前写的MainListViewSpec。 首先生成我们的数据: val data = arrayListOf<Int>() for (i in 0 until 32) { data.add(i) } 在MainListViewSpec中增加一个创建ListItemView组件的方法: @OnEvent(RenderEvent::class) fun onRender(c: SectionContext, @FromEvent model: Int): RenderInfo { return ComponentRenderInfo.create().component( ListItemView.create(c) .color(if (model % 2 == 0) Color.WHITE else Color.LTGRAY) .title(if (model % 2 == 0) "hello word" else model.toString()) ....

九月 9, 2020 · 1 分钟 · LengYue

Android 声明式 UI 框架 Litho 初探 —— Layout

Litho底层使用的是Yoga,Yoga是Facebook的另一个开源项目,它是一个跨iOS、Android、Windows平台在内的布局引擎,兼容Flexbox布局方式。 所以只要熟悉Flexbox布局,那么在使用Litho进行UI布局时基本毫无压力。 如果熟悉Flutter开发,那在使用Litho时,会有一些似曾相识的感觉,Litho中的 Row 与 Column 相关属性与Flutter中的 Row 与 Column 几乎无二。 本来想写一点示例代码,但是感觉没什么可写的。下面这个链接是Yoga 官网的playground。 https://yogalayout.com/playground 你可以通过它可视化的调整UI,构建你需要的layout。同时可以生成相应的Litho代码 在线可视化构建UI: 直接生成的Litho代码: 在 Flexbox 中可以通过 positionType(ABSOLUTE)属性来实现Android 中的 FrameLayout效果: @OnCreateLayout fun createLayout(c: ComponentContext): Component { return Column.create(c) .child( SolidColor.create(c) .color(Color.MAGENTA) .widthDip(100f) .heightDip(100f) ) .child( Text.create(c) .text("FrameLayout") .marginDip(YogaEdge.TOP, 30f) .positionType(YogaPositionType.ABSOLUTE) ) .build(); } 运行效果:

九月 6, 2020 · 1 分钟 · LengYue

Android声明式UI框架 Litho 初探 —— MountSpec的使用

Mount Specs Mount Specs 用来生成渲染具体View或者Drawable的组件。 Mount spec 必须使用@MountSpec注解来标注,并至少实现一个标注了@onCreateMountContent的方法。 Mount Spec相比于Layout Spec更复杂一些,它拥有自己的生命周期: @OnPrepare,准备阶段,进行一些初始化操作。 @OnMeasure,负责布局的计算。 @OnBoundsDefined,在布局计算完成后挂载视图前做一些操作。 @OnCreateMountContent,创建需要挂载的视图。 @OnMount,挂载视图,完成布局相关的设置。 @OnBind,绑定视图,完成数据和视图的绑定。 @OnUnBind,解绑视图,主要用于重置视图的数据相关的属性,防止出现复用问题。 @OnUnmount,卸载视图,主要用于重置视图的布局相关的属性,防止出现复用问题 Android 小伙伴应该对上面这几个状态比较熟悉 这里引用一张美团技术博客的图: 下面这个代码,只是一个单纯的ColorDrawable,你也可以替换成你需要实习的View 例如 ImageView: /** * 挂载操作有一个非常类似于Android的RecyclerView Adapter的API。 * 它有一个 onCreateMountContent 方法,用于在回收池为空时创建和初始化 View 和 Drawable 内容 onMount 使用当前信息对复用的内容进行更新。 * * 预分配 * 当挂载 MountSpec 组件时,其 View 或 Drawable 内容需要从回收池中初始化或重用。 * 如果池为空,那么将创建一个新实例,这可能会使UI线程过于繁忙并丢弃一个或多个帧。为了缓解这种情况,Litho 可以预先分配一些实例并放入回收池中。 * */ @MountSpec(poolSize = 0, canPreallocate = true, isPureRender = true) class MainColorViewSpec { private const val TAG = "MainColorViewSpec" // onCreateMountContent 的返回类型应该始终与 onMount 的第二个参数的类型匹配。它们必须是 View 或 Drawable 子类。参数在构建时进行校验。 // onCreateMountContent 不能接收 @Prop 或任何带有其他注解的参数。 @OnCreateMountContent fun onCreateMountContent(context: Context): ColorDrawable { Log....

九月 5, 2020 · 2 分钟 · LengYue

Android声明式UI框架 Litho 初探 ——两种数据类型

Litho中包含的的两种数据类型 Litho的两种属性分别是: 不可变属性称为Props 可变属性称为State 不可变属性Props 定义和使用props Props属性:Component中使用@Prop注解的参数集合,具有单向性和不可变性,可以在左右的方法中访问它的指。在同一个Component中我们可以定义和访问相同的prop 下面这个例子,定义了两个Prop,一个string类型text,一个int类型index,text的注解中optional = true表示它是一个可选参数。 当Component的生命周期方法被调用的时候,@Prop参数会保存component创建时从它们的父级传递过来的值(或者它们的默认值) 设置props prop参数其实在前几篇文章中都有使用过,用起来也没有什么特别的地方,这里不在赘述,制作一个简单的说明。 Component中的prop参数会在编译时候自动加入到Builder中,以上面的代码举例: PropComponent.create(c).index(10)./*text("测试文本").*/build() Prop的默认值 对于可选的Prop如果不设置值,就是java的默认值。或者你也可以使用@PropDefault注解然后添加默认值。 如果你使用Kotlin,那还需要加上@JvmFiel把该字段编辑为public才行。 @MountSpec object PropComponentSpec { @JvmField @PropDefault val prop1 = "default" @JvmField @PropDefault val prop2 = -1 资源类型 在Android开发中,我们经常会限定参数的类型。比如: fun doSomething(@ColorInt color: Int, @StringRes str: Int, @DimenRes width: Int){} 在Compontent的Prop中也有类似的操作,具体看代码: fun onMount( c: ComponentContext, textView: TextView, @Prop(optional = true,resType = ResType.STRING) text: String?...

九月 5, 2020 · 2 分钟 · LengYue

Android声明式UI框架 Litho 初探——基础使用

初衷 Litho作为一个高性能的UI引擎,学习曲线还是比较高的,但是在国内能用的资料非常少(大部分都是相互复制的”Hello Word“教程),国外除了Litho自己的文档外,也没有太多教程。这几篇教程也是我边学边写。如果有那里理解不到位的地方,欢迎指正。 Litho 是什么 Litho是一个用于在Android上构建高效用户界面(UI)的声明性框架。但不同以往的UI框架,它的底层是Yoga,它通过将不需要交互的UI转换为Drawable来渲染视图,通过Yoga来完成组件布局的异步或同步(可根据场景定制)测量和计算,实现了布局的扁平化。加速了UI渲染速度 在Litho中,使用组件(Component)来构建UI,而不是直接与传统的Android视图进行交互。组件本质上是一个函数,它接受不可变的输入(称为属性 props),并返回描述用户界面的组件层次结构。 如果有Flutter开发经验,那么Litho的开发方式有点类似 接下来的教程都将结合代码进行讲解 基础配置 gradle apply plugin: 'kotlin-kapt' dependencies 中加入 // Litho implementation 'com.facebook.litho:litho-core:0.37.1' implementation 'com.facebook.litho:litho-widget:0.37.1' kapt 'com.facebook.litho:litho-processor:0.37.1' // SoLoader implementation 'com.facebook.soloader:soloader:0.9.0' // For integration with Fresco implementation 'com.facebook.litho:litho-fresco:0.37.1' // Sections implementation 'com.facebook.litho:litho-sections-core:0.37.1' implementation 'com.facebook.litho:litho-sections-widget:0.37.1' compileOnly 'com.facebook.litho:litho-sections-annotations:0.37.1' kapt 'com.facebook.litho:litho-sections-processor:0.37.1' 初始化SoLoader.Litho依赖,SoLoader用于加载底层布局引擎Yoga SoLoader.init(this, false); 使用基础Component Component Specs Litho中的视图单元叫做Component,可以直观的翻译为组件 组件分为两种类型 : Layout Spec:将其他组件组合到特定的布局中。这相当于 Android 上的 ViewGroup 。...

九月 5, 2020 · 1 分钟 · LengYue

android 10 [API 29]适配指南

Android 11[API 30]已经发布正式版,来看看全新的 android 11 [api 30] 适配指南 非SDK接口 google 针对非SDK接口的限制 非SDK接口检测工具 官方给出了一个检测工具,下载地址:veridex veridex使用方法: appcompat.sh --dex-file=apk.apk blacklist、greylist、greylist-max-o、greylist-max-p含义 以上截图中,blacklist、greylist、greylist-max-o、greylist-max-p含义如下: blacklist 黑名单:禁止使用的非SDK接口,运行时直接Crash(因此必须解决) greylist 灰名单:即当前版本仍能使用的非SDK接口,但在下一版本中可能变成被限制的非SDK接口 greylist-max-o: 在targetSDK<=O中能使用,但是在targetSDK>=P中被禁止使用的非SDK接口 greylist-max-p: 在targetSDK<=P中能使用,但是在targetSDK>=Q中被禁止使用的非SDK接口 设备ID 从Android 10开始已经无法完全标识一个设备,曾经用mac地址、IMEI等设备信息标识设备的方法,从Android 10开始统统失效。而且无论你的APP是否是配过Android 10。 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(); targetSdkVersion<29 的应用,其在获取设备ID时,会直接返回null targetSdkVersion>=29 的应用,其在获取设备ID时,会直接跑出异常SecurityException 如果您的App希望在Android 10以下的设备中仍然获取设备IMEI等信息,可按以下方式进行适配: <uses-permission android:name="android.permission.READ_PHONE_STATE" android:maxSdkVersion="28" /> Mac地址随机分配 从Android10开始,默认情况下,在搭载 Android 10 或更高版本的设备上,系统会传输随机分配的 MAC 地址。(既从Android 10开始,普通应用已经无法获取设备的真正mac地址,标识设备已经无法使用mac地址) 唯一ID的替代 Google给出的解决方案是:如果您的应用有 追踪非登录用户重装 的需求,可用ANDROID_ID来标识设备。 ANDROID_ID的生成规则为:签名+设备信息+设备用户 ANDROID_ID重置规则:设备恢复出厂设置时,ANDROID_ID将被重置 String androidId = Settings....

十二月 9, 2019 · 1 分钟 · LengYue

Glide加载ImageView显示不全的问题(fitxy/centerCrop)

简单记录下,用了一个圆角处理,圆角里面了是centerCrop,设置ImageView控件的fitxy属性,也还是几率性的显示不全! 代码是这样的: GlideApp.with(imageView.getContext()) .load(imgUrl) .apply(new RequestOptions().transform(new CircleCrop()).placeholder(placeHolder).error(placeHolder)) .into(imageView); 然后我又网上查了下,发现有网友这样说: 由于位图尺寸导致问题: 如果Imageview中默认的占位图片大小没有填满Imageview,比如lmageview10080, 但是给Imageview设置占位图片后图片没有占满控件,例如控件被填了8080,那么Glide加载图片的时候,会出现加载图片也是填满8080的情况过一会才恢复正常100*80。 占位的问题?我看了下我自己的占位图片,确实尺寸跟代码设置的控件的尺寸不一样。然后就针对这个情况进行了填充设置 fitCenter GlideApp.with(imageView.getContext()) .setDefaultRequestOptions(new RequestOptions() .centerCrop() .placeholder(placeHolder).error(placeHolder) .fitCenter() ) .load(imgUrl) .apply(new RequestOptions().circleCrop()) .into(imageView);

八月 27, 2019 · 1 分钟 · LengYue