[置顶] quick-cocos2d-x下有关缓存图片的管理策略

       首先介绍下quick-cocos2d-x这款引擎,quick-cocos2d-x 是在官方 cocos2d-x + Lua 版本的基础上修改起来的增强版(目前的稳定版基于 cocos2d-x 2.2)。在底层 API 上,quick 和 cocos2d-x 保持一致,而 quick 扩展的 API 主要分为两个部分:
 1. quick 里提供了一个 Lua framework,封装了大部分的 C++ API,简化了使用。在大多数情况下,只需要熟悉 quick 的 API 就可以完成开发工作。
 2. quick 中包含了不少游戏开发中必须的扩展,这些扩展也具有相应的 API。
       在使用Lua开发移动网络游戏的时候,通常一些图片资源需要在线下载然后缓存下来使用,为了节约存储空间,需要制定一个策略来对缓存图片资源进行回收处理。
       对于下载的图片资源我们需要设定好存储路径,quick中使用全局变量device.writablePath即可获取到设备的可读写路径,它实际上就是调用cocos2d-x的CCFileUtils::sharedFileUtils()->getWritablePath()函数。当然我们也可以在这个路径下新建立一个cache目录。在游戏项目的入口lua文件可以添加CCFileUtils:sharedFileUtils():addSearchPath(device.writablePath .. "cache/")这句话,这样在加载资源的时候,会搜索该目录下的资源文件。当然cache目录是要预先创建的,否则在Android平台下可能报无法找到该路径的错误。
       接下来需要写一个管理器来下载资源图片以及用一个表来对缓存资源进行维护,代码如下:
--[[
	下载图片的缓存管理器
	@author xujh
]]

ImageCacheManager = {}

local gameState = require(cc.PACKAGE_NAME .. ".api.GameState")

-- 图片缓存信息表,包括下载图片URL对应的md5值和最后更新时间
local cacheData = {}

-- 初始化GameState,存储缓存图片相关信息
function ImageCacheManager:init()
	gameState.init(function(param)
		-- local returnValue = nil
		if param.errorCode then
			print("error")
		else
			if param.name == "save" then

			elseif param.name == "load" then

			end
		end
		return param.values
	end, "image_cache.txt", nil)
	if io.exists(gameState.getGameStatePath()) then
		cacheData = gameState.load()
	end
end

--[[
	根据图片的md5值判断是否存在在缓存表中
	@param 图片的md5值
	@return 存在:true以及所在的位置,不存在:false
]]
function ImageCacheManager:exist(md5)
	for i = 1, #cacheData do
		if cacheData[i].md5 == md5 then
			return true, i
		end
	end
	return false
end

--[[
	修改图片在缓存表中的最后更新时间
	@param md5:图片的md5值, position:图片在缓存表中的位置
]]
function ImageCacheManager:updateCacheTime(md5, position)
	if cacheData[position].md5 == md5 then
		table.remove(cacheData, position)
		self:insertCacheData(md5)
		-- dump(cacheData, "update")
	else
		print("ImageCacheManager updateCacheTime ERROR!")
	end
end

--[[
	根据缓存目录里的图片来创建精灵
	@param url 缓存图片的URL地址
		   x:精灵横向坐标值,y:精灵纵向坐标值
	@return 若缓存表中存在对应的md5值,返回创建的精灵对象
]]
function ImageCacheManager:newCacheSprite(url, x, y)
	local md5 = crypto.md5(url, false)
	local isExist, position = self:exist(md5)
	if isExist then
		self:updateCacheTime(md5, position)
		local filename = md5 .. ".png"
		return display.newSprite(filename, x, y)
	end
end

--[[
	向图片缓存表中插入数据
	@param 图片的md5值
]]
function ImageCacheManager:insertCacheData(md5)
	local data = {}
	data.time = getCurrentMillis()
	data.md5 = md5
	if table.nums(cacheData) > 100 then
		-- 超过缓存上限,清除第一个元素(通常最久未更新时间戳)
		table.remove(cacheData, 1)
	end
	table.insert(cacheData, data)
	gameState.save(cacheData)
