Android-使用@AutoService实现spi

什么是 SPI? Java SPI 全称 Service Provider Interface,是 Java 提供的一套用来被第三方实现或者扩展的 API,它可以用来启用框架扩展和替换组件。实际上是“基于接口的编程 + 策略模式 + 配置文件”组合实现的动态加载机制. 具体解释就是: 定义一个接口文件 写出多个该接口文件的实现 在 src/main/resources/ 下建立 /META-INF/services 目录, 新增一个以接口命名的文件 , 内容是要接口的实现类全路径 使用 ServiceLoader 类 来获取到这些实现的接口 示例 定义一个接口文件 - Book package com.apkdv.spi_test; public interface Book { String name(); } 实现两个接口 package com.apkdv.spi_test; public class Android implements Book { @Override public String name() { return "Android"; } } package com....

七月 22, 2021 · 1 分钟 · LengYue

Vue watch中调用this时出现undefined问题

记录一下在 Vue 学习使用时候的遇到的问题 在watch侦听器中,想要调用methods中的方法 applicationId: (val)=> { if (val) { this.getAppConfig(val) } } 结果提示: Error in callback for watcher "applicationId": "TypeError: Cannot read property 'getAppConfig' of undefined" 查了官方的资料才知道。改成这样就可以了:

二月 24, 2021 · 1 分钟 · LengYue

RecyclerView使用优化的ListAdapter

前言 ListAdapter发布很久了,但是一直没有机会使用,这次终于因为性能问题对项目进行优化,再次做一下笔记,相比传统的Adapter,它能够用较少的代码实现更多的RecylerView的动画,并且可以自动存储之前的list。并且ListAdapter还带有异步的DiffUtil的工具,只有当items变化的时候进行刷新,而不用刷新整个list,这无疑能大大提高RecyclerView的性能。 具体实现 在项目具体实现中,如果原先使用的是RecyclerView.Adapter, 则替换起来非常简单,只需要将继承改成ListAdapter即可。 public abstract class ListAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH>{ ... } 很明显,也只是多了一层继承(Android loves extends)。具体使用跟原来的实现差别并不大,不过多了些东西,下面列举一下。 构造方法变化 构造方法默认要实现DiffUtil.ItemCallback, 用于计算list的两个非空item的不同。具体要写两个抽象方法: 判断两个Objects 是否代表同一个item对象, 一般使用Bean的id比较 public abstract boolean areItemsTheSame(T oldItem, T newItem); public abstract boolean areContentsTheSame(T oldItem, T newItem); DiffUtil.ItemCallback的实现正是ListAdapter拓展类实现的精髓所在,它根据判断对象的一致性和对象的内容是否相同来进行UI的刷新和动画的实现,在性能方面,一般情况下当然是可以有所提升的。 提交数据集合 使用了ListAdapter, 相比RecyclerView.Adapter, 另一个好处就是不用构建内部的数据集合了,很简单直接adapter::submitList即可,而获取item,则使用getItem(position)即可。 //源码:提交list数据 public void submitList(List<T> list) { mHelper.submitList(list); } //源码:获取当前对象 protected T getItem(int position) { return mHelper.getCurrentList().get(position); } 对于分页 数据分页我们当然要摒弃原来的办法,拥抱PagedListAdapter, 该方法比较类似ListAdapter, 唯一不同的就是提交数据的时候,提交的是PagedList(用于构建分页的类,对此不了解的话,可以查看相应的API)。 //源码:提交分页数据 public void submitList(PagedList<T> pagedList) { mDiffer....

二月 22, 2021 · 3 分钟 · LengYue

⾃定义布局流程

⾃定义布局流程 布局过程 确定每个 View 的位置和尺⼨ 作⽤:为绘制和触摸范围做⽀持 绘制:知道往哪⾥绘制 触摸反馈:知道⽤户点的是哪⾥– 流程 从整体看 测量流程:从根 View 递归调⽤每⼀级⼦ View 的 measure() ⽅法,对它们进⾏测量 布局流程:从根 View 递归调⽤每⼀级⼦ View 的 layout() ⽅法,把测量过程得出的⼦ View 的位置和尺⼨传给⼦ View,⼦ View 保存 为什么要分两个流程? 从个体看,对于每个 View: 运⾏前,开发者在 xml ⽂件⾥写⼊对 View 的布局要求 layout_xxx ⽗ View 在⾃⼰的 onMeasure() 中,根据开发者在 xml 中写的对⼦ View 的要求,和⾃⼰的可⽤空间,得出对⼦ View 的具体尺⼨要求...

