Files
2025-01-25 04:38:09 +08:00

633 lines
21 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
{
/// <summary>
/// 更新流程管理器
/// 注对应IOS有几点需要注意
///1. 当包内有分段资源,启动时不做资源转移
///2. 当BaseVersion=0时强制转移资源
///3. 当有更新时无论分段更新还是patch都要先做转移资源
/// a分段更新在分段更新模块做资源转移
/// bpatch更新在RemoteVersion模块做资源转移
/// </summary>
public partial class UpdateManager : Singleton<UpdateManager>
{
//测试下载异常
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<BaseFlow> _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; }
/// <summary>
/// 初始化,重复调用只生效一次
/// </summary>
/// <param name="storedLocalXmlPath">存储在指定资源存放位置的LocalVersion.xml的绝对路径</param>
/// <param name="inAppLocalXmlPath">存储在app包里面的LocalVersion.xml的绝对路径</param>
/// <param name="storePath">指定存储资源的根路径</param>
/// <param name="platformType">平台类型Android/IOS/Windows</param>
/// <param name="halfCpuCoreCount">CPU核心数的一半作为线程个数</param>
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<BaseFlow>();
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;
}
/// <summary>
/// 日志回调注册
/// </summary>
/// <param name="log1"></param>
/// <param name="log2"></param>
/// <param name="log3"></param>
public void RegisterLog(DefaultLog log1, WarnLog log2, ErrorLog log3)
{
UpdateLog.RegisterLogCallback(log1, log2, log3);
}
/// <summary>
/// 测试流程要用到的数据
/// </summary>
/// <param name="imeiOrMacOrIdfa">imei、mac地址、idfa</param>
/// <param name="ip">ip地址基本没用了</param>
public void SetImeiOrMacOrIdfa(string imeiOrMacOrIdfa)
{
UpdateLog.WARN_LOG("UpdateManager ---> SetImeiOrMacOrIdfa() ");
if (this._initialized)
{
this.FlowInstance<Flow3RemoteXml>().SetExternalData(imeiOrMacOrIdfa, "");
}
}
/// <summary>
/// 每个流程开始时调用,用来记录流程进程
/// </summary>
/// <param name="callback"></param>
public void SetOnFlowBeginCallback(ActionCall callback)
{
if (!_initialized) return;
convertActionCall(callback);
BaseFlow.SetPerFlowActionCallback(onActionCall);
}
/// <summary>
/// 设置转移资源是要用到的数据
/// </summary>
/// <param name="inAppClientVersion">包内app版本</param>
/// <param name="inAppBaseVersion">包内分段版本</param>
/// <param name="appPath">app安装路径ios是var目录apk是/data/data目录</param>
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<Flow1TransResource>().SetExternalData(appPath, _platformType, onTransResourceFinishCallback);
if (_enableObb)
FlowInstance<Flow1TransResourceExOBB>().SetExternalData(_storePath, _platformType, onTransResourceFinishCallback);
}
/// <summary>
/// 设置备份的cdn列表当前cdn下载失败后自动转到下一个cdn下载
/// </summary>
/// <param name="backupCdnArray"></param>
public void SetBackupCdn(string[] backupCdnArray)
{
//Thousandto.Update.Download.Download.BackupCdn = backupCdnArray;
}
/// <summary>
/// 设置场景文件关联的资源列表的配置文件路径SceneConfig
/// </summary>
/// <param name="path"></param>
public void SetSceneReferenceResConfigPath(string dir)
{
_sceneConfigPath = dir;
}
/// <summary>
/// 客户端下载完成后的回调安卓是直接安装IOS是打开网页
/// 自定义下载客户端的方法ios不下载windows不下载一般设为null
/// </summary>
/// <param name="func1"></param>
public void SetClientDownClientFunc(ClientDownloadFinishCallback onFinish, CustomDownClientFunc customFunc = null)
{
//if (!_initialized) return;
//convertFuncClientDownloadFinishCallback(onFinish);
//FlowInstance<Flow4DownloadClient>().SetExternalData(onClientDownloadFinishCallback, customFunc, _platformType == PlatformType.IOS);
}
/// <summary>
/// 有资源需要下载时,要提示给用户,这个函数是通知弹出提示框用的
/// </summary>
/// <param name="func"></param>
public void SetDownloadNoticeFunc(DownloadNoticeCall func)
{
if (!_initialized) return;
convertFuncDownloadNoticeCall(func);
BaseFlow.SetDownloadNoticeCallback(onDownloadNoticeCall);
}
/// <summary>
/// 流程结束的回调
/// </summary>
/// <param name="func"></param>
public void SetFinishCallback(FinishCallback func)
{
if (!_initialized) return;
convertFuncFinishCallback(func);
_onFinish = onFinishCallback;
FlowInstance<FlowFinish>().SetExternalData(onFinishCallback);
}
/// <summary>
/// 语言
/// </summary>
/// <returns></returns>
public string GetLanguage()
{
return FlowInstance<Flow2LocalXml>().LocalXml.Developer;
}
/// <summary>
/// 获取一个服务器地址,这个地址用于上传日志信息
/// </summary>
/// <returns></returns>
public string GetUploadServerURL()
{
return FlowInstance<Flow2LocalXml>().LocalXml.UploadServerURL;
}
/// <summary>
/// 获取安装目录apk路径/ios的Raw目录
/// </summary>
/// <returns></returns>
public string GetInstallPath()
{
return _appPath;
}
/// <summary>
/// 设置预加载信息,用来显示进度
/// </summary>
/// <param name="total"></param>
/// <param name="loadedCount"></param>
public void SetPreloadTotal(int total)
{
UpdateLog.WARN_LOG("start preload+++++++++++++");
_showPreloadRes = true;
_totalPreloadRes = total;
}
/// <summary>
/// 已经预加载好的数量
/// </summary>
/// <param name="loaded"></param>
public void SetPreloadedCount(int loaded)
{
_loadedRes = loaded;
}
/// <summary>
/// 获取预加载进度信息在UIUpdateForm使用
/// </summary>
/// <param name="total"></param>
/// <param name="loaded"></param>
/// <returns></returns>
public bool ShowPreloadPregress()
{
return _showPreloadRes;
}
public int GetPreloadTotal()
{
return _totalPreloadRes;
}
public int GetPreloadedCount()
{
return _loadedRes;
}
public PlatformType GetPlatformType()
{
return _platformType;
}
/// <summary>
/// 获取本地版本信息
/// </summary>
/// <param name="appVer">本地app版本</param>
/// <param name="resVer">本地资源版本</param>
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;
}
}
/// <summary>
/// 远端最新app版本
/// </summary>
/// <returns></returns>
public string GetRemoteAppVersion()
{
if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null)
{
return CurrentFlow.CurrentRemoteData.AppVersion;
}
return string.Empty;
}
/// <summary>
/// 远端最新资源版本
/// </summary>
/// <returns></returns>
public string GetRemoteResVersion()
{
if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null)
{
return CurrentFlow.CurrentRemoteData.PatchVersion;
}
return string.Empty;
}
/// <summary>
/// 更新流程是否正在运行
/// </summary>
/// <returns></returns>
public bool Running()
{
return !_threadFinish;
}
/// <summary>
/// 中断更新流程
/// </summary>
public void AbortFlows()
{
_abortFlows = true;
if (CurrentFlow != null)
{
CurrentFlow.Abort();
UpdateLog.WARN_LOG("中断流程 " + CurrentFlow.FlowName());
}
Thousandto.Update.Download.BackDownload.AbortAll(null);
}
public T FlowInstance<T>() 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);
}
/// <summary>
/// 开始走更新流程
/// </summary>
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;
}
/// <summary>
/// 在中断所有流程后重新开始
/// </summary>
public void Restart()
{
if (_enableObb)
FlowInstance<Flow1TransResourceExOBB>().ReInitTransData();
//重置转移资源的数据
var transResFlow = FlowInstance<Flow1TransResource>();
if (transResFlow == null)
{
StartUpdate();
}
else
{
transResFlow.ReInitTransData();
AbortFlows();
if (Running())
{
_restart = true;
}
else
StartUpdate();
}
}
/// <summary>
/// 是否已经转移过资源了可以用此来判断资源读取路径是从app包里还是转移出去的资源路径
/// </summary>
/// <returns></returns>
public bool HasTransedRes()
{
return BaseFlow.HasTransedResource();
}
/// <summary>
/// 获取客户端下载地址在Flow3后才能获取
/// </summary>
/// <returns></returns>
public string GetClientUrl()
{
if (CurrentFlow != null && CurrentFlow.CurrentRemoteData != null)
return CurrentFlow.CurrentRemoteData.ClientUrl;
return "";
}
/// <summary>
/// 返回apk存放位置仅安卓平台可用
/// </summary>
/// <returns></returns>
public string GetApkPath()
{
if (CurrentFlow != null)
return CurrentFlow.ApkStorePath;
return "";
}
/// <summary>
/// 获取下载信息
/// </summary>
/// <param name="url"></param>
/// <param name="total"></param>
/// <param name="progressValue"></param>
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);
}
/// <summary>
/// 流程类型
/// </summary>
/// <returns></returns>
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();
}
/// <summary>
/// 在线程中执行更新流程
/// </summary>
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<Flow1TransResource>().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<FlowFinish>().FinishWithError(resultCode);
}
_threadFinish = true;
//重新开启
if (_restart)
{
UpdateLog.DEBUG_LOG("Restart update");
StartUpdate();
}
else
UpdateLog.DEBUG_LOG("Finish update flow!!! " + resultCode);
}
}
}