quick-cocos2d-x基于源码加密打包功能的更新策略(2)

本文详细探讨了Quick-x更新机制的原理,包括如何利用loadChunksFromZIP接口实现新旧代码的替换,解决require机制带来的更新难题,以及如何处理深层逻辑更新等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二、更新原理讨论及更复杂的更新功能

1.更新原理

在前面的更新过程中,从服务器取文件列表,并根据文件列表再更新相关的文件,这都是很好理解的。当然其中还有些流程细节关系到健壮性、续传、文件版本分发等,我们可以后面再讨论。

对于一些刚开始学习Quick-x的朋友来说,可能希望了解的是,这一更新机制的替换原理是什么,为什么新的文件下载后能够替代原来的代码生效呢?

从前面我们参考的源文件编译及加密的相关文章,可以清楚的看到,初始代码打包成的game.zip文件,是在AppDelegate.ccp中,在程序启动前通过loadChunksFromZIP载入的。这一载入工作实际上已经将所有代码都加载了,之后调用require时,将会直接 调用而不会再去找代码文件来载入。也正因为如此,一些朋友会感到迷惑,即使下载了新的代码文件,又如何让它生效呢?

其实很简单,loadChunksFromZIP是可以多次调用的,而且如果第二次载入的包中的代码模块与之前载入的模块有重名,新的模块会覆盖旧的模块。

在Quick-x的Lua代码中,对应的调用接口是CCLuaLoadChunksFromZIP。有了这个接口,我们下载新的代码包后,就可以自己加载了。在update.lua里,在下载完成后,会自动将act标记为load的文件加载一次,这样,模块的新代码就取代了旧的代码,也就实现了我们之前看到的更新功能。

原理很简单,似乎没问题了?可惜问题没这么简单。这是因为require的机制问题。如果在第二次载入包之前,某一个模块文件已经被require过,那么,虽然第二次载入后,这一模块的代码已经被更新了,但再次require时,由于此模块不会被真正调用而是直接取原来的调用结果,将会造成更新不生效。

下面我们通过调试一个新的更新情景来说明这个问题及解决方法。

2.已经require过的模块的更新

假设现在我们希望更新一下界面上的图片。

如果只是更换个别图片,这是很简单的,只需要在代码里newSprite里在图片文件名前加上完整的下载目录路径,然后将新的图片放到服务器上,在 flist文件中添加图片文件名即可。不建议在要更新的代码里使用addSearchPath来添加搜索路径,因为在图片同名的情况下并不能保证优先查找到的是新的文件。

如果是更新纹理图,仍然可以使用上述明确指定资源路径的方式,同时也是建议的方式。因此,对于我们之前MainScene.lua的代码,可以做以下修改(假设device.writablePath是下载路径):

display.addSpriteFramesWithFile(device.writablePath..GAME_TEXTURE_DATA_FILENAME, device.writablePath..GAME_TEXTURE_IMAGE_FILENAME)
self.bg = display.newSprite("#logo.png", display.cx-200, display.cy-200)
self:addChild(self.bg)

当然这不是唯一的改法。为了说明主题,我们换成修改config模块的方式。这次不修改MainScene.lua,而是在config.lua中,修改原来的纹理图定义如下:

GAME_TEXTURE_DATA_FILENAME = device.writablePath.."game.plist"
GAME_TEXTURE_IMAGE_FILENAME = device.writablePath.."game.png"

要注意的是,如果不是更新代码,这样写是有问题的。通常config模块都是最早被require的,甚至还在framework.init模块之前,此时device都还没有定义,所以如果是正常流程就会出错。但这里是更新代码,在被调用之前,update模块已经调用过framework.init,所以device已经被初始化过了。

这就引出了我们的问题,既然config模块在早期已经被调用,那么,当新的config下载到客户端后被加载时,再require实际上已经不能让它生效了!

最直接的解决办法是,在重新require之前,调用以下的代码:

package.loaded["config"] = nil

即将config的调用标记清除。这样,再次require时就能让Lua程序重新去执行新的config模块了。我们将这条语句加到appentry模块的开头,这是最合适的地方了。

我们仍然将修改后的config.lua和appentry.lua打包成update.bin,和新的图片资源文件一起放到服务器上,这次服务器的flist文件如下:

local list = {
  ver = "1.0.3",
  stage = {
   {name="game.plist", code="29d103e9831720c1be12d8b33a1ea762", },
   {name="game.png", code="e9dd2797018cad79186e03e8c5aec8dc", },
   {name="update.bin", code="a681c6a002989832645ed26b766c7afa", act="load"},
  },
  remove = {
  },
}
return list
在客户端启动程序,版本自动被更新,进入MainScene显示的图片变成了新的,成功了!

