版本: 3.8.0

语言: TypeScript

环境: Mac


简介


在cocosCreator中,针对于本地存储主要使用localStorage接口,通过key-value的格式进行存储和读取数据。

主要接口有:

定义文件如下:

// cc.d.ts
export const sys: {
  // HTML5 标准中的 localStorage 的本地存储功能,在 Web 端等价于 window.localStorage
  localStorage: Storage;
}

// lib.dom.d.ts
interface Storage {
    // 返回数据项的数量
    readonly length: number;
    // 移除所有存储的数据
    clear(): void;
    // 根据键名获取数据,如果没有则null
    getItem(key: string): string | null;
    // 获取指定索引处的键名,如果没有则null
    key(index: number): string | null;
    // 根据键名移除指定数据
    removeItem(key: string): void;
    // 存储键名和数据, 注意可能会存在存储已满的情况,这样会抛出异常
    setItem(key: string, value: string): void;
    [name: string]: any;
}

在cocosCreator中,本地数据的存储是以sqlite数据库格式存储的。

我们以setItem简单看下引擎的封装相关:

  • C++相关,目录在: …/engine-native/cocos/storage/local-storage
// LocalStorage.cpp
void localStorageSetItem(const std::string &key, const std::string &value) {
  assert(_initialized);
  int ok = sqlite3_bind_text(_stmt_update, 1, key.c_str(), -1, SQLITE_TRANSIENT);
  ok |= sqlite3_bind_text(_stmt_update, 2, value.c_str(), -1, SQLITE_TRANSIENT);

  ok |= sqlite3_step(_stmt_update);

  ok |= sqlite3_reset(_stmt_update);

  if (ok != SQLITE_OK && ok != SQLITE_DONE)
    printf("Error in localStorage.setItem()\n");
}
  • Android平台相关, 目录在 …/libcocos/intermediates/javac/com/cocos/lib
// LocalStorage-android.cpp
void localStorageSetItem(const std::string &key, const std::string &value) {
  assert(gInitialized);
  JniHelper::callStaticVoidMethod(JCLS_LOCALSTORAGE, "setItem", key, value);
}

// CocosLocalStorage.class 
public static void setItem(String key, String value) {
  try {
    String sql = "replace into " + TABLE_NAME + "(key,value)values(?,?)";
    mDatabase.execSQL(sql, new Object[]{key, value});
  } catch (Exception var3) {
    var3.printStackTrace();
  }
}

简单的看下内部的实现,了解本地数据的存储在数据库中即可。


使用


脚本中使用本地存储,常用的接口是:

  • setItem(key: string, value: string): void; 存储数据

  • getItem(key: string): string | null 获取数据

  • removeItem(key: string): void; 移除数据

简单的示例:

const key = "Debug_Storage";
// 保存数据
sys.localStorage.setItem(key, "cocosCreator");
// 获取数据
let value = sys.localStorage.getItem(key);
console.log("----- 存储的数据是:", value);

注意:

  • setItem存储的数据是string类型,因此存储数据时,注意对数据类型转换
  • setItem的存储存在已满的情况,注意异常的发生
  • getItem获取数据为stringnull, 注意对返回数据的安全判定

因此,在项目中可增加对localStorage的封装管理,以支持:

  1. 支持不同基础数据类型的存储,包括但不限于string类型, 使用数据转换即可

  2. 支持数组、Map等复杂数据类型的存储, 使用 Json 转换

  3. 数据读取,支持默认数据的设置

Json转换的主要接口:

  • JSON.stringify 将数据转换为Json字符串
  • JSON.parse 用于将Json字符串解析为数据

主要实现逻辑如下:

import { _decorator, sys} from 'cc';
const { ccclass, property } = _decorator;

export class StorageManager {
  private static _instance: StorageManager = null;
  static get instance() {
    if (this._instance) {
      return this._instance;
    }
    this._instance = new StorageManager();
    return this._instance;
  }

  // 保存数据
  public setItem(key: string, value:any) {
    if (value === undefined || value === null) {
      console.log(`本地存储数据非法, key:${key}`);
      return;
    }
    let valueType = typeof(value);
    if (valueType === "number" && isNaN(value)) {
      console.log(`本地存储数据为NaN, key:${key}`);
      return;
    } 

    // 转换数据
    if (valueType === "number") {
      value = value.toString();
    } else if (valueType === "boolean") {
      // boolean类型转换为0或1
      value = value ? "1" : "0";
    } else if (valueType === "object") {
      // 数组或Map类型转换为JSON字符串
      value = JSON.stringify(value);
    }
    sys.localStorage.setItem(key, value);
  }

  // 读取数据
  public getItem(key: string, defaultValue: any = ""): any {
    let value = sys.localStorage.getItem(key);
    // 数据获取失败,就走默认设置
    if (value === null) {
      return defaultValue;
    }

    // 检测是否为JSON字符串
    const regex = /^\s*{[\s\S]*}\s*$/;
    if (regex.test(value)) {
      return JSON.parse(value);
    }
    return value;
  }
}

测试用例:

private debugStorage() {
  let storageManager = StorageManager.instance;

  // 检测数据合法性
  storageManager.setItem("Storage_Debug_1", null);
  storageManager.setItem("Storage_Debug_2", undefined);
  storageManager.setItem("Storage_Debug_3", NaN);

  // 存储
  storageManager.setItem("Storage_Int", 10);
  storageManager.setItem("Storage_Boolean", true);
  storageManager.setItem("Storage_Array1", [1,2,3]);
  storageManager.setItem("Storage_Array2", new Array(4,5,6));
  storageManager.setItem("Storage_Map", {name: "TypeScript", index:10});

  // 获取数据
  console.log("Storage_Int", storageManager.getItem("Storage_Int"));
  console.log("Storage_Boolean", storageManager.getItem("Storage_Boolean"));
  console.log("Storage_Array1", storageManager.getItem("Storage_Array1"));
  console.log("Storage_Array2", storageManager.getItem("Storage_Array2"));
  console.log("Storage_Map", storageManager.getItem("Storage_Map"));
}

cocosCreator 之localStorage本地存储和封装拓展-LMLPHP

至于 removeItem, key, clear等实现,直接调用localStorage的相关方法即可。


拓展1: 支持保存多份数据


在实际的项目开发中,频繁的功能测试可能需要我们保存多份本地存储数据。

可以通过key键 + 玩家的唯一标识符ID的方式,存储不同用户的数据,以实现保存多份。

StorageManager类的大概修改,可以这样:

// 初始化角色ID, 可用于项目获取用户数据成功后进行设置
private _roleId: string = "";
public setRoleId(id: string) {
	this._roleId = id;
}

// 针对于setItem或getItem的设置,增加如下判定:
let newKey = key;
if (this._roleId !== "") {
  newKey = `${key}_${this._roleId}`;
}
sys.localStorage.setItem(newKey, value);

待定…

10-27 07:55