在上一篇文章中,说到了"流程"的由来,以及我对流程的使用. 这一片就是对流程的应用.前一篇文章中说到了三条流程 check_log_measure, check_env_measure, check_update_measure.先来看看chenck_log_measure的源码:
1 --小岩<[email protected]> 2 --2015-5-28 1:29 3 local clm = class("check_log_measure", fw.measure) 4 5 function clm:ctor(dispatcher, next_m) 6 clm.super.ctor(self, dispatcher, next_m) 7 end 8 9 function clm:onlaunch() 10 local appconf = fw.classickv.new("conf/app_conf") 11 if appconf:get("syslog_enabled") then 12 local sys_logger = fw.logger.new(appconf:get("syslog_limit_level")) 13 if appconf:get("syslog_console_enabled") then 14 local sys_clogger 15 if appconf:get("syslog_console_printfun") then 16 sys_clogger = fw.console_logger.new(appconf:get("syslog_console_printfun"), 0) 17 else 18 sys_clogger = fw.console_logger.new(print, 0) 19 end 20 sys_logger:add_log_chanel(sys_clogger) 21 end 22 23 if appconf:get("syslog_file_enabled") then 24 fw.fileutil.make_dir(appconf:get("syslog_file_dir")) 25 local sys_flogger = fw.file_logger.new( 26 appconf:get("syslog_file_path"), 27 "w+b", 28 appconf:get("syslog_file_autoflush"), 29 0, 30 appconf:get("syslog_file_parser") 31 ) 32 sys_logger:add_log_chanel(sys_flogger) 33 sys_logger:info("%s", "创建sylog 日志文件 " .. appconf:get("syslog_file_path") .. "!") 34 end 35 36 fw.objpool.set_object(sys_logger, string.upper("sys_logger")) 37 end 38 fw.objpool.set_object(appconf, string.upper("appconf")) 39 40 _G.sys_logger = { 41 critical = function(fmt, ...) 42 if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then 43 fw.objpool.get_object(string.upper("sys_logger")):critical(fmt, ...) 44 end 45 end, 46 err = function(fmt, ...) 47 if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then 48 fw.objpool.get_object(string.upper("sys_logger")):error(fmt, ...) 49 end 50 end, 51 warning = function(fmt, ...) 52 if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then 53 fw.objpool.get_object(string.upper("sys_logger")):warning(fmt, ...) 54 end 55 end, 56 info = function(fmt, ...) 57 if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then 58 fw.objpool.get_object(string.upper("sys_logger")):info(fmt, ...) 59 end 60 end, 61 debug = function(fmt, ...) 62 if fw.objpool.get_object(string.upper("appconf")):get("syslog_enabled") then 63 fw.objpool.get_object(string.upper("sys_logger")):debug(fmt, ...) 64 end 65 end 66 } 67 68 self:finish() 69 end 70 71 function clm:onfinish() 72 sys_logger.info("%s", "执行check_log_measure结束,启动sys_logger成功!") 73 self:remove() 74 end 75 76 function clm:onremove() 77 sys_logger.info("%s", "移除check_log_measure!") 78 end 79 80 return clm
这边是启动sys_logger, 我在启动结束的时候添加到全局表,这样方便操作.启动sys_logger以后,方便记录app在运行时候的情况.出现了问题也方便定位.当然,不同开发者对于问题的处理方式是不一样的,我是习惯这样做.然后给出check_env_measure的源码:
1 --小岩<[email protected]> 2 --2015-5-27 17:53 3 local cem = class("check_env_measure", fw.measure) 4 5 function cem:ctor(dispatcher, next_m) 6 cem.super.ctor(self, dispatcher, next_m) 7 end 8 9 function cem:onlaunch() 10 sys_logger.info("%s", "启动check_env_measure!") 11 local envconf = fw.classickv.new("conf/env_conf") 12 fw.fileutil.set_search_paths(envconf:get("search_paths")) 13 14 local envprofile_cls = require "game.env_profile" 15 local envprofile = envprofile_cls.new() 16 17 envprofile:set_os_code(fw.platform.arch_code) 18 envprofile:set_os_name(fw.platform.arch_name) 19 envprofile:set_os_lancode(fw.platform.arch_lancode) 20 envprofile:set_os_lanname(fw.platform.arch_lanname) 21 22 envprofile:set_checkupdate(envconf:get("check_update")) 23 envprofile:set_updatealways(envconf:get("update_always")) 24 envprofile:set_http_server_url(envconf:get("http_server_url")) 25 26 envprofile:set_network_enabled(fw.network.is_network_enabled()) 27 envprofile:set_network_type(fw.network.get_network_type()) 28 29 fw.objpool.set_object(envconf, string.upper("envconf")) 30 fw.objpool.set_object(envprofile, string.upper("envprofile")) 31 32 self:finish() 33 end 34 35 function cem:onfinish() 36 sys_logger.info("%s", "执行check_env_measure结束,检查env成功!") 37 self:remove() 38 end 39 40 function cem:onremove() 41 sys_logger.info("%s", "移除check_env_measure!") 42 end 43 44 return cem
check_env主要是对平台的一些处理,我模仿操作系统去初始化一些环境变量,并以oop的方式添加到env_profile里面,env_profile主要是简单数据结构,提供的只是getter/setter方法。这里有一点需要注意的是,我根据配置文件修改了searchpaths,这主要是应对我们的更新模块,如果更新了新的文件,放在writable_path下面的一个指定目录,那么应该先从这个目录搜索,然后再去搜索apk中的资源目录.其他的环境变量有一些是配置文件配置的,有一些则是网络启用相关的判断,当然,在启动的过程中,依次初始化判定网络状态是不可靠的,因为网络状态可能随时关闭,不过我有仔细想过这个问题:在这么极短的时间内,我们可以认为这个环境变量是可靠的,所以不需要要担心这个问题了.
下面是配置文件env_conf:
1 search_paths = { 2 fw.platform.get_writable_path() .. "res", 3 fw.platform.get_writable_path() .. "src", 4 "res", 5 "src", 6 }, 7 8 check_update = true, 9 update_always= true, 10 11 http_server_url = "http://192.168.1.131:8000/web/serverInfo",
配置文件我是使用classickv模式读取的,也就是支持lua syntax的,所以我们可以直接使用之前初始化的lua模块.这样很方便.我现在主要是配置了searchpath和更新模块的相应参数.下面看一下更新流程的源码:
1 --小岩<[email protected]> 2 --2015-5-27 17:59 3 local cum = class("check_update_measure", fw.measure) 4 5 function cum:ctor(dispatcher, next_m) 6 cum.super.ctor(self, dispatcher, next_m) 7 self.proxy_ = fw.proxy.new(self.dispatcher_) 8 self.percent_ = 0 9 end 10 11 function cum:onlaunch() 12 sys_logger.info("%s", "启动check_update_measure!") 13 14 self.proxy_:add_listener(fw.ircu.START_IRCU_UPDATE, 15 function(e) 16 self:on_START_IRCU_UPDATE(e) 17 end, self:get_name() .. "tag") 18 :add_listener(fw.ircu.CANNOT_CONN_TO_HOST, 19 function(e) 20 self:on_CANNOT_CONN_TO_HOST(e) 21 end, self:get_name() .. "tag") 22 :add_listener(fw.ircu.IRCU_UPDATE_FINISH_SUCC, 23 function(e) 24 self:on_IRCU_UPDATE_FINISH_SUCC(e) 25 end, self:get_name() .. "tag") 26 :add_listener(fw.ircu.IRCU_UPDATE_FINISH_PART, 27 function(e) 28 self:on_IRCU_UPDATE_FINISH_PART(e) 29 end, self:get_name() .. "tag") 30 :add_listener(fw.ircu.IRCU_UNCOMPRESS_FILE, 31 function(e) 32 self:on_IRCU_UNCOMPRESS_FILE(e) 33 end, self:get_name() .. "tag") 34 :add_listener(fw.ircu.ALREADY_NEWEST, 35 function(e) 36 self:on_ALREADY_NEWEST(e) 37 end, self:get_name() .. "tag") 38 :add_listener(fw.ircu.IRCU_UPDATE_START_PART, 39 function(e) 40 self:on_IRCU_UPDATE_START_PART(e) 41 end, self:get_name() .. "tag") 42 43 local envprofile = fw.objpool.get_object(string.upper("envprofile")) 44 45 if not envprofile:get_network_enabled() then 46 sys_logger.info("%s", "本地网络没开启, 不执行check_update!") 47 self:finish() 48 return 49 end 50 sys_logger.info("%s", "本地网络已开启,执行check_update!") 51 52 local ircu_update = fw.ircu.new( 53 self.dispatcher_, 54 envprofile:get_http_server_url(), 55 envprofile:get_network_type() 56 ) 57 58 ircu_update:check_update() 59 end 60 61 function cum:on_START_IRCU_UPDATE(e) 62 sys_logger.info("%s", "------------------") 63 sys_logger.info("%s", "开始执行版本更新!") 64 end 65 66 function cum:on_CANNOT_CONN_TO_HOST(e) 67 sys_logger.info("%s", e.name) 68 sys_logger.info("%s", "执行版本更新失败!") 69 self:finish() 70 end 71 72 function cum:on_IRCU_UPDATE_FINISH_SUCC(e) 73 sys_logger.info("%s", e.name) 74 sys_logger.info("%s", "执行版本更新成功!") 75 self:finish() 76 end 77 78 function cum:on_IRCU_UPDATE_FINISH_PART(e) 79 sys_logger.info("%s", e.name) 80 sys_logger.info("%s [%s]", "成功更新版本", e.patch) 81 end 82 83 function cum:on_IRCU_UNCOMPRESS_FILE(e) 84 sys_logger.info("%s", e.name) 85 sys_logger.info("%s, -->[%s] [%.4f]", "成功解压", e.file, e.percent) 86 self.percent_ = self.percent_ + e.percent 87 sys_logger.info("%s --> [%.4f]", "更新总进度", self.percent_) 88 end 89 90 function cum:on_ALREADY_NEWEST(e) 91 sys_logger.info("%s", e.name) 92 sys_logger.info("%s", "已经是最新版本了, 不需要更新!") 93 self:finish() 94 end 95 96 function cum:on_IRCU_UPDATE_START_PART(e) 97 sys_logger.info("%s", e.name) 98 sys_logger.info("%s --[%s] [%.4f]", "开始下载", e.path, e.size/e.total*100) 99 end 100 101 function cum:onfinish() 102 sys_logger.info("%s", "执行check_update_measure结束!") 103 self:remove() 104 end 105 106 function cum:onremove() 107 sys_logger.info("%s", "移除check_update_measure!") 108 self.proxy_:remove_all_listeners() 109 end 110 111 112 return cum
可以看到这个流程其实主要都是处理ircu模块抛出的事件,然后就执行流程跳转的相关操作.根据之前的env_conf配置文件中的相关参数,我们可以给项目添加debug开关,如果在开发过程中可以屏蔽掉更新模块,当然我是没有添加
这个也很容易做到.在合适的配置添加:
1 if not envprofile:get("check_update") then 2 sys_logger.info("%s", "更新模块没有开启!“) 3 self:finish() 4 return 5 end
这个还是很简单的.下面就看看ircu模块,也就是我们的增量更新模块:
1 --小岩<[email protected]> 2 --2015-5-28 17:44 3 local network = require "fw.util.network" 4 local fileutil= require "fw.util.fileutil" 5 local encrypt = require "fw.util.encrypt" 6 local ini = require "fw.util.ini" 7 8 local ircu = class("ircu") 9 10 ircu.CANNOT_CONN_TO_HOST = "CANNOT_CONNECTION_TO_HOST" 11 ircu.START_IRCU_UPDATE = "START_IRCU_UPDATE" 12 ircu.ALREADY_NEWEST = "ALREADY_NEWEST" 13 ircu.IRCU_UPDATE_FINISH_SUCC = "IRCU_UPDATE_FINISH_SUCC" 14 ircu.IRCU_UPDATE_FINISH_PART = "IRCU_UPDATE_FINISH_PART" 15 ircu.IRCU_UNCOMPRESS_FILE = "IRCU_UNCOMPRESS_FILE" 16 ircu.IRCU_UPDATE_START_PART = "IRCU_UPDATE_START_PART" 17 18 ircu.TMP = fileutil.get_writable_path() .. "TMP" 19 20 function ircu:ctor(dispatcher, http_server_url, network_type) 21 self.dispatcher_ = dispatcher 22 self.http_server_url_ = http_server_url 23 self.http_request_ = cc.XMLHttpRequest:new() 24 self.responseType = cc.XMLHTTPREQUEST_RESPONSE_STRING 25 self.network_type_ = network_type 26 end 27 28 function ircu:check_update() 29 self:check_local_version_file() 30 self:requrest_http_server_url() 31 end 32 33 --检查本地版本文件 34 function ircu:check_local_version_file() 35 local local_version_file = fileutil.get_writable_path() .. "version" 36 if not fileutil.is_exists(local_version_file) then 37 fileutil.write_content(local_version_file, encrypt.encrypt("[current]\nv=0"), "wb") 38 end 39 end 40 41 --请求http服务器地址 42 function ircu:requrest_http_server_url() 43 self.http_request_:open("GET", self.http_server_url_) 44 self.http_request_:registerScriptHandler(function() 45 if self.http_request_.readyState == 4 and (self.http_request_.status >= 200 and self.http_request_.status <= 207) then 46 self.dispatcher_:dispatch({name = ircu.START_IRCU_UPDATE}) 47 self:requrest_version_file(self.http_request_.response) 48 elseif self.http_request_.readyState == 1 then 49 self.dispatcher_:dispatch({name = ircu.CANNOT_CONN_TO_HOST}) 50 end 51 end) 52 self.http_request_:send() 53 end 54 55 --请求版本文件 56 function ircu:requrest_version_file(version_file_url) 57 self.http_request_:open("GET", version_file_url) 58 self.http_request_:registerScriptHandler(function() 59 if self.http_request_.readyState == 4 and (self.http_request_.status >= 200 and self.http_request_.status <= 207) then 60 if self.network_type_ == network.t.WIFI then 61 self:update_wifi_mode(self.http_request_.response) 62 elseif self.network_type_ == network.t.GPRS then 63 self:update_gprs_mode(self.http_request_.response) 64 end 65 elseif self.http_request_.readyState == 1 then 66 self.dispatcher_:dispatch({name = ircu.CANNOT_CONN_TO_HOST}) 67 end 68 end) 69 self.http_request_:send() 70 end 71 72 function ircu:update_wifi_mode(version_file_content) 73 local tmp_version_file = fileutil.get_writable_path() .. "tmp_version" 74 fileutil.write_content(tmp_version_file, encrypt.encrypt(version_file_content), "wb") 75 self.tmp_ini = ini.new(tmp_version_file) 76 77 local tmp_t = {} 78 local total_size = 0 79 80 for s, _ in pairs(self.tmp_ini.props_) do 81 table.insert(tmp_t, s) 82 end 83 84 --给版本排序 85 table.sort(tmp_t, function(v1, v2) 86 return tonumber(v1) <= tonumber(v2) 87 end) 88 89 self.local_version_ini_ = ini.new(fileutil.get_writable_path() .. "version") 90 local local_version = self.local_version_ini_:get("current", "v") 91 92 --检查是不是最新版本 93 if tonumber(local_version) == tonumber(tmp_t[#tmp_t]) then 94 self.dispatcher_:dispatch({name = ircu.ALREADY_NEWEST}) 95 return 96 end 97 98 local tt = {} 99 100 for _, v in ipairs(tmp_t) do 101 if tonumber(v) >= tonumber(local_version) then 102 103 table.insert(tt, v) 104 total_size = total_size + tonumber(self.tmp_ini:get(v, "size")) 105 end 106 end 107 108 self.new_version_t_ = tt 109 self.total_size_ = total_size 110 111 self:download_patch() 112 end 113 114 function ircu:update_gprs_mode(version_file_content) 115 116 end 117 118 function ircu:download_patch() 119 if #self.new_version_t_ == 0 then 120 self.dispatcher_:dispatch({name = ircu.IRCU_UPDATE_FINISH_SUCC}) 121 fileutil.remove(ircu.TMP) 122 return 123 end 124 125 fw.ircu_helper.download(self.tmp_ini:get(self.new_version_t_[1], "path"), ircu.TMP, 126 function(code) end, 127 function(p) 128 if p == 100 then 129 local patch_size = tonumber(self.tmp_ini:get(self.new_version_t_[1], "size")) 130 local patch_percent = patch_size / self.total_size_ 131 self.dispatcher_:dispatch({ 132 name = ircu.IRCU_UPDATE_START_PART, 133 path = self.new_version_t_[1], 134 size = patch_size, 135 total= self.total_size_ 136 }) 137 138 139 fw.ircu_helper.uncompress(fileutil.get_writable_path(), ircu.TMP, 140 function(f, n, t) 141 self.dispatcher_:dispatch({ 142 name = ircu.IRCU_UNCOMPRESS_FILE, 143 file = f, 144 num = n, 145 total = t, 146 percent = 1/t*patch_percent * 100, 147 }) 148 end, 149 150 function() 151 self.local_version_ini_:update("current", "v", self.new_version_t_[1]) 152 self.local_version_ini_:save() 153 self.dispatcher_:dispatch({name = ircu.IRCU_UPDATE_FINISH_PART, patch=self.new_version_t_[1]}) 154 table.remove(self.new_version_t_, 1) 155 self:download_patch() 156 end) 157 end 158 end 159 ) 160 end 161 162 return ircu
这个模块使用很简单,需要注意的就是下面这几个抛出的事件就行了,我将这个模块独立封装出来,就是依靠事件来保持低耦合的.
1 ircu.CANNOT_CONN_TO_HOST = "CANNOT_CONNECTION_TO_HOST" 2 ircu.START_IRCU_UPDATE = "START_IRCU_UPDATE" 3 ircu.ALREADY_NEWEST = "ALREADY_NEWEST" 4 ircu.IRCU_UPDATE_FINISH_SUCC = "IRCU_UPDATE_FINISH_SUCC" 5 ircu.IRCU_UPDATE_FINISH_PART = "IRCU_UPDATE_FINISH_PART" 6 ircu.IRCU_UNCOMPRESS_FILE = "IRCU_UNCOMPRESS_FILE" 7 ircu.IRCU_UPDATE_START_PART = "IRCU_UPDATE_START_PART"
当然,这几个事件除了错误代码之外,其他都是用来做更新界面百分比显示,以及显示更新信息的.ircu模块中使用了我写的c++的异步下载和解压函数.也就是fw.ircu_helper.download/uncompress.下面我给出我的这个C++模块:
1 #include "lua_ircu.h" 2 #include "tolua_fix.h" 3 #include "CCLuaEngine.h" 4 #include <curl/curl.h> 5 #include <curl/easy.h> 6 #include <stdio.h> 7 #include <vector> 8 #include <thread> 9 10 #include <cocos2d.h> 11 12 #if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32) && (CC_TARGET_PLATFORM != CC_PLATFORM_WP8) && (CC_TARGET_PLATFORM != CC_PLATFORM_WINRT) 13 #include <sys/types.h> 14 #include <sys/stat.h> 15 #include <errno.h> 16 #include <dirent.h> 17 #endif 18 19 #ifdef MINIZIP_FROM_SYSTEM 20 #include <minizip/unzip.h> 21 #else 22 #include "unzip.h" 23 #endif 24 25 #if __cplusplus 26 extern "C" { 27 #endif 28 #include <lualib.h> 29 #include <lauxlib.h> 30 #if __cplusplus 31 } 32 #endif 33 34 #define BUFFER_SIZE 8192 35 #define MAX_FILENAME 512 36 37 #define LOW_SPEED_LIMIT 1L 38 #define LOW_SPEED_TIME 5L 39 40 bool createDirectory(const char *path) 41 { 42 #if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) 43 return FileUtils::getInstance()->createDirectory(_storagePath.c_str()); 44 #elif (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) 45 BOOL ret = CreateDirectoryA(path, nullptr); 46 if (!ret && ERROR_ALREADY_EXISTS != GetLastError()) 47 { 48 return false; 49 } 50 return true; 51 #else 52 mode_t processMask = umask(0); 53 int ret = mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO); 54 umask(processMask); 55 if (ret != 0 && (errno != EEXIST)) 56 { 57 return false; 58 } 59 60 return true; 61 #endif 62 } 63 64 static int 65 lua_fw_ircu_download(lua_State* lua_state) { 66 std::string url = lua_tostring(lua_state, 1); 67 std::string tmppath = lua_tostring(lua_state, 2); 68 int h1 = toluafix_ref_function(lua_state, 3, 0); 69 int h2 = toluafix_ref_function(lua_state, 4, 0); 70 auto t = std::thread(fw::ircu::download, url, tmppath, h1, h2); 71 t.detach(); 72 return 0; 73 } 74 75 static int 76 lua_fw_ircu_uncompress(lua_State* lua_state) { 77 std::string dstpath = lua_tostring(lua_state, 1); 78 std::string filepath = lua_tostring(lua_state, 2); 79 int h1 = toluafix_ref_function(lua_state, 3, 0); 80 int h2 = toluafix_ref_function(lua_state, 4, 0); 81 auto t = std::thread(fw::ircu::uncompress, dstpath, filepath, h1, h2); 82 t.detach(); 83 return 0; 84 } 85 86 namespace fw { 87 88 static size_t 89 downloadPatch(void *ptr, size_t size, size_t nmemb, void *userdata) { 90 FILE *fp = (FILE*)userdata; 91 size_t written = fwrite(ptr, size, nmemb, fp); 92 return written; 93 } 94 95 static int 96 downloadProgressFunc(void *ptr, double totalToDownload, double nowDownloaded, double totalToUpLoad, double nowUpLoaded) { 97 typedef cocos2d::LuaStack LuaStack; 98 typedef cocos2d::LuaEngine LuaEngine; 99 LuaStack *pStack = LuaEngine::getInstance()->getLuaStack(); 100 static int percent = 0; 101 int tmp = (int)(nowDownloaded / totalToDownload * 100); 102 if (percent != tmp) 103 { 104 percent = tmp; 105 cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{ 106 pStack->pushInt(tmp); 107 pStack->executeFunctionByHandler((int)ptr,1); 108 }); 109 } 110 return 0; 111 } 112 113 114 void 115 ircu::download(const std::string& url, const std::string& tmppath, int h1, int h2) { 116 typedef cocos2d::LuaStack LuaStack; 117 typedef cocos2d::LuaEngine LuaEngine; 118 LuaStack *pStack = LuaEngine::getInstance()->getLuaStack(); 119 120 FILE *fp = fopen(tmppath.c_str(), "wb"); 121 if(!fp) { 122 cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{ 123 if (h1) 124 pStack->pushInt(ircu::CREATE_FILE); 125 pStack->executeFunctionByHandler(h1,1); 126 }); 127 return; 128 } 129 void *_curl; 130 _curl = curl_easy_init(); 131 132 if(!_curl) { 133 return; 134 } 135 136 CURLcode res; 137 curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); 138 curl_easy_setopt(_curl, CURLOPT_WRITEFUNCTION, downloadPatch); 139 curl_easy_setopt(_curl, CURLOPT_WRITEDATA, fp); 140 curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, false); 141 curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, downloadProgressFunc); 142 curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, h2); 143 curl_easy_setopt(_curl, CURLOPT_NOSIGNAL, 1L); 144 curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_LIMIT, LOW_SPEED_LIMIT); 145 curl_easy_setopt(_curl, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME); 146 curl_easy_setopt(_curl, CURLOPT_FOLLOWLOCATION, 1 ); 147 148 res = curl_easy_perform(_curl); 149 curl_easy_cleanup(_curl); 150 151 if (res != 0 ) { 152 cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=]{ 153 pStack->pushInt(ircu::NETWORK); 154 pStack->executeFunctionByHandler(h1,1); 155 }); 156 fclose(fp); 157 return; 158 } 159 160 fclose(fp); 161 return; 162 } 163 164 void 165 ircu::uncompress(const std::string& dstpath, const std::string& filepath, int h1, int h2) { 166 typedef cocos2d::LuaStack LuaStack; 167 typedef cocos2d::LuaEngine LuaEngine; 168 LuaStack *pStack = LuaEngine::getInstance()->getLuaStack(); 169 170 unzFile zipFile = cocos2d::unzOpen(filepath.c_str()); 171 if (!zipFile) { 172 return; 173 } 174 cocos2d::unz_global_info global_info; 175 if (cocos2d::unzGetGlobalInfo(zipFile, &global_info) != UNZ_OK) { 176 cocos2d::unzClose(zipFile); 177 return ; 178 } 179 char readBuffer[BUFFER_SIZE]; 180 uLong i; 181 182 for (i = 0; i < global_info.number_entry; ++i) 183 { 184 cocos2d::unz_file_info fileInfo; 185 char fileName[MAX_FILENAME]; 186 if (cocos2d::unzGetCurrentFileInfo(zipFile, 187 &fileInfo, 188 fileName, 189 MAX_FILENAME, 190 nullptr, 191 0, 192 nullptr, 193 0) != UNZ_OK) 194 { 195 cocos2d::unzClose(zipFile); 196 return; 197 } 198 199 const std::string fullPath = dstpath + "/" + fileName; 200 201 const size_t filenameLength = strlen(fileName); 202 if (fileName[filenameLength-1] == ‘/‘) 203 { 204 if (!createDirectory(fullPath.c_str())) 205 { 206 cocos2d::unzClose(zipFile); 207 return; 208 } 209 } 210 else 211 { 212 const std::string fileNameStr(fileName); 213 214 size_t startIndex=0; 215 216 size_t index=fileNameStr.find("/",startIndex); 217 218 while(index != std::string::npos) 219 { 220 const std::string dir=dstpath+fileNameStr.substr(0,index); 221 222 FILE *out = fopen(dir.c_str(), "r"); 223 224 if(!out) 225 { 226 if (!createDirectory(dir.c_str())) 227 { 228 cocos2d::unzClose(zipFile); 229 return; 230 } 231 else 232 { 233 //CCLOG("create directory %s",dir.c_str()); 234 } 235 } 236 else 237 { 238 fclose(out); 239 } 240 241 startIndex=index+1; 242 243 index=fileNameStr.find("/",startIndex); 244 245 } 246 247 if (cocos2d::unzOpenCurrentFile(zipFile) != UNZ_OK) 248 { 249 cocos2d::unzClose(zipFile); 250 return; 251 } 252 253 FILE *out = fopen(fullPath.c_str(), "wb"); 254 if (! out) 255 { 256 cocos2d::unzCloseCurrentFile(zipFile); 257 cocos2d::unzClose(zipFile); 258 return; 259 } 260 261 int error = UNZ_OK; 262 do 263 { 264 error = cocos2d::unzReadCurrentFile(zipFile, readBuffer, BUFFER_SIZE); 265 if (error < 0) 266 { 267 cocos2d::unzCloseCurrentFile(zipFile); 268 cocos2d::unzClose(zipFile); 269 return; 270 } 271 272 if (error > 0) 273 { 274 fwrite(readBuffer, error, 1, out); 275 } 276 } while(error > 0); 277 278 fclose(out); 279 280 281 } 282 cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){ 283 pStack->pushString(fullPath.c_str()); 284 pStack->pushInt(i+1); 285 pStack->pushInt(global_info.number_entry); 286 pStack->executeFunctionByHandler(h1, 3); 287 }); 288 289 cocos2d::unzCloseCurrentFile(zipFile); 290 291 if ((i+1) < global_info.number_entry) 292 { 293 if (cocos2d::unzGoToNextFile(zipFile) != UNZ_OK) 294 { 295 cocos2d::unzClose(zipFile); 296 return; 297 } 298 } 299 } 300 cocos2d::Director::getInstance()->getScheduler()->performFunctionInCocosThread([=](){ 301 pStack->executeFunctionByHandler(h2, 0); 302 }); 303 304 cocos2d::unzClose(zipFile); 305 return; 306 } 307 308 const luaL_reg g_ircu_funcs[] = { 309 {"download", lua_fw_ircu_download}, 310 {"uncompress", lua_fw_ircu_uncompress}, 311 {NULL, NULL}, 312 }; 313 314 void 315 register_fw_ircu(lua_State* lua_state) { 316 luaL_register(lua_state, "fw.ircu_helper", g_ircu_funcs); 317 } 318 }
好吧,就到这里了.下面来说一下log. 之前我也写过log模块,但是没有文件记录模块,我这边想要的功能就是同时在终端显示,然后又可以写入到文件.好了,从这个需求出发,我到Github去浏览了一下lua的代码,最后发现了zengrong的仓库, 这里给出他的地址: github.com/zengrong.所以我就从他的log模块取出了部分修改了一下:
1 --小岩<[email protected]> 2 --2015-5-28 1:37 3 local l = class("logger") 4 5 l.CRITICAL = 50 6 l.ERROR = 40 7 l.WARNING = 30 8 l.INFO = 20 9 l.DEBUG = 10 10 l.NOSET = 0 11 12 function l:ctor(level, ...) 13 self:set_limit_level(level or l.NOSET) 14 self.chanels_ = {...} 15 end 16 17 function l:set_limit_level(ll) 18 self.ll_ = ll 19 end 20 21 function l:get_limit_level() 22 return self.ll_ 23 end 24 25 function l:add_log_chanel(lc) 26 self.chanels_[#self.chanels_ + 1] = lc 27 end 28 29 function l:clear_chanel() 30 self.chanels_ = {} 31 end 32 33 function l:flush() 34 for __, lc in pairs(self.chanels_) do 35 lc:flush() 36 end 37 end 38 39 function l:log(level, fmt, ...) 40 if level < self.ll_ then 41 return 42 end 43 args = {...} 44 for __, lc in pairs(self.chanels_) do 45 lc:emit(level, fmt, args) 46 end 47 end 48 49 function l:critical(fmt, ...) 50 self:log(l.CRITICAL, fmt, ...) 51 end 52 53 function l:error(fmt, ...) 54 self:log(l.ERROR, fmt, ...) 55 end 56 57 function l:warning(fmt, ...) 58 self:log(l.WARNING, fmt, ...) 59 end 60 61 function l:info(fmt, ...) 62 self:log(l.INFO, fmt, ...) 63 end 64 65 function l:debug(fmt, ...) 66 self:log(l.DEBUG, fmt, ...) 67 end 68 69 return l
我这边只是给出了logger的接口,如果需要的话,请直接去看zr的github.请原谅我并没有在源码中给出引用出处.下面就是app配置Logger的文件:
1 luagarbage_setpause_val = 100, 2 luagarbage_setstepmul_val = 5000, 3 4 app_name = "firework 小岩<[email protected]>", 5 6 gl_win_size = cc.rect(0,0,960, 640), 7 gl_frame_size = cc.size(1140, 650), 8 9 dr_size = cc.size(480, 320), 10 dr_policy = cc.ResolutionPolicy.FIXED_HEIGHT, 11 12 show_status = false, 13 14 animation_interval = 1/60, 15 16 app = "game.application", 17 18 syslog_enabled = true, 19 syslog_console_enabled = true, 20 syslog_console_printfun = function(str) 21 print(">> syslog " .. str) 22 end, 23 syslog_limit_level = fw.logger.NOSET, 24 25 syslog_file_enabled = true, 26 syslog_file_autoflush = true, 27 syslog_file_parser = function(str) 28 return fw.encrypt.encrypt(str .. ‘\n‘) 29 end, 30 syslog_file_dir = fw.platform.get_writable_path() .. "log", 31 syslog_file_path= fw.platform.get_writable_path() .. "log/sys_log",
当然,这份是app配置文件,可以看到在logger写入函数我采用了xxtea加密方式写入,如果在开发期,可以屏蔽掉加密.直接写入就好了.当然也可以不写.好了,就到这里了.
这里是可以添加测试用例的,方法就是修改其中的app属性配置,从app.lua继承实现一个自己的test_application,然后修改配置文件,启动的时候就切换了入口.看需求添加吧,如果是有独立封装的ui类库以及其他独立功能可以考虑添加测试用例.又或者是需要添加工具,也可以在这里做考虑。
这次的文章篇幅比较的长,希望大家耐心点看. 终于我算是完成了自己的承诺了. 嘻嘻嘻嘻嘻~