Files
Main/Assets/Plugins/References/FuncellUpdate/UpdateModel/UpdateManager.cs

633 lines
21 KiB
C#
Raw Normal View History

2025-01-25 04:38:09 +08:00
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);
}
}
}