fix:总内存显示bug修复

This commit is contained in:
zheng020 2026-07-30 15:44:17 +08:00
parent 2f672920d9
commit 7667aadf3a
3 changed files with 65 additions and 27 deletions

View File

@ -161,9 +161,11 @@ const info = await cacheManager.getCacheInfo()
// quotaTotalBytes, // 配额总量 = uni.getStorageInfoSync().limitSize*1024 // quotaTotalBytes, // 配额总量 = uni.getStorageInfoSync().limitSize*1024
// quotaAvailableBytes, // 配额可用 = quotaTotalBytes - appUsedBytes // quotaAvailableBytes, // 配额可用 = quotaTotalBytes - appUsedBytes
// usagePercent, // 百分比0-1001 位小数)= appUsedBytes / quotaTotalBytes * 100 // usagePercent, // 百分比0-1001 位小数)= appUsedBytes / quotaTotalBytes * 100
// deviceTotalBytes, // 设备总存储 = plus.io.getStorageInfo().totalSize * 1024HTML5+ APIMVP 已支持) // deviceTotalBytes, // 设备总存储字节Android 走 StatFs(Environment.getDataDirectory())iOS 走 NSFileManager.attributesOfFileSystem
// deviceFreeBytes, // 设备可用存储 = plus.io.getStorageInfo().availableSize * 1024 // deviceFreeBytes, // 设备可用字节,同上
// deviceUsagePercent, // 设备占比0-1002 位小数)= appUsedBytes / deviceTotalBytes * 100 // deviceUsagePercent, // 设备占比0-1002 位小数)= appUsedBytes / deviceTotalBytes * 100
// // 注deviceTotalBytes/Free 通过 Native.js 在 ioPath.getDeviceStorageInfo() 取得
// // 与 getAndroidApkSize / useShare.js 同模式;不算 native plugin
// othersBytes, // "其他" section 大小 = 所有黑名单 keys + 不可清理文件大小 // othersBytes, // "其他" section 大小 = 所有黑名单 keys + 不可清理文件大小
// categories: [{id, label, description, sizeBytes, keyCount, warning}, ...] // categories: [{id, label, description, sizeBytes, keyCount, warning}, ...]
// } // }
@ -323,8 +325,12 @@ await invalidateAll() // 清空 memoryMap + inFlightMap
**关于"磁盘" vs "配额"** **关于"磁盘" vs "配额"**
- **进度条**仍以 `uni.getStorageInfoSync().limitSize`app 自己的 SQLite 配额)为 100%,表达"app 缓存压力"(用户的清理决策依据) - **进度条**仍以 `uni.getStorageInfoSync().limitSize`app 自己的 SQLite 配额)为 100%,表达"app 缓存压力"(用户的清理决策依据)
- **百分比分母** = `plus.io.getStorageInfo().totalSize`(设备总存储),即"占设备 X% 存储空间"——HTML5+ 标准 APIAndroid/iOS 均原生支持,**不需要 native plugin** - **百分比分母** = 设备总存储(`appUsedBytes / deviceTotalBytes * 100`),即"占设备 X% 存储空间"
- **设备信息行** = `plus.io.getStorageInfo()` 直接拿 `{ totalSize, availableSize }`,无权限要求;非 APP-PLUS 平台或调用失败时显示 `—` - **设备信息行**(设备总空间 / 设备可用)通过 **Native.js**`utils/ioPath.js#getDeviceStorageInfo()` 取得:
- Android`plus.android.importClass('android.os.StatFs')` + `Environment.getDataDirectory()`,无需权限
- iOS`plus.ios.invoke('NSFileManager', 'attributesOfFileSystemForPath:error:', '/')` 读 `NSFileSystemSize` / `NSFileSystemFreeSize`public API
- 非 APP-PLUS 平台或失败时返回 `{ totalBytes: 0, freeBytes: 0 }`,前端 `formatSize(0)` 显示 `—`
- Native.js **不是** native plugin不需要写 Java/ObjC、不需要 `nativeplugins/` 目录、不需要 `manifest.json` 改动),与项目已有 `getAndroidApkSize` / `useShare.js` 同模式
**草稿/引导的二级提示**:行右侧副文字"按账号分组清理",警示用户详情页会有分组。 **草稿/引导的二级提示**:行右侧副文字"按账号分组清理",警示用户详情页会有分组。
@ -426,7 +432,7 @@ cacheManager.getCacheInfo()
├─ [并行 2] 读取存储配额 ├─ [并行 2] 读取存储配额
│ ├─ uni.getStorageInfoSync() → { currentSize (KB), limitSize (KB) } │ ├─ uni.getStorageInfoSync() → { currentSize (KB), limitSize (KB) }
│ ├─ ioPath.getSandboxTotalSize() → 沙盒 doc 目录下所有文件总字节数(新增 ioPath 只读 API与 scanSandboxTmpFiles 不同,前者含白名单 preload/share/image 所有文件,后者仅统计 tmp/ │ ├─ ioPath.getSandboxTotalSize() → 沙盒 doc 目录下所有文件总字节数(新增 ioPath 只读 API与 scanSandboxTmpFiles 不同,前者含白名单 preload/share/image 所有文件,后者仅统计 tmp/
│ └─ ioPath.getDeviceStorageInfo() → 设备级 { totalSize (KB), availableSize (KB) }HTML5+ API非 APP-PLUS 返回 {0,0} │ └─ ioPath.getDeviceStorageInfo() → 设备级 { totalBytes, freeBytes }Android 走 StatFsiOS 走 NSFileManagerNative.js非 native plugin非 APP-PLUS 返回 {0,0}
聚合: 聚合:
totalBytes = sum(categories.sizeBytes) // 可清理总量(=内存缓存空间 chip totalBytes = sum(categories.sizeBytes) // 可清理总量(=内存缓存空间 chip

View File

@ -105,13 +105,12 @@ export async function getCacheInfo() {
} catch (e) { } catch (e) {
console.warn('[cacheManager] getSandboxTotalSize failed:', e.message) console.warn('[cacheManager] getSandboxTotalSize failed:', e.message)
} }
let deviceTotalKB = 0, deviceFreeKB = 0 let deviceTotalBytes = 0, deviceFreeBytes = 0
try { try {
const dev = await getDeviceStorageInfo() const dev = await getDeviceStorageInfo()
deviceTotalKB = dev.totalBytes || 0 // ioPath.getDeviceStorageInfo 返回的 totalBytes/freeBytes 已是字节Android: blockSize*blocks, iOS: NSFileSystemSize不要再 * 1024
deviceFreeKB = dev.freeBytes || 0 deviceTotalBytes = dev.totalBytes || 0
// 调试日志:把 getDeviceStorageInfo 的原始返回和 cacheManager 解析后的值都打出来 deviceFreeBytes = dev.freeBytes || 0
console.log('[cacheManager][debug] getDeviceStorageInfo raw =', JSON.stringify(dev), 'parsed totalKB =', deviceTotalKB, 'freeKB =', deviceFreeKB)
} catch (e) { } catch (e) {
console.warn('[cacheManager] getDeviceStorageInfo failed:', e.message) console.warn('[cacheManager] getDeviceStorageInfo failed:', e.message)
} }
@ -123,9 +122,7 @@ export async function getCacheInfo() {
const quotaAvailableBytes = Math.max(0, raw) const quotaAvailableBytes = Math.max(0, raw)
const quotaExceeded = raw < 0 const quotaExceeded = raw < 0
const usagePercent = quotaTotalBytes > 0 ? (appUsedBytes / quotaTotalBytes) * 100 : 0 const usagePercent = quotaTotalBytes > 0 ? (appUsedBytes / quotaTotalBytes) * 100 : 0
// 设备级数据plus.io.getStorageInfo 返回 KB转字节 // 设备级数据已在 try 块里取好ioPath 返回的就是字节,不再 * 1024
const deviceTotalBytes = deviceTotalKB * 1024
const deviceFreeBytes = deviceFreeKB * 1024
const deviceUsagePercent = deviceTotalBytes > 0 const deviceUsagePercent = deviceTotalBytes > 0
? (appUsedBytes / deviceTotalBytes) * 100 ? (appUsedBytes / deviceTotalBytes) * 100
: 0 : 0

View File

@ -524,31 +524,66 @@ async function _listDir(dirEntry) {
} }
/** /**
* 获取设备级存储信息HTML5+ 标准 APIAndroid/iOS 均支持 * 获取设备级存储信息Native.jsAndroid StatFsiOS NSFileManager
* 不需要任何 native plugin APP-PLUS 平台或失败时返回安全零值 * 不需要任何 native plugin APP-PLUS 平台或失败时返回安全零值
* 单位为 KBplus.io 原始返回调用方按需 * 1024 转字节 * 与项目已有 getAndroidApkSize / useShare.js Native.js 模式 1:1 对齐
* @returns {Promise<{totalBytes:number, freeBytes:number}>} * @returns {Promise<{totalBytes:number, freeBytes:number}>}
*/ */
export function getDeviceStorageInfo() { export function getDeviceStorageInfo() {
return new Promise((resolve) => { return new Promise((resolve) => {
// #ifdef APP-PLUS // #ifdef APP-PLUS
try { try {
plus.io.getStorageInfo({ if (typeof plus === 'undefined') {
success: (res) => { return resolve({ totalBytes: 0, freeBytes: 0 })
// 调试日志:打印 plus.io.getStorageInfo 的真实返回 shape }
console.log('[ioPath][debug] plus.io.getStorageInfo res =', JSON.stringify(res), 'keys =', res ? Object.keys(res) : null) if (plus.os.name === 'Android') {
// Android用 StatFs 查 /data 分区(即系统"内部存储"
// 不需要任何权限Android 10+ 分区存储不影响 StatFsStatFs 查的是卷级元数据)
const Environment = plus.android.importClass('android.os.Environment')
const StatFs = plus.android.importClass('android.os.StatFs')
const dataDir = Environment.getDataDirectory()
const stat = new StatFs(dataDir.getPath())
// getBlockSizeLong 是 API 18+project minSdkVersion=21 保证可用
// plus.android 对 *Long 变体代理有时不稳定 → parseFloat 兜底
const blockSize = parseFloat(plus.android.invoke(stat, 'getBlockSizeLong'))
const totalBlocks = parseFloat(plus.android.invoke(stat, 'getBlockCountLong'))
const freeBlocks = parseFloat(plus.android.invoke(stat, 'getAvailableBlocksLong'))
if (blockSize > 0 && totalBlocks > 0) {
resolve({ resolve({
totalBytes: Number(res?.totalSize) || 0, totalBytes: blockSize * totalBlocks,
freeBytes: Number(res?.availableSize) || 0, freeBytes: blockSize * freeBlocks,
}) })
}, } else {
fail: (e) => { console.warn('[ioPath] getDeviceStorageInfo (Android) got non-positive values')
console.warn('[ioPath] getStorageInfo failed:', e?.message)
resolve({ totalBytes: 0, freeBytes: 0 }) resolve({ totalBytes: 0, freeBytes: 0 })
}, }
}) } else if (plus.os.name === 'iOS') {
// iOS用 NSFileManager.attributesOfFileSystem 查系统卷
// NSFileSystemSize / NSFileSystemFreeSize 是 public API参照 useShare.js 调用风格)
const fm = plus.ios.invoke('NSFileManager', 'defaultManager')
if (!fm) {
console.warn('[ioPath] getDeviceStorageInfo (iOS) fm is null')
return resolve({ totalBytes: 0, freeBytes: 0 })
}
const attrs = plus.ios.invoke(fm, 'attributesOfFileSystemForPath:error:', '/')
if (attrs) {
const total = parseFloat(attrs.plusGetAttribute('NSFileSystemSize')) || 0
const free = parseFloat(attrs.plusGetAttribute('NSFileSystemFreeSize')) || 0
// 必须 deleteObject否则 NSObject proxy 会泄露(参照 useShare.js 清理模式)
plus.ios.deleteObject(attrs)
plus.ios.deleteObject(fm)
resolve({ totalBytes: total, freeBytes: free })
} else {
plus.ios.deleteObject(fm)
console.warn('[ioPath] getDeviceStorageInfo (iOS) attrs is null')
resolve({ totalBytes: 0, freeBytes: 0 })
}
} else {
// Harmony / 其它平台:暂不支持,返回 0
resolve({ totalBytes: 0, freeBytes: 0 })
}
} catch (e) { } catch (e) {
console.warn('[ioPath] getStorageInfo threw:', e?.message) console.warn('[ioPath] getDeviceStorageInfo failed:', e?.message)
resolve({ totalBytes: 0, freeBytes: 0 }) resolve({ totalBytes: 0, freeBytes: 0 })
} }
// #endif // #endif