大家可以自行验证不清除调用标记的情况。要注意的是如果是在player上,显示的图片一样会变成新的,这是因为device.writablePath 在player上刚好是最优先的搜索路径,所以下载后的图片会被先搜到。因此这个测试最好下载到其他目录下,或者在新的config模块里加一条调试输出语句,这样就很容易确认新的模块是否真的被执行了。

由上述例子可以看到,更新模块时,困难的地方就在于判断重加载前有没有已经被调用的模块。特别是作在应用运行过程中动态更新的方案时,更需要对流程有清楚的把握。

即使是现在实现的这个预更新的方案,在解决了config模块的重载调用问题后,仍然要面对一个相对更困难的问题:如果framework模块需要更新,应该怎么办?

3.深层逻辑更新的处理方式:强制覆盖式调用

framework模块是Quick-x的核心之一,旧版本里是在main.lua里载入,现在的版本更是将它的载入位置提前到了Lua程序入口被调用之前的C++代码中,而通常framework的init模块也是早早会被调用。即使是update模块,也是先调用了init才能方便的实现更新功能。虽然这一模块更新的机率很小,但作为解决方案,还是要考虑清楚应该如何对它进行更新。

首先会想到的,是象之前处理config模块一样,清除调用标记。然而仔细考虑,会觉得不太行得通。因为看代码可以知道,init模块还调用了 framework里很多的其他模块,在修改之后要理清哪些模块需要重调用是很繁琐的,而繁琐就容易出错。因此这不是最好的解决方案。

经过考虑,我觉得最方便的是这样一种解决方式:如果需要更新framework模块,那么,在打包时,不将代码放在framework目录下,而是其他的目录,如“framework1”。这样,所有的模块就变成了framework1.init、framework1.device等等,在新的代码里调用“require "framework1.int"”,将会真正重新运行框架代码,而不必担心有哪个模块没有重新运行生效。

这就是“强制覆盖式调用”的含义,它在很多情形下可以避免过于复杂的更新逻辑的判断,推荐给大家。

再举个例子,如果更新模块本身需要更新,那应该怎么办呢?如果要为此在更新模块中预留更新逻辑,会非常复杂难解。然而,如果跳出来看,其实完全可以在修改完update模块后,打包成一个updateNew模块放到服务器,让原更新模块下载后强制调用,这样一来,整个处理就简单了。

通过上面的讨论,现在我们已经能确认,update模块的更新功能是有效的。接下来我们还要讨论更新方案的另一个要点:安全性。

如果因为更新失败造成客户端无法启动,那无疑是致命的。幸运的是,Quick-x的打包机制能让我们保有一个原始版本,即使是在最不幸的出错情况下,程序仍然能回到最初的版本而不至于崩渍。

资源下载链接为: https://pan.quark.cn/s/22ca96b7bd39 在 IT 领域,文档格式转换是常见需求,尤其在处理多种文件类型时。本文将聚焦于利用 Java 技术栈,尤其是 Apache POI 和 iTextPDF 库,实现 doc、xls(涵盖 Excel 2003 及 Excel 2007+)以及 txt、图片等格式文件向 PDF 的转换,并实现在线浏览功能。 先从 Apache POI 说起,它是一个强大的 Java 库,专注于处理 Microsoft Office 格式文件,比如 doc 和 xls。Apache POI 提供了 HSSF 和 XSSF 两个 API,其中 HSSF 用于读写老版本的 BIFF8 格式(Excel 97-2003),XSSF 则针对新的 XML 格式(Excel 2007+)。这两个 API 均具备读取和写入工作表、单元格、公式、样式等功能。读取 Excel 文件时,可通过创建 HSSFWorkbook 或 XSSFWorkbook 对象来打开相应格式的文件,进而遍历工作簿中的每个 Sheet,获取行和列数据。写入 Excel 文件时,创建新的 Workbook 对象,添加 Sheet、Row 和 Cell,即可构建新 Excel 文件。 再看 iTextPDF,它是一个用于生成和修改 PDF 文档的 Java 库,拥有丰富的 API。创建 PDF 文档时,借助 Document 对象,可定义页面尺寸、边距等属性来定制 PDF 外观。添加内容方面,可使用 Paragraph、List、Table 等元素将文本、列表和表格加入 PDF,图片可通过 Image 类加载插入。iTextPDF 支持多种字体和样式,可设置文本颜色、大小、样式等。此外,iTextPDF 的 TextRenderer 类能将 HTML、
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值