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);
}
}
}