633 lines
21 KiB
C#
633 lines
21 KiB
C#
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)分段更新:在分段更新模块做资源转移
|
||
/// b)patch更新:在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);
|
||
}
|
||
}
|
||
}
|