Flutter 基础 | Dart 语法 mixin

假设有这样一种场景:小明和小方都是程序员。其中小方会跳舞,当然它们都会编程。 用面向对象的方法可以建模如下: 因为小明和小方都会写编程,为了复用这个行为,提取了超类 Programmer,它包含所有程序员共用的行为 code()。这样一来,Ming 和 Fang 就能复用编程行为,而不是各自重新实现一遍相同的逻辑。(继承复用了行为) 小慧是一个舞者,再用面向对象的方法建模如下: 这样的继承关系违反了 DRY 原则,即 Don’t repeat yourself. 因为小慧并未复用小方的跳舞行为,所以同样的跳舞逻辑出现了两次。 那把跳舞行为上提到它们公共的基类 Human 中,是不是就解决问题了?的确,但这不是强迫所有程序员都必须会跳舞吗。。。 那让小方同时继承 Programmer 和 Dancer 能解决问题吗?能!但多重继承容易出事情,比如 “Diamond Problem”: 假设 Human 类中有 eat() 方法,且 Programmer 和 Dancer 都重写了它,此时 Fang 会发生编译报错。因为它不知道自己的 eat() 方法该采用哪一个父类的实现。上面的类图就好像一个钻石的形状,所以称为Diamond problem。 Dart 禁用了多重继承,而是引入了mixin来解决这个问题。 mixin 是一个特殊的类,它的属性和行为可以被其他类复用,而且不需要通过继承。 语法 如果希望一组属性和行为能够复用于多个类,碰巧这些类不在一条继承链路上,此时就应该使用mixin: mixin DanceMixin { void dance() {} } 这是声明 mixin 的方式,几乎和声明 class 一模一样,就是把 class 换成 mixin 而已。 还可以通过 on 限定 mixin 适用范围:...

二月 10, 2022 · 1 分钟 · LengYue

Android 使用字节码 hook 修复第三发 bug

今天分享一下如何简单方便的实现代码插装~ 修复第三方 bug 事情是这样的,大概在上个月,公司的 Android 项目使用了一个阿里云提供的功能(真就独一份)。因为开发测试机一直是 wifi 情况下使用,完全正常,再快上线前在使用流量的情况下会崩溃。 最后发现是阿里的这个 SDK 使用的网络判断方法还是旧版本的方式(使用了getActiveNetworkInfo,该方法已经废弃),在targetSdkVersion30 的时候回直接崩溃。询问了阿里客服,答复是下周修复,我们肯定是等不到了。(事实是现在也没修复) 如何修复是个问题 上线时间已经确定了,不可能等第三方修复了。只能自己想办法: 比如我们有这么段代码 public class Utils { public static void evil() { int a = 1 / 0; } } 我们项目在打包的时候经历了:.java -> .class -> dex -> apk,假设我们在打包的时候这么做 .java -> .class -> 拿到 Utils.class,修正里面的方法 evil 方法 -> dex -> apk。这个时机,其实构建过程中也给我们提供了,也就是传说的 Transform 阶段。(类似的 arouter、butterknife 都是同样的原理实现代码插装的) 如何修改 Utils.class 呢?可以看看鸿阳大神的 ASM 修改字节码,这样学就对了! 轻量级 aop 框架 lancet 出现 饿了么,很早的时候就开源了一个框架,叫lancet。 这个框架可以支持你,在不懂字节码的情况下,也能够完成对对应方法字节码的修改。 代入到我们刚才的思路: .java -> ....

十一月 9, 2021 · 2 分钟 · LengYue

在 Jetpack Compose 中使用输入框(TextField )遇到的一些问题

在 Jetpack Compose 中使用输入框(TextField )遇到的一些问题 为了更好的阅读体验,在阅读本文之前,你需要对 Compose或者 Flutter (实在太像了)有过基础的了解 Compose 虽然发布已经快一个月了。但是真正用到项目中的应该是少之又少了。靠着以前写 Flutter 积累的少许经验,最近决定试试水,在项目中使用,接下来大概率会更新一些在使用 Compose 遇到的问题 先定一个小目标 日常开发中,类似下面这中搜索功能应该是很常见的需求了,接下来我们就来实现它 TextField 的简单使用 TextField 提供了很多的参数用法,我们先参照 Google 开发文档的基础用法尝试完成以下 UI 给的样式。 稍微了解的同学都知道这个实现起来很简单:row + icon + TextField 完事 还是贴一下简单的代码吧。主要看TextField 部分 var text by remember { mutableStateOf("") } Row( Modifier .fillMaxWidth() .padding(end = 20.dp, start = 10.dp) .background(Color.White), verticalAlignment = Alignment.CenterVertically ) { ···· 省略 ···· TextField( value = text, onValueChange = { text = it onValueChange....

八月 20, 2021 · 4 分钟 · LengYue

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

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