一月 25, 2021 · 5 分钟 · LengYue

ViewModel之自定义构造函数

刚入坑架构组件没多久,发现很多基础性的东西理解起来是没什么问题的,但是一到具体使用就各种问题,相关实践文章也比较少,更多的只能靠自己解决 = =。今天无意间了解了AndroidViewModel的一个使用场景和实现原理,特地记录下来。 ###前言 一开始,跟着官方文档,ViewModel我们是这样实现的: public class MyViewModel entends ViewModel { //... } 然后是创建实例对象: MyViewModel viewModel = ViewModelProviders.of(this).get(MyViewModel.class); 可以注意到,我们定义的ViewModel是没有构造函数的,也就是说如果遇到比较复杂情况下(需要在创建ViewModel时赋一些初始值),应该怎么办呢?总不能自己加一个构造函数,然后new出来吧,或者通过set方法一个一个加也行,但是这样就有点low了。于是,我发现了一个类AndroidViewModel AndroidViewModel AndroidViewModel这个类的定义很简单,它继承自ViewModel,然后添加了一个application私有属性: public class AndroidViewModel extends ViewModel { @SuppressLint("StaticFieldLeak") private Application mApplication; public AndroidViewModel(@NonNull Application application) { mApplication = application; } /** * Return the application. */ @SuppressWarnings("TypeParameterUnusedInFormals") @NonNull public <T extends Application> T getApplication() { //noinspection unchecked return (T) mApplication; } } 上面提到,很多时候我们需要在初始化ViewModel时需要传递一些参数,例如Activity中的一些参数等,但是根据ViewModel的生命周期我们知道,ViewModel中是不能传入Activity等实例对象的,因为在ViewModel存活的过程中,Activity是有可能会被销毁的。因此,Google爸爸推荐我们传入application。...

十一月 30, 2020 · 3 分钟 · LengYue

Android Jetpack Compose 超快速上手指南

Compose 介绍 Jetpack Compose发布也快有一年的时间了,虽然目前仍是DEV阶段,但是距离可用已经不远了。Compose库是用响应式编程的方式对View进行构建,可以用更少更直观的代码,更强大的功能,能提高开发速度(可以参考几乎一模一样的Flutter,开发速度确实很快)。 Jetpack Compose对于没有接触过声明式UI的小伙伴可能会学习曲线有点陡峭,对于已经能熟练开发Flutter应用的小伙白来说几乎没有难度。(Compose就是参考flutter的模式开发的,代码中还可以看到Flutter的相关注释) 这篇文章不回去解释过多的原理与技巧,只要是让你能够快速上手开发项目。 Compose 如何使用 因为目前(2020年09月27日)Compose仍处于开发阶段,所以需要使用最新 Canary 版的 Android Studio 预览版。 可以选择直接创建Empty Compose Activity来创建一个全新的Compose应用,或者可以选手动修改gradle的方式来创建Compose应用 修改Gradle 在模块的build.gradle文件中新增: android { ... kotlinOptions { jvmTarget = '1.8' useIR = true } buildFeatures { compose true } composeOptions { kotlinCompilerExtensionVersion compose_version kotlinCompilerVersion '1.4.0' } } dependencies中新增: implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.material:material:$compose_version" implementation "androidx.ui:ui-tooling:$compose_version" 目前最新的Compose版本是1.0.0-alpha03 使用Compose最低的buildTools版本要求为29 使用 @Compose 所有关于构建View的方法都必须添加@Compose的注解才可以。并且@Compose跟协程的Suspend的使用方法比较类似,被@Compose的注解的方法只能在同样被@Comopse注解的方法中才能被调用...

九月 27, 2020 · 2 分钟 · LengYue

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