Live Note

Remain optimistic

问题现象:升级到 Android 15 后,你的 React Native 应用底部安全区域失效了?当安卓系统导航栏覆盖应用内容时,这不再是个别现象——Android 15 的Edge-to-Edge设计策略强制改变了窗口布局规则。本文将提供完整的诊断和修复方案。

问题根源分析

Android 15 的关键行为变更

  1. 默认强制 Edge-to-Edge 渲染

    • 所有应用内容默认延伸至系统导航栏后方
    • SYSTEM_BAR_BACKGROUNDS 属性不再自动处理安全区域
  2. window.setDecorFitsSystemWindows(false) 成为强制标准

    • 需手动处理 Insets(内容插入区域)
    • 传统安全区域计算库失效的原因

完整解决方案

第一步:更新安全区域处理库

1
2
# 更新到支持Android 15的版本(至少4.5.0+)
yarn add react-native-safe-area-context@latest

第二步:改造根组件结构

App.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";

export default function App() {
return (
<SafeAreaProvider
initialMetrics={{
insets: { top: 0, bottom: 48 }, // Android 15最小安全高度
}}
>
{/* 使用新边距处理方式 */}
<EdgeToEdgeWrapper>
<MainAppContent />
</EdgeToEdgeWrapper>
</SafeAreaProvider>
);
}

第三步:创建 Android 15 专用适配组件

EdgeToEdgeWrapper.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { View, Platform, StatusBar } from "react-native";

export default function EdgeToEdgeWrapper({ children }) {
const insets = useSafeAreaInsets();

return (
<View
style={{
flex: 1,
paddingTop:
Platform.OS === "android" ? StatusBar.currentHeight : insets.top,
paddingBottom: insets.bottom > 0 ? insets.bottom : 48, // 兼容旧设备
}}
>
{children}

{/* 底部安全区域视觉指示器 (DEBUG模式使用) */}
{__DEV__ && (
<View
style={{
position: "absolute",
bottom: 0,
height: insets.bottom,
left: 0,
right: 0,
backgroundColor: "rgba(255,0,0,0.3)",
}}
/>
)}
</View>
);
}

第四步:关键 Android 原生适配(针对遗留问题)

android/app/src/main/java/com/yourapp/MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import android.os.Build;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Android 15+ 边缘处理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
getWindow().setDecorFitsSystemWindows(false);

final WindowInsetsController insetsController = getWindow().getInsetsController();
if (insetsController != null) {
// 控制导航栏行为 (新API)
insetsController.setSystemBarsBehavior(
WindowInsetsController.BEHAVIOR_DEFAULT
);
}
}
}

兼容性注意事项

方案 Android <14 Android 15+ 备注
基础 SafeAreaView 需升级
EdgeToEdgeWrapper 推荐方案
原生 setDecorFits… ⚠️ 需版本条件检查

结语

在 Android 15 强制推行的Full-Screen App 革命中,主动处理 Insets 不再是可选项。建议:

  1. 立即更新react-native-safe-area-context到最新版
  2. 使用提供的EdgeToEdgeWrapper组件替换旧方案
  3. MainActivity.java中添加未来兼容代码

据 Android 官方统计,Edge-to-Edge 问题在 Android 15 设备上的 Crash 率已飙升320%,及时修复可避免大规模用户流失

问题描述

我突然想把我 Blog 的主页上的排序换一下, 因为一直以来都是按 create_at 排序的, 所以我想换成 update_at, 然后我搜索了一下, 发现有一个配置可以满足这个需求.

就是这个:

1
2
3
4
5
6
7
8
# Home page setting
# path: Root path for your blogs index page. (default = '')
# per_page: Posts displayed per page. (0 = disable pagination)
# order_by: Posts order. (Order by date descending by default)
index_generator:
path: ""
per_page: 5
order_by: -updated # 这个地方

然后我修改了这个配置, 本地跑一下 hexo s 嗯. 完美!
然后推送到 CloudFlare 自动部署.

嗯…部署出来, 我的主页顺序是乱的. 并且每次部署的结果都不一致.

Read more »

非常不幸,又肾结石了.
其实大概几个月前的某天, 我发现自己的尿变茶色了.
那个时候我就知道有点不对劲了, 凌晨还在搜索自己是什么问题, 怕是尿毒症.
但是后面又没有什么事, 就没放心上.