end

--[[
	下载缓存图片
	@param url 下载图片的URL地址
		   callback 调用下载处的回调函数地址
]]
function ImageCacheManager:downloadImage(url, callback)
	local md5 = crypto.md5(url, false)
	if not self:exist(md5) then
		local request = network.createHTTPRequest(function (event)
			local ok = (event.name == "completed")
			local request = event.request
			if not ok then
				print(request:getErrorCode(), request:getErrorMessage())
        		return
			end
			local code = request:getResponseStatusCode()
    		if code ~= 200 then
        		-- 请求结束,但没有返回 200 响应代码
        		print(code)
        		return
    		end
    		local filename = md5 .. ".png"
    		request:saveResponseData(device.writablePath .. "cache/" .. filename)
    		local item = {}
    		item.time = getCurrentMillis()
    		item.md5 = md5
    		table.insert(cacheData, item)
			gameState.save(cacheData)
			if callback then callback(url) end
		end, url, "GET")
		request:start()
	else
		echoInfo("The %s has downloaded.", url)
	end
end


--[[
	根据需要删除缓存图片,释放存储空间
]]
function ImageCacheManager:gcCache()
	for i = #cacheData, 1, -1 do
		local recordTime = cacheData[i].time
		local currentTime = getCurrentMillis()
		if currentTime - recordTime > 7*24*3600 then
			local cmd
			if (device.platform == "windows") then
				cmd = "DEL /Q " .. device.writablePath .. "cache\\" .. cacheData[i].md5 .. ".png"
			else
				cmd = "rm -r " .. device.writablePath .. "cache/" .. cacheData[i].md5 .. ".png"
			end
			os.execute(cmd)
			table.remove(cacheData, i)
		end
	end
	gameState.save(cacheData)
end

       下面来详细说明下代码的具体作用,首先需要一个配置文件来管理缓存目录下图片资源的一些信息,这里并没有使用cocos2d-x的CCUserDefault,而是采用了quick-cocos2d-x提供的GameState(具体用法参见http://www.kankanews.com/ICkengine/archives/69049.shtml)。使用GameState需要先进行初始化,分别传入回调函数地址、文件名称和密匙,在回调函数中我们可以根据需要对要save或是load的数据进行加密解密操作。如果已经存在配置文件,我们则将里面的内容赋给表cacheData。在cacheData表中,线性存放了每张缓存图片相关信息的表集合,主要是下载URL对应的md5值和最后更新时间。       
       ImageCacheManager:newCacheSprite函数中,会根据传入的url转换成对应的md5值(crypto.md5为quick封装好的函数),然后遍历查找cacheData表中是否存在该md5值,存在则更新该图片在cacheData表中的最后更新时间,然后调用quick-cocos2d-x中的display.newSprite来创建精灵并返回(缓存在存储空间中的图片名称即“md5值.png”),不存在即返回nil。       
       ImageCacheManager:downloadImage函数中,会根据传入的url通过CCHTTPRequest来进行下载操作。下载的图片以“md5值.png”的文件名存储在cache目录下,并获取当前时间戳连同md5值一同插入cacheData表的尾端,记得要调用GameState的sava函数进行保存操作。       
       ImageCacheManager:gcCache函数中,会倒序遍历cacheData表,当发现与当前时间戳之差超过7天时,则删除该文件,并在cacheData中remove该图片的信息。       
       在代码中获取当前时间的接口函数getCurretMillis是通过C++实现的,通过tolua++绑定到lua中,使之可以在lua代码中直接调用,具体实现代码如下:
#ifdef WIN32
#include <time.h>
#else
#include <sys/time.h>
#endif

long getCurrentMillis()
{
    long cur_time = 0;
#ifdef WIN32
    time_t now;
    time(&now);
    cur_time = now*1000;
#else
    struct timeval tv;
    gettimeofday(&tv, NULL);
    cur_time = tv.tv_sec*1000 + tv.tv_usec/1000;
#endif
    return cur_time;
}


你可能感兴趣的:([置顶] quick-cocos2d-x下有关缓存图片的管理策略)