这真的也是一个大坑,为了想要做 APP 数据持久化存储,就开始找社区的解决方案。最开始接触的是 redux,但是这个方案适用于复杂场景,对于简单的 APP 来说无疑是增加无所谓的代码量,更何况这个方案的上手难度比较高,而且也没有这么多时间去学习,所以就放弃用这个方案了。
然后在社区中我找到了一个比较使用小体积应用的存储方案—react-native-encrypted-storage
刚开始,我试了一下发现能用,但官网的 API 比较少,只有创建、删除、获取、删除全部几个 API。虽然少,但是毕竟开源,每个人时间有限,咱们也不能说责怪作者写的少,于是我根据他的 API 又封装了几个。看着我封装的方法,那可真是写的优雅呀(自我感觉良好),但当我满怀期待的想要在我的 APP 中开始使用的时候,它的问题出来了:1、它返回数据延迟大概 1—2s 2、由于延迟比较快导致我封装的方法根本没办法用
import EncryptedStorage from 'react-native-encrypted-storage';
class velocityFactory {
// 创建
createStore<T>(token: string, storeCon: T): Promise<void | Error> {
throw new Error('抽象方法,不允许直接调用,需要重写');
}
// 获取
getStore<T>(storeName: string): Promise<string | Error> {
throw new Error('抽象方法,不允许直接调用,需要重写');
}
// 删除
delStore(storeName: string): Promise<void | Error> {
throw new Error('抽象方法,不允许直接调用,需要重写');
}
// 清除所有
delAllStore(): Promise<void | Error> {
throw new Error('抽象方法,不允许直接调用,需要重写');
}
}
export class useVelocity extends velocityFactory {
/**
* 存储内容
* @param token 存储令牌
* @param storeCon 需要存储内容
*/
async createStore<T>(token: string, storeCon: T): Promise<void | Error> {
try {
await EncryptedStorage.setItem(token, JSON.stringify(storeCon));
console.log('data storage success!!!');
} catch (error) {
return new Error(`createStore show error:${error}`);
}
}
/**
* 获取存储内容
* @param storeName 存储项名
*/
async getStore(storeName: string): Promise<string | Error> {
try {
console.log('---');
const storeCon = await EncryptedStorage.getItem(storeName);
if (storeCon) {
console.log('---');
return storeCon;
}
return new Error('getStore() return undefined!');
} catch (error) {
return new Error(`An error occurred in getStore() ${error}`);
}
}
/**
* 移除指定存储项名
* @param storeName 存储项名
*/
async delStore(storeName: string): Promise<void | Error> {
try {
await EncryptedStorage.removeItem(storeName);
} catch (error) {
return new Error(`An error occurred in delStore():${error}`);
}
}
/**
* 移除全部存储项目
*/
async delAllStore(): Promise<void | Error> {
try {
await EncryptedStorage.clear();
} catch (error) {
return new Error(`An error occurred in delAllStore() ${error}`);
}
}
}
好家伙,这一问题让我立马改变了对它的看法,本来好不容易看到一个好用的,结果可还是不能用,只能另求其他方案了react-native-storage。重点来了,接下来才是我的噩梦。
由于上一个的教训,我想着我先留着这东西,要是我觉得比这个好用我再把它移除了,就是这个想法,导致了后面的烦躁!我安装官网的指引引入了它所需要的依赖,然后重新运行,当时还没出啥毛病,我还尝试来着,结果到了再重新运行的时候它一直报错一直运行不起来,我也不知道那里错了(因为之前也有这种莫名其妙的错误出现)再加上错误信息一大堆(react-native 什么错误都是一大堆一大堆的根本不想看😥),后面就开始清理缓存删除全部依赖重新引入(之前有一次是因为依赖的原因也导致了无法启动),但是还是不行。最后实在没招了,下班!
What went wrong:
Execution failed for task ':app:mergeLibDexDebug'.
A failure occurred while executing com.android.build.gradle.internal.tasks.DexMergingTaskDelegate
There was a failure while executing work items > A failure occurred while executing com.android.build.gradle.internal.tasks.DexMergingWorkAction
> com.android.builder.dexing.DexArchiveMergerException: Error while merging dex archives:
Type com.reactnativecommunity.asyncstorage.AsyncLocalStorageUtil is defined multiple times: E:\software_cache\codetotal\react\native\repo\Velocity\node_modules@react-native-async-storage\async-storage\android\build.transforms\5f2d060f42be84a9ba5564585dc8826e\transformed\classes\classes.dex, E:\software_cache\codetotal\react\native\repo\Velocity\node_modules@react-native-community\async-storage\android\build.transforms\fb3d93e6d7c3a3290fac0ed27892d7d9\transformed\classes\classes.dex
Learn how to resolve the issue at https://developer.android.com/studio/build/dependencies#duplicate_classes.
第二天再试了一下上面的处理流程,还是不行,只好老老实实的看错误信息(其实主要是 gpt 给我翻译过来的),大致意思是:我的项目中出现了重复类问题,让我看看这个链接解决跟着它解决。不打开不要紧,一打开全是字,脑瓜子给我弄的呀,直接关掉。再回想一些这个错误信息,说我有重复的依赖项,我们回想一下:我也就引入了一个关于存储的依赖就出错了,所以咱们的 APP 可能是因为重复引入多个存储相关的依赖导致了底层出现了重复类问题。把昨天的依赖给去掉以后果然项目能够跑起来了,真的哭死了。这件事情告诉了我真的要看看他的报错信息。
不过,话又说回来 [react-native-storage] 是真的快,比之前的那个简直不要太快!真的好用!
使用示例
import React, {useEffect, useState} from 'react';
import storage from '@/store/useStorage';
import {View, Text, Button} from 'react-native';
import type {IRecordItem} from '@/type/location';
export async function loadStorage(storageName: string) {
const storageItem = await storage.load({key: storageName});
return storageItem;
}
export async function saveStorage<T>(storageName: string, storageNeed: T) {
await storage.save({
key: storageName,
data: [storageNeed],
});
}
export async function appendStorage<T>(storageName: string, newStorage: T) {
const oldStorage = await storage.load({key: storageName});
return await storage.save({
key: storageName,
data: [...oldStorage, newStorage],
});
}
export default function () {
const [storageList, setStorageList] = useState<IRecordItem[]>();
async function fetchStorage() {
const loadedStorage = await loadStorage('newTest');
setStorageList(loadedStorage);
}
const saveItem: IRecordItem = {
startPoint: {
longitude: 1,
latitude: 2,
speed: 1,
timestamp: 1,
currentLocation: '111',
},
endPoint: {
longitude: 1,
latitude: 2,
speed: 1,
timestamp: 1,
currentLocation: '111',
},
distance: 1,
duration: 1,
averageVelocity: 1,
InstantVelocity: 1,
averageAccelerated: 1,
};
useEffect(() => {
fetchStorage();
}, []);
return (
<>
<View>
<Button
title="保存"
onPress={() => {
saveStorage<IRecordItem>('newTest', saveItem);
}}
/>
<Button onPress={() => loadStorage('newTest')} title="查看" />
<Button
onPress={() => appendStorage('newTest', saveItem)}
title="追加"
/>
</View>
<View>
{storageList &&
storageList.map((item, index) => (
<Text key={index}>{JSON.stringify(item)}</Text>
))}
</View>
</>
);
}
// storage.ts
import Storage from 'react-native-storage';
import AsyncStorage from '@react-native-async-storage/async-storage';
const storage = new Storage({
// maximum capacity, default 1000 key-ids
size: 1000,
// Use AsyncStorage for RN apps, or window.localStorage for web apps.
// If storageBackend is not set, data will be lost after reload.
storageBackend: AsyncStorage, // for web: window.localStorage
// expire time, default: 1 day (1000 * 3600 * 24 milliseconds).
// can be null, which means never expire.
defaultExpires: 1000 * 3600 * 24,
// cache data in the memory. default is true.
enableCache: true,
// if data was not found in storage or expired data was found,
// the corresponding sync method will be invoked returning
// the latest data.
sync: {
// we'll talk about the details later.
},
});
export default storage;
效果图贴上