using System.Collections.Generic; using System.IO; using System.Threading; using Thousandto.Update.Data; using Thousandto.Update.Delegate; using Thousandto.Update.Enum; using Thousandto.Update.Flow; using Thousandto.Update.Log; using Thousandto.Update.Singleton; using AppManager = UnityEngine.Gonbest.MagicCube.AppManager; namespace Thousandto.Update.Manager { /// /// 更新流程管理器 /// 注:对应IOS有几点需要注意 ///1. 当包内有分段资源,启动时不做资源转移 ///2. 当BaseVersion=0时,强制转移资源 ///3. 当有更新时,无论分段更新还是patch,都要先做转移资源 /// a)分段更新:在分段更新模块做资源转移 /// b)patch更新:在RemoteVersion模块做资源转移 /// public partial class UpdateManager : Singleton { //测试下载异常 public static bool TestDownloadException = false; //保存下载文件的日志 public static bool SAVA_DOWNLOAD_FILE_LOG = true; public static int CPUCORES = 4; private PlatformType _platformType; private string _localXmlPath; private string _storePath; private string _appPath; #region //预加载资源,显示进度用的 //开启预加载,显示进度用 private bool _showPreloadRes; //总共需要预加载的资源数 private int _totalPreloadRes; //已经加载好的资源数 private int _loadedRes; #endregion //是否初始化 private bool _initialized; //流程是否结束,仅用来判断线程结束 private bool _threadFinish = true; //重新开始,为true则在所有流程结束的时候重新开启整个流程 private bool _restart; private bool _enableObb = false; //流程列表 private List _flowList; //流程结束调用 private FinishCallback _onFinish; //中断流程 private bool _abortFlows; //场景资源列表配置文件路径 private string _sceneConfigPath; public string StorePath { get { return _storePath; } } public BaseFlow CurrentFlow; //包体内是否存在FileList.txt文件 public bool IsExistFileList { get; private set; } /// /// 初始化,重复调用只生效一次 /// /// 存储在指定资源存放位置的LocalVersion.xml的绝对路径 /// 存储在app包里面的LocalVersion.xml的绝对路径 /// 指定存储资源的根路径 /// 平台类型:Android/IOS/Windows /// CPU核心数的一半作为线程个数 public void Initialize(string storedLocalXmlPath, string inAppLocalXmlPath, string appPath, string storePath, PlatformType ptype, bool enableObb = false, int halfCpuCoreCount = 4) { CPUCORES = halfCpuCoreCount; if (this._initialized) { UpdateLog.WARN_LOG("_initialized: " + true); } else { UpdateLog.DEBUG_LOG(string.Format("Update Initialize: storedLocalXmlPath = {0}\n inAppLocalXmlPath ={1}\n appPath={2}\n storePath={3}\n needTransResource={4}", storedLocalXmlPath, inAppLocalXmlPath, appPath, storePath, (ptype== PlatformType.Android))); this._threadFinish = true; this._localXmlPath = storedLocalXmlPath; this._storePath = storePath; this._appPath = appPath; _enableObb = enableObb; this._platformType = ptype; this._flowList = new List(); if (_enableObb) { this._flowList.Add(new Flow1TransResourceExOBB()); } this._flowList.Add(new Flow1TransResource()); this._flowList.Add(new Flow2LocalXml()); this._flowList.Add(new Flow3RemoteXml()); this._flowList.Add(new Flow7ExDownloadMapFile()); this._flowList.Add(new Flow8ExCheckResource()); this._flowList.Add(new Flow9RepairResource()); this._flowList.Add(new FlowFinish()); this._initialized = true; BaseFlow.SetPath(storedLocalXmlPath, inAppLocalXmlPath, storePath, appPath); InitBackDownload(halfCpuCoreCount); Recorder.StepRecorder.Initialize(); } } public void Unitialize() { AbortFlows(); _initialized = false; if (_flowList != null) { _flowList.Clear(); } _onFinish = null; } /// /// 日志回调注册 /// /// /// /// public void RegisterLog(DefaultLog log1, WarnLog log2, ErrorLog log3) { UpdateLog.RegisterLogCallback(log1, log2, log3); } /// /// 测试流程要用到的数据 /// /// imei、mac地址、idfa /// ip地址,基本没用了 public void SetImeiOrMacOrIdfa(string imeiOrMacOrIdfa) { UpdateLog.WARN_LOG("UpdateManager ---> SetImeiOrMacOrIdfa() "); if (this._initialized) { this.FlowInstance().SetExternalData(imeiOrMacOrIdfa, ""); } } /// /// 每个流程开始时调用,用来记录流程进程 /// /// public void SetOnFlowBeginCallback(ActionCall callback) { if (!_initialized) return; convertActionCall(callback); BaseFlow.SetPerFlowActionCallback(onActionCall); } /// /// 设置转移资源是要用到的数据 /// /// 包内app版本 /// 包内分段版本 /// app安装路径,ios是var目录,apk是/data/data目录 public void SetTransData(string inAppClientVersion, string inAppBaseVersion, string appPath, TransResourceFinishCallback callback) { UpdateLog.DEBUG_LOG(string.Format("SetTransData: inAppClientVersion={0} inAppBaseVersion={1} appPath={2}", inAppClientVersion, inAppBaseVersion, appPath)); if (!_initialized) return; BaseFlow.SetInAppVer(inAppClientVersion, inAppBaseVersion); convertFuncTransResourceFinishCallback(callback); FlowInstance().SetExternalData(appPath, _platformType, onTransResourceFinishCallback); if (_enableObb) FlowInstance().SetExternalData(_storePath, _platformType, onTransResourceFinishCallback); } /// /// 设置备份的cdn列表,当前cdn下载失败后,自动转到下一个cdn下载 /// /// public void SetBackupCdn(string[] backupCdnArray) { //Thousandto.Update.Download.Download.BackupCdn = backupCdnArray; } /// /// 设置场景文件关联的资源列表的配置文件路径:SceneConfig /// /// public void SetSceneReferenceResConfigPath(string dir) { _sceneConfigPath = dir; } /// /// 客户端下载完成后的回调:安卓是直接安装,IOS是打开网页 /// 自定义下载客户端的方法,ios不下载,windows不下载,一般设为null /// /// public void SetClientDownClientFunc(ClientDownloadFinishCallback onFinish, CustomDownClientFunc customFunc = null) { //if (!_initialized) return; //convertFuncClientDownloadFinishCallback(onFinish); //FlowInstance().SetExternalData(onClientDownloadFinishCallback, customFunc, _platformType == PlatformType.IOS); } /// /// 有资源需要下载时,要提示给用户,这个函数是通知弹出提示框用的 /// /// public void SetDownloadNoticeFunc(DownloadNoticeCall func) { if (!_initialized) return; convertFuncDownloadNoticeCall(func); BaseFlow.SetDownloadNoticeCallback(onDownloadNoticeCall); } /// /// 流程结束的回调 /// /// public void SetFinishCallback(FinishCallback func) { if (!_initialized) return; convertFuncFinishCallback(func); _onFinish = onFinishCallback; FlowInstance().SetExternalData(onFinishCallback); } /// /// 语言 /// /// public string GetLanguage() { return FlowInstance().LocalXml.Developer; } /// /// 获取一个服务器地址,这个地址用于上传日志信息 /// /// public string GetUploadServerURL() { return FlowInstance().LocalXml.UploadServerURL; } /// /// 获取安装目录:apk路径/ios的Raw目录 /// /// public string GetInstallPath() { return _appPath; } /// /// 设置预加载信息,用来显示进度 /// /// /// public void SetPreloadTotal(int total) { UpdateLog.WARN_LOG("start preload+++++++++++++"); _showPreloadRes = true; _totalPreloadRes = total; } /// /// 已经预加载好的数量 /// /// public void SetPreloadedCount(int loaded) { _loadedRes = loaded; } /// /// 获取预加载进度信息,在UIUpdateForm使用 /// /// /// /// public bool ShowPreloadPregress() { return _showPreloadRes; } public int GetPreloadTotal() { return _totalPreloadRes; } public int GetPreloadedCount() { return _loadedRes; } public PlatformType GetPlatformType() { return _platformType; } /// /// 获取本地版本信息 /// /// 本地app版本 /// 本地资源版本 public void GetVersionInfo(out string appVer, out string resVer) { appVer = ""; resVer = ""; if (CurrentFlow != null && CurrentFlow.LocalXml != null) { appVer = CurrentFlow.LocalXml.LocalAppVersion; resVer = CurrentFlow.LocalXml.PatchResVersion; } } /// /// 远端最新app版本 /// /// public string GetRemoteAppVersion() { if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null) { return CurrentFlow.CurrentRemoteData.AppVersion; } return string.Empty; } /// /// 远端最新资源版本 /// /// public string GetRemoteResVersion() { if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null) { return CurrentFlow.CurrentRemoteData.PatchVersion; } return string.Empty; } /// /// 更新流程是否正在运行 /// /// public bool Running() { return !_threadFinish; } /// /// 中断更新流程 /// public void AbortFlows() { _abortFlows = true; if (CurrentFlow != null) { CurrentFlow.Abort(); UpdateLog.WARN_LOG("中断流程 " + CurrentFlow.FlowName()); } Thousandto.Update.Download.BackDownload.AbortAll(null); } public T FlowInstance() where T : BaseFlow { if (_flowList != null && _flowList.Count > 0) { for (int i = 0; i < _flowList.Count; ++i) { if (_flowList[i] is T) { return (T)_flowList[i]; } } } return default(T); } /// /// 开始走更新流程 /// public void StartUpdate() { //未初始化 if (!_initialized) return; //未完成 if (!_threadFinish) return; //需要先调用一次,避免UI线程的函数在子线程中调用了 Update(); _threadFinish = false; _restart = false; Thread thread = new Thread(updateFlowByThread); thread.Start(); thread.IsBackground = true; } /// /// 在中断所有流程后重新开始 /// public void Restart() { if (_enableObb) FlowInstance().ReInitTransData(); //重置转移资源的数据 var transResFlow = FlowInstance(); if (transResFlow == null) { StartUpdate(); } else { transResFlow.ReInitTransData(); AbortFlows(); if (Running()) { _restart = true; } else StartUpdate(); } } /// /// 是否已经转移过资源了,可以用此来判断资源读取路径是从app包里还是转移出去的资源路径 /// /// public bool HasTransedRes() { return BaseFlow.HasTransedResource(); } /// /// 获取客户端下载地址,在Flow3后才能获取 /// /// public string GetClientUrl() { if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null) return CurrentFlow.CurrentRemoteData.ClientUrl; return ""; } /// /// 返回apk存放位置,仅安卓平台可用 /// /// public string GetApkPath() { if (CurrentFlow != null) return CurrentFlow.ApkStorePath; return ""; } /// /// 获取下载信息 /// /// /// /// public void GetDownloadInfo(out string url, out int total, out int progressValue, out bool isDownloadProgress) { isDownloadProgress = CurrentFlow.UseDownload; CurrentFlow.GetCurDownInfo(out url, out total, out progressValue); } /// /// 流程类型 /// /// public FlowEnum GetCurFlowType() { var flowName = CurrentFlow.FlowName(); if (System.Enum.IsDefined(typeof(FlowEnum), flowName)) { return (FlowEnum)System.Enum.Parse(typeof(FlowEnum), flowName); } return CurrentFlow.FlowType; } public bool TryGetInAppData(string relativePath, out string md5, out int size) { md5 = null; size = 0; var info = AppManager.Instance.GetAssetInfo(relativePath); if (info != null) { md5 = info.MD5; size = info.Size; return true; } return false; } //删除之前释放出去的资源 internal void DelelteReleasedFiles() { RepairRecordFileData recordData = new RepairRecordFileData(); recordData.Read(); var e = AppManager.Instance.AssetData.GetEnumerator(); try { while (e.MoveNext()) { var savePath = _storePath + e.Current.Key; if (File.Exists(savePath)) { File.Delete(savePath); recordData.IncRepairCount(e.Current.Key); } } } finally { e.Dispose(); } recordData.Write(); } /// /// 在线程中执行更新流程 /// private void updateFlowByThread() { bool continueFlow = false; CurrentFlow = null; _abortFlows = false; int resultCode = CodeDefine.RET_SUCCESS; int runFlowCount = 0; while (true && !_abortFlows) { BaseFlow oldFlow = null; continueFlow = false; runFlowCount = 0; for (int i = 0; !_abortFlows && i < _flowList.Count; ++i) { runFlowCount++; CurrentFlow = _flowList[i]; UpdateLog.DEBUG_LOG(CurrentFlow.FlowName()); CurrentFlow.OnEnter(oldFlow); resultCode = CurrentFlow.Work(); CurrentFlow.OnLeave(resultCode); //更新客户端,跳过所有流程 if (resultCode == CodeDefine.RET_SKIP_BY_DOWNLOAD_APP) { UpdateLog.DEBUG_LOG("Download Client finish, skip all left flows!!!"); break; } //需要强制释放资源,重新走更新流程 if (resultCode == CodeDefine.RET_SKIP_BY_FORCE_TRANS_RESOURCE) { FlowInstance().SetForceUnzip(); continueFlow = true; break; } //中断操作 if (resultCode == CodeDefine.RET_SKIP_BY_ABORT) { UpdateLog.DEBUG_LOG("Abort flow -> " + CurrentFlow.FlowName()); break; } if (resultCode == CodeDefine.RET_SKIP_BY_DISABLEDOWNLOAD) { UpdateLog.DEBUG_LOG("Not support download, skip all flows!!!"); break; } //取消操作 if (resultCode == CodeDefine.RET_SKIP_BY_CANCEL) { UpdateLog.DEBUG_LOG("Skip flow by cancel download option, exit game!!!"); break; } if (resultCode < CodeDefine.RET_SUCCESS) { break; } oldFlow = CurrentFlow; } if (!continueFlow) break; } if (runFlowCount != _flowList.Count) { FlowInstance().FinishWithError(resultCode); } _threadFinish = true; //重新开启 if (_restart) { UpdateLog.DEBUG_LOG("Restart update"); StartUpdate(); } else UpdateLog.DEBUG_LOG("Finish update flow!!! " + resultCode); } } }