时间线

  • 2025 年 5 月 14 日,这天到了公司之后, 开始腰痛. 疼痛持续到了中午, 午休后不痛了. 晚上睡前吃了 💊.
  • 2025 年 5 月 15 日, 今天白天一天都不痛, 晚上睡前开始有点隐隐作痛了. 吃药, 吃的药是化石胶囊.
  • 2025 年 5 月 16 日, 白天不痛, 晚上疼痛增加. 去急症照了个彩超, 右输尿管下段结石, 大小 7mm*4mm, 肾盂扩张 12mm. 医生建议体外碎石/微创. 我选择在看一个礼拜看看.
  • …, 这几天就是多喝水+跳, 但是没有好转, 吃止痛药有时候也顶不住.但是没有去年第一次肾结石痛,可能是习惯了.
  • 2025 年 5 月 24 日, 早上被痛醒, 去彩超, 看片子是在输尿管最下面了, 但是最下面好像是横着的, 平躺着卡在里面. 依旧是 7mm*4mm, 我一开始以为我是不是有两个结石, 为什么一下腰痛一下小腹痛, 可能卡在这一段平着的地方就是原因. 肾盂扩张到了 21mm, 医生说不干预的话就要中度肾积水了, 所以我选择了冲击波体外碎石.
Read more »

一篇文章

昨天晚上看了某乎的一篇文章,叫《比 996 更该死的那些人》,里面的开头:

这几年真的觉得这个世界真的已经变得不可救药了
勤劳和奋斗不仅不再被传颂和赞扬,竟然成了被鄙视,被视作愚蠢的品质。
而享乐,投机取巧成这些在过去难以启齿的行为,成了光明正大,人人争相炫耀的东西。

虽然这篇文章我开了开头就没看了,但是还是让我感触颇深,我不赞同第二句,但是第三句我是非常赞同的。在以前我是不会羡慕别人的,不知道从什么时候开始,我会越来越羡慕那些比我强、过的比我好、智商比我高的人。是欲望变了吗?我觉得是的。那个无欲无求的日子,我是不想去回忆的,因为对我来说,和坐牢一样。

从不会去鄙视那些勤劳和奋斗的人,因为他们不知道什么时候就已经追上你并且超过你了,学如逆水行舟,不进则退,但是我实在无法理解的是,投机取巧,为什么在某些人眼里成了政治正确。炫耀享乐,这个我还是可以理解的,毕竟大部分的人还是喜欢被别夸,被别人羡慕嫉妒,当然也有真正是为了分享快乐。

投机取巧的话,不知道是否是因为圈子不同,我觉得我周围认为投机取巧的人是正确的人还不少,甚至于在学校加大监管的情况下,组队进行“帮助”,并且在成绩还可以的时候,去蛊惑那些认真考试但成绩不佳的同学,试图拉他们下水。实在是不能理解那些人心里的想法,是为了让别人顺从自己从而获得心里的快感吗?我不太懂。似乎各大高校都有这种人的存在,这到底是为什么呢?

Read more »

需求概述

在旧项目中,有一个策略页面包含四种策略,分别为:Auto、Spot、Long 和 Short。页面中涉及多种配置(Config),包括公共配置(Common Config)、期货配置(Futures Config)以及仅适用于现货的配置(Spot Only Config)。

旧项目实现方式

旧项目采用 MVC 架构,页面数据全部存储在 Controller 中。四种策略共用同一个 config。在页面中使用 panel 时,整个 Controller 会被直接传入 props。由于 panel 是一个独立的组件,需要进行数据分离,这种实现方式导致策略页面运行非常卡顿。具体问题包括:每个 panel 都复制了一份完整的数据;当数据发生修改时,所有 panel 都会重新渲染(rerender),用户体验极差。

旧项目里面有一个策略页面, 里面假设有四种策略, 分别为: Auto, Spot, Long, Short. 下面有一些 Config, 他们有的是公共 Config, 有的是 Futures Config,还有的是 Spot Only Config.

新项目实现方式

Strategy Page Architecture

  • 配置分离:为四种策略分别设置独立的 config。对于公共配置(Common Config),在更新时会同步更新其他三个 config。(为何不单独设置一个 common config?一方面,四种策略的配置已完全分离,公共配置数量较少时,这种方式是可接受的;另一方面,产品需求变动频繁,单独的公共配置不利于后续扩展。)
  • Panel 交互优化:panel 组件通过 onChange 回调管理当前页面的数据变动,而 visible 状态则由 Global Store 统一管理,因为这部分与页面无关。
Read more »