<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>aitsuki</title><link>https://aitsuki.com/</link><description>Recent content on aitsuki</description><generator>Hugo -- 0.154.5</generator><language>zh-CN</language><lastBuildDate>Thu, 02 Apr 2026 17:56:00 +0800</lastBuildDate><atom:link href="https://aitsuki.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Compose rememberUpdatedState 的用法和本质</title><link>https://aitsuki.com/blog/compose-rememberupdatedstate-usage-and-essence/</link><pubDate>Thu, 02 Apr 2026 17:56:00 +0800</pubDate><guid>https://aitsuki.com/blog/compose-rememberupdatedstate-usage-and-essence/</guid><description>&lt;p&gt;&lt;strong&gt;rememberUpdatedState 本质上是为了让长生命周期的 effect / listener 闭包引用到最新的变量值，而不是重启 effect 或重新设置 listener。&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="原因是长生命周期的闭包捕获的是初始值"&gt;原因是长生命周期的闭包捕获的是初始值&lt;/h2&gt;
&lt;p&gt;来看一个容易误解的例子&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Composable&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Demo&lt;/span&gt;(name: String) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; LaunchedEffect(Unit) { delay(&lt;span style="color:#ae81ff"&gt;3000&lt;/span&gt;); println(name) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;假设初始传入的是 &lt;code&gt;name = &amp;quot;A&amp;quot;&lt;/code&gt;，1 秒后外部重组，&lt;code&gt;name&lt;/code&gt; 变成了 &lt;code&gt;&amp;quot;B&amp;quot;&lt;/code&gt;。很多人会直觉以为 3 秒后打印的是 &lt;code&gt;&amp;quot;B&amp;quot;&lt;/code&gt;，但实际上，大概率打印的是 &lt;strong&gt;&lt;code&gt;&amp;quot;A&amp;quot;&lt;/code&gt;&lt;/strong&gt;。&lt;/p&gt;</description></item><item><title>ReactNative配置Flavor</title><link>https://aitsuki.com/blog/react-natvice-12factor/</link><pubDate>Wed, 14 Jan 2026 15:51:12 +0800</pubDate><guid>https://aitsuki.com/blog/react-natvice-12factor/</guid><description>&lt;p&gt;在客户端开发中，配置 Flavor 是必不可少的环节。Android 可以通过 flavor 配置，flutter 可以通过 &amp;ndash;dart-define 配置。
ReactNative 相对麻烦一些，一是编写 RN 原生模块将原生端配置好的 flavor 传输到 RN 端。
二是使用 &lt;a href="https://github.com/lugg/react-native-config"&gt;react-native-config&lt;/a&gt; 库，它其实就是自动干了一所需要的事情。&lt;/p&gt;</description></item><item><title>Android Compose 自定义表单组件</title><link>https://aitsuki.com/blog/android-compose-custom-form-component/</link><pubDate>Tue, 13 Jan 2026 15:33:23 +0800</pubDate><guid>https://aitsuki.com/blog/android-compose-custom-form-component/</guid><description>&lt;h2 id="表单组件"&gt;表单组件&lt;/h2&gt;
&lt;p&gt;Flutter 的 Form 组件，ReactNative 中的 RHF 都是体验非常不错的表单组件，特别是 flutter 的。&lt;/p&gt;
&lt;p&gt;但是 Compose 一直没有找到合适的表单组件，所以我根据自身需求自定义了一个：&lt;/p&gt;</description></item><item><title>Android状态持久化</title><link>https://aitsuki.com/blog/android-persistent-state/</link><pubDate>Tue, 13 Jan 2026 15:30:05 +0800</pubDate><guid>https://aitsuki.com/blog/android-persistent-state/</guid><description>&lt;h2 id="app-状态和用户状态"&gt;App 状态和用户状态&lt;/h2&gt;
&lt;p&gt;在日常项目开发中，我们通常都会维护一些状态，比如 App 级别的全局状态有用户是否已登录、是否同意了隐私协议、是否完成新手引导等。或者是用户级别的状态，比如首页是单列还是双列展示、是否开启深色模式、某些功能开关等。
&lt;strong&gt;这些状态的特点是不复杂，但是访问和修改频繁，并且需要立马反应到 UI 上。&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Android和Flutter预设图片本地缓存</title><link>https://aitsuki.com/blog/prefilling-image-cache-after-upload/</link><pubDate>Mon, 12 Jan 2026 13:43:26 +0800</pubDate><guid>https://aitsuki.com/blog/prefilling-image-cache-after-upload/</guid><description>&lt;p&gt;我们经常会遇到图片上传后本地回显的需求，大概流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;拍照或选择图片，并压缩图片文件&lt;/li&gt;
&lt;li&gt;将压缩后的图片上传到服务器，并得到服务端返回的图片 URL&lt;/li&gt;
&lt;li&gt;客户端加载 URL 显示该图片&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;问题主要出在第三步&lt;/p&gt;</description></item><item><title>Android Compose 渲染Html字符串并自定义样式</title><link>https://aitsuki.com/blog/android-compose-html-string-render-and-extend/</link><pubDate>Sun, 05 Jan 2025 01:04:13 +0800</pubDate><guid>https://aitsuki.com/blog/android-compose-html-string-render-and-extend/</guid><description>&lt;p&gt;&lt;a href="https://developer.android.com/guide/topics/resources/string-resource#StylingWithHTML"&gt;Html 字符串&lt;/a&gt;可以非常方便的给文字添加样式，并且 compose-ui 在 1.7.0 版本后也提供了支持（1.7.0 以前无法在 compose 中使用）&lt;/p&gt;
&lt;p&gt;在原生 View 系统中 &lt;code&gt;HtmlCompat.fromHtml(...)&lt;/code&gt; 可以将 Html 字符串转换成 Spanned，本质上是解析少数的 html 标签为 Spannable，从而实现样式化文字。&lt;/p&gt;</description></item><item><title>使用 AWS Rekognition 进行活体检测</title><link>https://aitsuki.com/blog/liveness-detection-with-aws-rekognition/</link><pubDate>Fri, 15 Mar 2024 23:59:16 +0800</pubDate><guid>https://aitsuki.com/blog/liveness-detection-with-aws-rekognition/</guid><description>&lt;p&gt;在两年前，我分享了一篇使用Google ML kit实现活体检测的教程，但其实我并没有依赖该方案完成活体检测，因为 ML Kit 的 Face detection 仅仅是人脸检测，不具备活体识别的功能。
在那篇文章中，我通过SDk提供的人脸参数实现了活体检测，但其实这个检测流程是很容易被绕过的。如果提高检测精度，必然会导致真实用户的流失，这其中的关系很难平衡。
所以那个活体检测仅仅是第一道关卡，我会在活体检测的每一个步骤中都拍摄照片，然后将这些照片交给第三方API进行人脸检测。
而 Google ML kit 的作用仅仅是用来拍照姿势检测……&lt;/p&gt;</description></item><item><title>Android Compose Autosize</title><link>https://aitsuki.com/blog/android-compose-autosize/</link><pubDate>Sun, 04 Feb 2024 11:05:39 +0800</pubDate><guid>https://aitsuki.com/blog/android-compose-autosize/</guid><description>&lt;p&gt;某些情况下，使用 &lt;a href="https://github.com/JessYanCoding/AndroidAutoSize"&gt;AndroidAutosize&lt;/a&gt; 能极大的降低复杂UI的开发难度。
AndroidAutoSize的原理也非常简单，就是缩放当前上下文的 density。&lt;/p&gt;
&lt;p&gt;在原生View系统中，可以通过修改 &lt;code&gt;resources.displayMetrics.density&lt;/code&gt; 的值来实现，对整个Activity生效，无法做到局部缩放。&lt;/p&gt;</description></item><item><title>Android 同步的 Dialog</title><link>https://aitsuki.com/blog/android-synchronous-dialog/</link><pubDate>Fri, 15 Dec 2023 11:37:06 +0800</pubDate><guid>https://aitsuki.com/blog/android-synchronous-dialog/</guid><description>&lt;p&gt;有时候我们想要同步的等待Dialog返回结果，而不是通过异步回调，这在前端和Flutter中都是很容易实现的事情。在Android中并没有提供此类功能的默认实现，但通过kotlin和协程实现一个还是不难的。&lt;/p&gt;</description></item><item><title>Android Compose Dialog AdjustResize 无效的解决方案</title><link>https://aitsuki.com/blog/android-compose-dialog-adjust-resize-workaround/</link><pubDate>Sat, 16 Sep 2023 07:38:02 +0800</pubDate><guid>https://aitsuki.com/blog/android-compose-dialog-adjust-resize-workaround/</guid><description>&lt;blockquote&gt;
&lt;p&gt;compose-ui 1.7 以后对 AdjustResize 的支持已经非常完美，如果你使用此版本以上的 compose，不需要再阅读这篇博文。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Android Compose Dialog 目前有个 Bug，不响应软键盘的 AdjustResize。
Compose Dialog 中如果包含输入框，在键盘弹出时 Compose View 的高度并未发生变化，导致 Dialog 内容无法滚动。&lt;/p&gt;</description></item><item><title>Android 6.0 以下 font 标签的 bug</title><link>https://aitsuki.com/blog/font-color-rendering-bug-on-android/</link><pubDate>Sun, 01 Jan 2023 02:30:37 +0800</pubDate><guid>https://aitsuki.com/blog/font-color-rendering-bug-on-android/</guid><description>&lt;blockquote&gt;
&lt;p&gt;这是一个很古老且不为人知的Bug，而且这个Bug的的work-around非常有趣。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通常我们可以在 &lt;code&gt;res/string.xml&lt;/code&gt; 中配置部分文字的颜色，但是从Android4.3开始到6.0之前这种方式存在Bug，
font标签的颜色无法正常工作，标签内容会变成透明或者白色，例如下面例子中的 &amp;ldquo;Hello&amp;rdquo; 在6.0之前无法显示。&lt;/p&gt;</description></item><item><title>Android 简单的 Dialog 队列</title><link>https://aitsuki.com/blog/android-dialog-queue/</link><pubDate>Thu, 22 Dec 2022 23:46:58 +0800</pubDate><guid>https://aitsuki.com/blog/android-dialog-queue/</guid><description>&lt;p&gt;Dialog队列能避免页面中同时显示复数的窗口，App的首页是个重灾区，例如广告弹框，公告弹框，更新弹框等。&lt;/p&gt;
&lt;p&gt;一个Dialog队列应该具备以下特性：&lt;/p&gt;</description></item><item><title>使用 Google ML Kit 进行活体检测</title><link>https://aitsuki.com/blog/liveness-detection-with-google-ml-kit/</link><pubDate>Sun, 10 Jul 2022 17:13:02 +0800</pubDate><guid>https://aitsuki.com/blog/liveness-detection-with-google-ml-kit/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;首先要明确的是人脸检测（Detection）和人脸识别（Recognition）是两个不同的步骤，前者是检测图像中是否存在人脸，后者才是识别两张图像中的人脸是否属于同一人。&lt;/p&gt;</description></item><item><title>阿里云函数计算执行 ffmpeg 命令</title><link>https://aitsuki.com/blog/aliyun-fc-ffmpeg/</link><pubDate>Sat, 12 Mar 2022 09:25:30 +0800</pubDate><guid>https://aitsuki.com/blog/aliyun-fc-ffmpeg/</guid><description>&lt;blockquote&gt;
&lt;p&gt;我就说阿里怎么可能这么良心：&lt;a href="https://help.aliyun.com/document_detail/460408.html"&gt;【产品变更】免费额度过渡方案&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;自2023年01月01日起，函数计算原有的免费额度（每月前100万次函数调用免费和每月前40万GB*秒的资源使用免费）正式取消。改为新用户赠送免费试用额度。&lt;/p&gt;</description></item><item><title>SQLite文档存储——JSON1扩展</title><link>https://aitsuki.com/blog/sqlite-json1-extension/</link><pubDate>Thu, 21 Oct 2021 02:49:01 +0800</pubDate><guid>https://aitsuki.com/blog/sqlite-json1-extension/</guid><description>&lt;p&gt;这几天在写一个Pixiv的图片抓取器，Chrome插件和接口都写好了。项目使用Sqlite存储数据，主要是看中它的嵌入式特性，零配置，容易部署和备份。但是项目中存在很不满意的地方，就是sql部分过于复杂和臃肿，原生sql的多表查询的结果集实在是太难处理了。&lt;/p&gt;</description></item><item><title>Jetpack Compose TopAppBar 标题居中</title><link>https://aitsuki.com/blog/jetpack-compose-topappbar-centering-title/</link><pubDate>Fri, 08 Oct 2021 02:50:30 +0800</pubDate><guid>https://aitsuki.com/blog/jetpack-compose-topappbar-centering-title/</guid><description>&lt;blockquote&gt;
&lt;p&gt;本文章发布的20天后，Jetpack Compose 发布了 &lt;a href="https://developer.android.google.cn/jetpack/androidx/releases/compose-material3#1.0.0-alpha01"&gt;material3:1.1.0-alpha01&lt;/a&gt;，支持标题居中了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Material Design 的标题栏是真的有毒，开发Android好几年了，基本上所有App都是自定义标题栏，不会真的有人使用Material的原生Toolbar吧，灵活性也太低了，想标题居中都困难。对比起 Flutter Material 的 AppBar 真的有很大的差距，后者的 AppBar 非常好用，可以高度的自定义。&lt;/p&gt;</description></item><item><title>关于写作和学习方式</title><link>https://aitsuki.com/blog/writting-and-learning/</link><pubDate>Fri, 01 Oct 2021 02:00:14 +0800</pubDate><guid>https://aitsuki.com/blog/writting-and-learning/</guid><description>&lt;p&gt;最近一年，我发布了十几篇关于算法的文章，几乎都是我学习过程中的一些笔记，内容和算法4这本书重复，只是稍微添加了一些个人思考。&lt;/p&gt;
&lt;p&gt;我寻思这些文章对于阅读者的意义，好像没有，所以我把它们都删了。&lt;/p&gt;</description></item><item><title>Bye Bye，2020</title><link>https://aitsuki.com/blog/bye-bye-2020/</link><pubDate>Fri, 18 Dec 2020 21:04:01 +0800</pubDate><guid>https://aitsuki.com/blog/bye-bye-2020/</guid><description>&lt;p&gt;想写点感想时才发现自己是个文盲，听歌吧！&lt;/p&gt;
&lt;div&gt;
&lt;iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="330" height="86"
src="//music.163.com/outchain/player?type=2&amp;amp;id=1364291063&amp;amp;auto=0&amp;amp;height=66"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img alt="pixiv 86162768" loading="lazy" src="https://aitsuki.com/img/pixiv86162768.png"&gt;&lt;/p&gt;
&lt;p&gt;pixiv画师Y u z u K i作品：https://www.pixiv.net/artworks/86162768&lt;/p&gt;</description></item></channel></rss>