cocos2d中的脚本加载一:优先加载下载目录中的脚本。
上一篇文章说到,要在lua层实现,下载目录中的lua脚本优加载,只需要:
package.path =
download/scripts/?.lua;+package.path即可。
但是这个做法,在android下却无法工作,于是,没有办法,只好硬啃一下cocos2d中android下面的脚本加载的源码,以求破!
bool CCLuaEngine::init(void)
{
m_state =
lua_open();
luaL_openlibs(m_state);
tolua_Cocos2d_open(m_state);
tolua_prepare_ccobject_table(m_state);
#if
(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
//android平台的话,添加一个lua脚本加载器。然后,加载脚本时就是通过这个加载器来加载。
addLuaLoader(cocos2dx_lua_loader)
#endif
return true;
}
于是乎,开始啃addLuaLoader的源码,在啃addLuaLoader的源码之前,先来科普一下lua中的package.loaders:
static
const lua_CFunction package_loaders[] =
{
lj_cf_package_loader_preload,
lj_cf_package_loader_lua,
lj_cf_package_loader_c,
lj_cf_package_loader_croot,
NULL
};
loaders中,存有各种lua的加载器,当lua
require一个文件里,将按升序的顺序,遂一调用loaders中的加载器去加载。
通过名字也可以猜得到:
lj_cf_package_loader_preload,
加载预加载的文件
lj_cf_package_loader_lua,
正常的lua文件加载
lj_cf_package_loader_c,
加载c库
对loaders有所了解之后,可以来啃addLuaLoader的源码了。
void CCLuaStack::addLuaLoader(lua_CFunction func)
{
if (!func) return;
lua_getglobal(m_state,
"package");
/* L: package */
lua_getfield(m_state, -1,
"loaders");
/* L: package, loaders */
lua_pushcfunction(m_state,
func);
/* L: package, loaders, func */
for (int i =
lua_objlen(m_state, -2) + 1; i > 2; --i)
{
lua_rawgeti(m_state, -2, i -
1);
/* L: package, loaders, func, function
*/
lua_rawseti(m_state, -3,
i);
/* L: package, loaders, func */
}
lua_rawseti(m_state, -2,
2);
/* L: package, loaders */
lua_setfield(m_state, -2,
"loaders");
/* L: package */
lua_pop(m_state,
1);
}
不熟lua api的同学可以遂个去谷歌。这个接口做的事情无非是:
实现
pacakge.loaders ={ preload,
cocos2dx_lua_loader, loader_lua, loader_c, loader_croot, ...}
然后可知道,CCLuaStack::addLuaLoader的功能实际上就是把cocos2dx_lua_loader塞在lj_cf_package_loader_lua前面,这样子,
当require一个脚本时,就会优先使用cocos2dx_lua_loader做为加载器来加载,然后再使用正常的lj_cf_package_loader_lua加载
器来加载。
cocos2d为什么要这么做呢?我想,大概是与apk包中的asset目录有关吧。cocos2dx_lua_loader这个加载器可能是具有从apk中的
asset中去正确地加载脚本的功能。有兴趣的同学可以去啃cocos2dx_lua_loader的源码了。
了解了这么底层的原理之后,回归一最原始的问题上来,如何实现,优先加载下载下来的lua脚本呢。
如果前面所说的,package.loader变成这样子:
{ preload, loader_lua, cocos2dx_lua_loader,loader_c, loader_croot, ...}就可以了,也就是把cocos2dx_lua_loader,
塞在lj_cf_package_loader_lua的后面,而非前面。
原理就是,在本文最开始时说到
“要在lua层实现,下载目录中的lua脚本优加载,只需要:
package.path =
download/scripts/?.lua;+package.path即可。”
也就是,下载目录,已经拼在package.path的前头了,但是因为,优先使用了cocos2dx_lua_loader做为加载器,而
cocos2dx_lua_loader并非源生的lua加载器,所以并不理会pacakge.path,而是直接在初如包asset中去加载脚本,所以就会优先在
初始包asset中找到老的脚本文件。
那么当cocos2dx_lua_loader,排在lj_cf_package_loader_lua,后面之时,lua的源生加载器lj_cf_package_loader_lua,会遵循lua
的加载之道,按package.path中列出的顺搜寻目顺序去遂一查找脚本。这样,就会优先从拼在package.path前头的,
download/scripts/中查找脚本,如有下载下来的新脚本,那么就会优先被查找到。
当没有下载下来的新脚本时,在download/scripts/就会查找失败。然后,会继续用排在后面的cocos2dx_lua_loader加载器去初始
包asset中去查找脚本,这样初始包中的老脚本就会被查找到。
但是,如何实现,packaget.loader ={ preload, loader_lua,
cocos2dx_lua_loader,loader_c, loader_croot, ...}呢。小
伙伴们对比CCLuaStack::addLuaLoader(lua_CFunction func)的源码稍为改动一下就可以了。
然后重新编译,运行。android工程里面,也已经是优先加载下载目录里面的脚本了。
PS:这只是cocos2d里面的原理。对比了一下quick cocos的源码,cocos2dx_lua_loader已经自己会去处理pakcage.path时面的内容
了。直接拼pacakge.path路径就可以了。但是,看了一下实现过程,就是遂个searchpath中搜寻。所以会有两种弊端:
1,加载脚本会耗时,搜寻时间增长了
2,当不同两个文件夹下面,有两个文件同名时,并且require时没有指定目录,而是让加载器在整个大目录中搜找时。就不知道会
发生什么事情,先找到那个,就加载那个了。
如有转载,还望标明出处:http://www.cnblogs.com/WaterWithAir/p/3568165.html。