編輯:關於Android編程
第一次搞熱更新,這裡記錄一下全過程,方面以後查看。
原理:每次登陸游戲利用cocos的assetManager從服務器拉去當前最新的兩個文件。 一個是version.mainifest,一個project.mainifest. 這兩個文件都是xml的描述文件。一個包含了版本信息,第二個包含了游戲所有資源的MD5碼。首先通過version文件對比本地的版本是否相同,如果不相同,再通過跟本地的project文件對比MD5碼來判斷哪些文件需要重新下載,替換資源。
步驟:
1.首先要進行測試先下載一個HFS服務器,這個服務器方便易學。每次將最新項目全部拖進服務器中,用GeneateManifest.py文件自動生成version 和 project文件。
2.編寫Geneate文件。
這個文件是一個python。目的是生成對應的version和project文件。project文件可以幫你給每個資源生成獨一無二的MD5碼,相當於每個資源的標記。下面是一段python文件的代碼,非常簡單。
#!/usr/bin/env python
#coding:utf-8
import os
import json
import hashlib
import subprocess
assetsDir = {
"searchDir" : ["src", "res"],
"ignorDir" : ["cocos", "obj","version"]
}
versionConfigFile = "res/config/version_info.json" #版本信息的配置文件路徑
versionManifestPath = "res/version/version.manifest" #由此腳本生成的version.manifest文件路徑
projectManifestPath = "res/version/project.manifest" #由此腳本生成的project.manifest文件路徑
class SearchFile:
def __init__(self):
self.fileList = []
for k in assetsDir:
if (k == "searchDir"):
for searchdire in assetsDir[k]:
self.recursiveDir(searchdire)
def recursiveDir(self, srcPath):
''' 遞歸指定目錄下的所有文件'''
dirList = [] #所有文件夾
files = os.listdir(srcPath) #返回指定目錄下的所有文件,及目錄(不含子目錄)
for f in files:
#目錄的處理
if (os.path.isdir(srcPath + '/' + f)):
if (f[0] == '.' or (f in assetsDir["ignorDir"])):
#排除隱藏文件夾和忽略的目錄
pass
else:
#添加非需要的文件夾
dirList.append(f)
#文件的處理
elif (os.path.isfile(srcPath + '/' + f)):
self.fileList.append(srcPath + '/' + f) #添加文件
#遍歷所有子目錄,並遞歸
for dire in dirList:
#遞歸目錄下的文件
self.recursiveDir(srcPath + '/' + dire)
def getAllFile(self):
''' get all file path'''
return tuple(self.fileList)
def GetSvnCurrentVersion():
popen = subprocess.Popen(['svn', 'info'], stdout = subprocess.PIPE)
while True:
next_line = popen.stdout.readline()
if next_line == '' and popen.poll() != None:
break
valList = next_line.split(':')
if len(valList)<2:
continue
valList[0] = valList[0].strip().lstrip().rstrip(' ')
valList[1] = valList[1].strip().lstrip().rstrip(' ')
if(valList[0]=="Revision"):
return valList[1]
return ""
def CalcMD5(filepath):
"""generate a md5 code by a file path"""
with open(filepath,'rb') as f:
md5obj = hashlib.md5()
md5obj.update(f.read())
return md5obj.hexdigest()
def getVersionInfo():
'''get version config data'''
configFile = open(versionConfigFile,"r")
json_data = json.load(configFile)
configFile.close()
json_data["version"] = json_data["version"] + '.' + str(GetSvnCurrentVersion())
return json_data
def GenerateversionManifestPath():
''' 生成大版本的version.manifest'''
json_str = json.dumps(getVersionInfo(), indent = 2)
fo = open(versionManifestPath,"w")
fo.write(json_str)
fo.close()
def GenerateprojectManifestPath():
searchfile = SearchFile()
fileList = list(searchfile.getAllFile())
project_str = {}
project_str.update(getVersionInfo())
dataDic = {}
for f in fileList:
dataDic[f] = {"md5" : CalcMD5(f)}
project_str.update({"assets":dataDic})
json_str = json.dumps(project_str, sort_keys = True, indent = 2)
fo = open(projectManifestPath,"w")
fo.write(json_str)
fo.close()
if __name__ == "__main__":
GenerateversionManifestPath()
GenerateprojectManifestPath()
3.利用cocos assetManager來從服務器獲取文件並且進行資源的替換(這裡所謂的替換並不是真正的替換,利用了Fileutils->searchPath()設置資源文件讀取的優先級。也就是老資源和代碼並沒有刪除,而是捨棄不用。)
--region *.lua
--Date
--此文件由[BabeLua]插件自動生成
local AssetsManager = class("AssetsManager",function ()
return cc.LayerColor:create(cc.c4b(20, 20, 20, 220))
end)
function AssetsManager:ctor()
self:onNodeEvent("exit", handler(self, self.onExitCallback))
self:initUI()
self:setAssetsManage()
end
function AssetsManager:onExitCallback()
self.assetsManagerEx:release()
end
function AssetsManager:initUI()
local hintLabel = cc.Label:createWithTTF("正在更新...", CONFIG.TTF_FONT_2, 20)
:addTo(self)
:move(600, 80)
local progressBg = display.newSprite("sprites/hyd_progress_bg.png")
:addTo(self)
:move(600, 40)
self.progress = cc.ProgressTimer:create(display.newSprite("sprites/hyd_progress.png"))
:addTo(progressBg)
:move(380, 19)
self.progress:setType(cc.PROGRESS_TIMER_TYPE_BAR)
self.progress:setBarChangeRate(cc.p(1, 0))
self.progress:setMidpoint(cc.p(0.0, 0.5))
self.progress:setPercentage(0)
--觸摸吞噬
self.listener = cc.EventListenerTouchOneByOne:create()
self.listener:setSwallowTouches(true)
local onTouchBegan = function (touch, event)
return true
end
self.listener:registerScriptHandler(onTouchBegan, cc.Handler.EVENT_TOUCH_BEGAN)
cc.Director:getInstance():getEventDispatcher():addEventListenerWithSceneGraphPriority(self.listener, self)
end
function AssetsManager:setAssetsManage()
--創建可寫目錄與設置搜索路徑
local storagePath = cc.FileUtils:getInstance():getWritablePath() .. "NewRes/"
local resPath = storagePath.. '/res/'
local srcPath = storagePath.. '/src/'
if not (cc.FileUtils:getInstance():isDirectoryExist(storagePath)) then
cc.FileUtils:getInstance():createDirectory(storagePath)
cc.FileUtils:getInstance():createDirectory(resPath)
cc.FileUtils:getInstance():createDirectory(srcPath)
end
local searchPaths = cc.FileUtils:getInstance():getSearchPaths()
table.insert(searchPaths, 1, storagePath)
table.insert(searchPaths, 2, resPath)
table.insert(searchPaths, 3, srcPath)
cc.FileUtils:getInstance():setSearchPaths(searchPaths)
self.assetsManagerEx = cc.AssetsManagerEx:create("version/project.manifest", storagePath)
self.assetsManagerEx:retain()
local eventListenerAssetsManagerEx = cc.EventListenerAssetsManagerEx:create(self.assetsManagerEx,
function (event)
self:handleAssetsManagerEvent(event)
end)
local dispatcher = cc.Director:getInstance():getEventDispatcher()
dispatcher:addEventListenerWithFixedPriority(eventListenerAssetsManagerEx, 1)
--檢查版本並升級
self.assetsManagerEx:update()
end
function AssetsManager:handleAssetsManagerEvent(event)
local eventCodeList = cc.EventAssetsManagerEx.EventCode
local eventCodeHand = {
[eventCodeList.ERROR_NO_LOCAL_MANIFEST] = function ()
print("發生錯誤:本地資源清單文件未找到")
end,
[eventCodeList.ERROR_DOWNLOAD_MANIFEST] = function ()
print("發生錯誤:遠程資源清單文件下載失敗") --資源服務器沒有打開,
self:downloadManifestError()
end,
[eventCodeList.ERROR_PARSE_MANIFEST] = function ()
print("發生錯誤:資源清單文件解析失敗")
end,
[eventCodeList.NEW_VERSION_FOUND] = function ()
print("發現找到新版本")
end,
[eventCodeList.ALREADY_UP_TO_DATE] = function ()
print("已經更新到服務器最新版本")
self:updateFinished()
end,
[eventCodeList.UPDATE_PROGRESSION]= function ()
print("更新過程的進度事件")
self.progress:setPercentage(event:getPercentByFile())
end,
[eventCodeList.ASSET_UPDATED] = function ()
print("單個資源被更新事件")
end,
[eventCodeList.ERROR_UPDATING] = function ()
print("發生錯誤:更新過程中遇到錯誤")
end,
[eventCodeList.UPDATE_FINISHED] = function ()
print("更新成功事件")
self:updateFinished()
end,
[eventCodeList.UPDATE_FAILED] = function ()
print("更新失敗事件")
end,
[eventCodeList.ERROR_DECOMPRESS] = function ()
print("解壓縮失敗")
end
}
local eventCode = event:getEventCode()
if eventCodeHand[eventCode] ~= nil then
eventCodeHand[eventCode]()
end
end
function AssetsManager:updateFinished()
self:setVisible(false)
self.listener:setEnabled(false)
end
function AssetsManager:downloadManifestError()
self:setVisible(false)
self.listener:setEnabled(false)
end
return AssetsManager
--endregion
4.遇到的問題: 測試的時候服務器設置的IP輸出總是不對,跟自己設置的IP有出入!原因是自己只設置了本地的IP地址,但是服務器的IP地址沒有更改。 按照正常邏輯,按照本地提供的IP地址訪問服務器成功並且下載了version和project文件。 發現需要跟新的時候,這個時候是按照下載下來的project提供的IP地址去下載資源。由於服務器projec的IP根本都是錯誤的。 會導致資源下載失敗!
現在的智能手機不敢說百分百的都是觸摸屏,也應該是百分之九九以上為觸摸屏了,觸摸屏為我們操作無鍵盤、無鼠標的手機系統帶來了很多的便利。當用戶觸摸屏幕時會產生很多
上上周寫的一個demo,仿照網易一元奪寶的下拉刷新效果。原效果是(第一部分)一個小太陽拉下來,然後松開回彈上去,(第二部分)再掉下來一個硬幣進行中軸旋轉。本文實現的效果的
recyclerView是android5.0之後推出的一款新的View布局,功能相較於ListView有過之而無不及,相信在以後的學習和工作中都將可能會用上,這兩天自己
前言SQLite是一款輕型的數據庫,是遵守ACID的關系型數據庫管理系統,它包含在一個相對小的C庫中。它是D.RichardHipp建立的公有領域項目,設計目標是嵌入式的
(一)前言今天我們一起來看一下抽屜DrawerLayoutAndroid