在上一篇文章中,说到了"流程"的由来,以及我对流程的使用. 这一片就是对流程的应用.前一篇文章中说到了三条流程 check_log_measure, check_env_measure, check_update_measure.先来看看chenck_log_measure的源码:
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的源码:
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和更新模块的相应参数.下面看一下更新流程的源码:
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模块,也就是我们的增量更新模块:
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模块取出了部分修改了一下:
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,
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类库以及其他独立功能可以考虑添加测试用例.又或者是需要添加工具,也可以在这里做考虑。
这次的文章篇幅比较的长,希望大家耐心点看. 终于我算是完成了自己的承诺了. 嘻嘻嘻嘻嘻~