feat: 优化webp加载,增加安卓堆内存解决OOM问题

This commit is contained in:
康猛 2026-01-19 11:43:50 +08:00
parent 4ffaadeb7d
commit f557fb74b3
10 changed files with 64 additions and 2817 deletions

View File

@ -78,7 +78,10 @@ const Img = forwardRef<ExpoImage, ImgProps>((props, ref) => {
style: [style, imageStyle],
ref,
source: imageSource,
// 使用 disk 缓存策略,减少内存占用
cachePolicy: 'disk' as const,
// 添加内存缓存上限,当内存紧张时优先释放
recyclingKey: typeof src === 'string' ? src : undefined,
errorSource,
transition: { duration: 200, effect: 'cross-dissolve' as const },
...reset,

View File

@ -127,8 +127,10 @@ const VideoBox = ({ url, needWeb = true, width = 256, style, autoplay = true, ..
// 移除 key 避免组件重建导致闪烁,使用 transition 实现平滑切换
<Image
ref={imageRef}
// cachePolicy="memory-disk"
// 只使用 disk 缓存,减少内存占用
cachePolicy="disk"
// 添加 recyclingKey 帮助内存回收
recyclingKey={urlFinal}
source={{ uri: urlFinal }}
style={style as any}
autoplay={autoplay}

View File

@ -1,5 +1,3 @@
const withCmakeVersion = require('./plugins/withCmakeVersion')
export const PROJECT_ID = 'c951a6a3-e1d8-4e39-8636-c476da1fe5be'
export const APPNAME = 'duooomi'
export const EXPO_PROJECT = 'duooomi'
@ -100,7 +98,8 @@ export default ({ config }) => {
'expo-router',
'expo-native-alipay',
'expo-wechat',
// [withCmakeVersion, { version: '4.1.2' }],
'./plugins/withLargeHeap',
// ['./plugins/withCmakeVersion', { version: '4.1.2' }],
[
'@sentry/react-native/expo',
@ -112,11 +111,20 @@ export default ({ config }) => {
],
[
// expo-build-properties是另外一个expo官方的插件它能让你自定义很多构建阶段的参数包括自定义混淆规则和maven仓库等。
// 记得先npx expo install expo-build-properties
'expo-build-properties',
{
ios: {
useFrameworks: 'static',
},
android: {
enableProguardInReleaseBuilds: true,
enableShrinkResourcesInReleaseBuilds: true,
// 增加 Java 堆内存
packagingOptions: {
pickFirst: ['**/libc++_shared.so', '**/libjsc.so'],
},
// 设置 JVM 参数
jvmArgs: ['-Xmx4096m', '-XX:MaxMetaspaceSize=512m'],
extraProguardRules: `-keep class com.tencent.mm.opensdk.** {
*;
}
@ -131,6 +139,7 @@ export default ({ config }) => {
},
},
],
[
'react-native-ble-plx',
{

View File

@ -13,7 +13,7 @@ import BannerSection from '@/components/BannerSection'
import { useTemplateActions } from '@/hooks/actions/use-template-actions'
import { useTemplates } from '@/hooks/data/use-templates'
import { userBalanceStore, userStore } from '@/stores'
import { screenHeight, screenWidth, uploadFile } from '@/utils'
import { screenWidth, uploadFile } from '@/utils'
const CATEGORY_ID = process.env.EXPO_PUBLIC_GENERATE_GROUP_ID
@ -300,7 +300,7 @@ const Generate = observer(function Generate() {
<FlashList
contentContainerStyle={{ paddingHorizontal: 12, paddingBottom: 200 }}
data={displayTemplates}
drawDistance={screenHeight}
drawDistance={300}
// @ts-ignore
estimatedItemSize={itemWidth}
extraData={selectedTemplateId}

View File

@ -364,16 +364,16 @@ const Index = observer(function Index() {
// 快速滑动时可能为空,保留上次状态
if (currentVisible.size === 0 && visibleIdsRef.current.size > 0) return
// 添加前后缓冲(6个
// 添加前后缓冲(减少到3个以降低内存占用
if (viewableItems.length > 0 && allItems.length > 0) {
const first = viewableItems[0]?.index ?? 0
const last = viewableItems[viewableItems.length - 1]?.index ?? first
if (first !== null && last !== null) {
for (let i = Math.max(0, first - 12); i < first; i++) {
for (let i = Math.max(0, first - 6); i < first; i++) {
if (allItems[i]) currentVisible.add(allItems[i].id)
}
for (let i = last + 1; i < Math.min(allItems.length, last + 13); i++) {
for (let i = last + 1; i < Math.min(allItems.length, last + 7); i++) {
currentVisible.add(allItems[i].id)
}
}
@ -391,7 +391,7 @@ const Index = observer(function Index() {
isSelected={selectedId === item.id}
item={item}
itemWidth={ITEM_WIDTH}
isVisible={index < 12 || visibleIdsRef.current.has(item.id)}
isVisible={index < 9 || visibleIdsRef.current.has(item.id)}
onSelect={() => setSelectedItem(item)}
/>
),
@ -426,7 +426,7 @@ const Index = observer(function Index() {
onEndReached={handleLoadMore}
maxItemsInRecyclePool={0}
removeClippedSubviews={true}
drawDistance={600}
drawDistance={300}
onEndReachedThreshold={0.3}
refreshControl={<RefreshControl colors={['#FFE500']} refreshing={refreshing} onRefresh={handleRefresh} />}
renderItem={renderItem}

View File

@ -7,6 +7,8 @@ module.exports = function (api) {
'babel-preset-expo',
{
jsxImportSource: 'nativewind',
// 禁用 loose 模式,使用 assumptions 代替
loose: false,
},
],
'nativewind/babel',

2799
bun.lock

File diff suppressed because it is too large Load Diff

View File

@ -21,7 +21,8 @@ const BannerSection = memo<BannerProps>(function Banner({ bgVideo }) {
return (
<Block className="absolute inset-0 z-0 overflow-hidden">
<VideoBox style={{ width: screenWidth, height: screenHeight }} url={bgUrl} width={512} />
{/* 使用较小的分辨率降低内存占用 */}
<VideoBox style={{ width: screenWidth, height: screenHeight }} url={bgUrl} width={360} />
<Block className="absolute inset-0">
<Svg height="100%" preserveAspectRatio="none" viewBox="0 0 400 800" width="100%">

View File

@ -15,10 +15,7 @@ android {
`
if (!buildGradle.includes('externalNativeBuild')) {
config.modResults.contents = buildGradle.replace(
androidBlockPattern,
cmakeConfig
)
config.modResults.contents = buildGradle.replace(androidBlockPattern, cmakeConfig)
}
return config

32
plugins/withLargeHeap.js Normal file
View File

@ -0,0 +1,32 @@
const { withAndroidManifest } = require('@expo/config-plugins')
/**
* Android 应用启用 largeHeap 模式
* 这将允许应用使用更大的堆内存避免因图片/视频加载导致的内存不足
*/
const withLargeHeap = (config) => {
return withAndroidManifest(config, (config) => {
const androidManifest = config.modResults.manifest
// 确保 application 节点存在
if (!androidManifest.application) {
androidManifest.application = [{}]
}
const application = androidManifest.application[0]
// 设置 largeHeap 为 true
application.$['android:largeHeap'] = 'true'
// 可选:启用硬件加速
if (!application.$['android:hardwareAccelerated']) {
application.$['android:hardwareAccelerated'] = 'true'
}
console.log('✅ withLargeHeap: 已启用 largeHeap 模式')
return config
})
}
module.exports = withLargeHeap