结合Unity官方机制和实际工程流程,详细讲解AssetBundle资源加载的原理、流程、常用API和注意事项,并结合视频播放器场景,给出一些实际建议。
1. AssetBundle是什么?
AssetBundle是Unity官方提供的资源打包与分发格式,可以将场景、Prefab、贴图、音频、视频、脚本对象等资源打包成二进制文件,便于按需加载、热更新、节省包体。
2. AssetBundle加载资源的基本流程
2.1 加载AssetBundle文件
AssetBundle文件可以来自本地(StreamingAssets、PersistentDataPath等)或远程服务器(通过URL下载)。
常用API:
AssetBundle.LoadFromFile(path)
:同步从本地磁盘加载AssetBundle.LoadFromFileAsync(path)
:异步从本地磁盘加载UnityWebRequestAssetBundle.GetAssetBundle(url)
:从网络下载并加载
示例:
// 本地同步加载
AssetBundle ab = AssetBundle.LoadFromFile("path/to/your.ab");
// 本地异步加载
AssetBundleCreateRequest req = AssetBundle.LoadFromFileAsync("path/to/your.ab");
yield return req;
AssetBundle ab = req.assetBundle;
// 网络异步加载
UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle("http://server/your.ab");
yield return www.SendWebRequest();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
2.2 加载AssetBundle中的资源
AssetBundle加载后,里面的资源还没有实例化,需要进一步加载。
常用API:
ab.LoadAsset<T>("assetName")
:同步加载ab.LoadAssetAsync<T>("assetName")
:异步加载ab.LoadAllAssets<T>()
:加载所有资源
示例:
// 同步加载
GameObject prefab = ab.LoadAsset<GameObject>("MyPrefab");
// 异步加载
AssetBundleRequest req = ab.LoadAssetAsync<GameObject>("MyPrefab");
yield return req;
GameObject prefab = req.asset as GameObject;
2.3 实例化资源
对于Prefab、Texture、AudioClip等,加载出来后可以直接用或实例化。
GameObject go = GameObject.Instantiate(prefab);
3. 视频播放器场景下的AssetBundle资源加载
3.1 视频文件打包进AssetBundle
- Unity支持将视频文件(如.mp4)作为
VideoClip
资源打包进AssetBundle。 - 也可以直接打包原始二进制文件(如.mp4),用
TextAsset
或StreamingAssets
方式加载。
3.2 加载VideoClip
AssetBundle ab = AssetBundle.LoadFromFile("path/to/video.ab");
VideoClip clip = ab.LoadAsset<VideoClip>("MyVideo");
videoPlayer.clip = clip;
videoPlayer.Play();
3.3 加载原始视频文件(如.mp4)
如果你用自定义播放器(如FFmpeg),可以把.mp4直接打包进AssetBundle,加载出来后写到本地临时目录,再传给Native层解码。
AssetBundle ab = AssetBundle.LoadFromFile("path/to/video.ab");
TextAsset videoAsset = ab.LoadAsset<TextAsset>("MyVideo.mp4");
string tempPath = Application.persistentDataPath + "/MyVideo.mp4";
File.WriteAllBytes(tempPath, videoAsset.bytes);
// 传 tempPath 给 C++ 层解码
4. 注意事项
- 依赖管理:AssetBundle有依赖关系,需提前加载依赖(如Shader、材质等)。
- 资源卸载:用完后需调用
ab.Unload(false)
或ab.Unload(true)
释放内存。 - 异步加载:推荐用异步API,避免主线程卡顿。
- 平台差异:AssetBundle需针对不同平台分别打包(Win/Android/iOS等不通用)。
- 路径管理:本地路径、网络路径、StreamingAssets、PersistentDataPath等要区分清楚。
5. 总结流程图
6. 典型伪代码(视频文件)
// 1. 加载AssetBundle
AssetBundle ab = AssetBundle.LoadFromFile("path/to/video.ab");
// 2. 加载视频资源
TextAsset videoAsset = ab.LoadAsset<TextAsset>("MyVideo.mp4");
// 3. 写到本地临时目录
string tempPath = Application.persistentDataPath + "/MyVideo.mp4";
File.WriteAllBytes(tempPath, videoAsset.bytes);
// 4. 传递给Native层解码
gvpPrepare(id, tempPath, decodeType);
// 5. 用完后卸载
ab.Unload(false);
下面我将继续深入,详细讲解AssetBundle资源加载的进阶用法、工程实践、常见问题与优化建议,并结合视频播放器的实际需求,给出更具体的方案。
1. AssetBundle资源加载的进阶用法
1.1 依赖关系的处理
AssetBundle之间可能存在依赖(如Prefab依赖材质、Shader等)。
正确加载顺序:
- 先加载依赖的AssetBundle(如Shader、材质等)
- 再加载主AssetBundle(如Prefab、VideoClip等)
依赖信息获取:
- Unity打包AssetBundle时会生成
AssetBundleManifest
,可用来查询依赖关系。
示例:
// 加载主Manifest
AssetBundle manifestBundle = AssetBundle.LoadFromFile("path/to/AssetBundles");
AssetBundleManifest manifest = manifestBundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
// 获取依赖
string[] dependencies = manifest.GetAllDependencies("mainbundle");
foreach (string dep in dependencies)
{
AssetBundle.LoadFromFile("path/to/" + dep);
}
AssetBundle mainAb = AssetBundle.LoadFromFile("path/to/mainbundle");
1.2 异步协程加载
推荐用协程异步加载,避免主线程卡顿。
IEnumerator LoadAssetBundleAsync(string abPath, string assetName)
{
AssetBundleCreateRequest abReq = AssetBundle.LoadFromFileAsync(abPath);
yield return abReq;
AssetBundle ab = abReq.assetBundle;
AssetBundleRequest assetReq = ab.LoadAssetAsync(assetName);
yield return assetReq;
UnityEngine.Object asset = assetReq.asset;
// 使用asset
}
1.3 网络下载与缓存
- 用
UnityWebRequestAssetBundle.GetAssetBundle(url)
下载远程AssetBundle。 - 可结合
Caching
系统,避免重复下载。
IEnumerator DownloadAndCacheAB(string url, string assetName)
{
UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(url);
yield return www.SendWebRequest();
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(www);
// 加载资源...
}
2. 视频播放器场景下的特殊处理
2.1 视频文件的二进制加载与本地缓存
- AssetBundle中的视频文件(如.mp4)通常以
TextAsset
或StreamingAssets
方式打包。 - 加载后需写到本地磁盘,因为大多数Native解码库(如FFmpeg)只支持文件路径,不支持内存流。
伪代码:
AssetBundle ab = AssetBundle.LoadFromFile("path/to/video.ab");
TextAsset videoAsset = ab.LoadAsset<TextAsset>("MyVideo.mp4");
string tempPath = Application.persistentDataPath + "/MyVideo.mp4";
if (!File.Exists(tempPath))
File.WriteAllBytes(tempPath, videoAsset.bytes);
// 传递tempPath给Native层
2.2 多视频并发加载
- 多个视频同时播放时,需为每个视频分配独立的临时文件路径,避免覆盖。
- 可用唯一ID或GUID命名。
string tempPath = Application.persistentDataPath + $"/MyVideo_{id}.mp4";
2.3 资源卸载与清理
- 播放结束后,及时调用
ab.Unload(false)
释放内存。 - 临时文件可在播放结束后删除,或定期清理。
3. 常见问题与优化建议
3.1 AssetBundle平台兼容性
- AssetBundle需针对不同平台分别打包(Win/Android/iOS等不通用)。
- 加载时要用
Application.platform
判断,选择正确的AssetBundle文件。
3.2 内存与IO优化
- 大文件(如视频)加载时,建议用异步方式,避免主线程阻塞。
- 写入本地磁盘时,注意IO性能,必要时可用后台线程。
3.3 资源热更新
- 通过服务器下发最新AssetBundle,实现资源热更新。
- 可结合版本号、MD5校验等机制,判断是否需要重新下载。
3.4 资源管理框架
- 工程中建议封装统一的AssetBundle管理器,负责加载、缓存、依赖、卸载等。
- 支持同步/异步、网络/本地、依赖自动加载等功能。
4. 典型工程实践(伪代码)
// 统一的AssetBundle加载接口
public class ABManager
{
Dictionary<string, AssetBundle> loadedBundles = new Dictionary<string, AssetBundle>();
public IEnumerator LoadAssetAsync<T>(string abPath, string assetName, Action<T> onLoaded) where T : UnityEngine.Object
{
if (!loadedBundles.ContainsKey(abPath))
{
AssetBundleCreateRequest abReq = AssetBundle.LoadFromFileAsync(abPath);
yield return abReq;
loadedBundles[abPath] = abReq.assetBundle;
}
AssetBundle ab = loadedBundles[abPath];
AssetBundleRequest assetReq = ab.LoadAssetAsync<T>(assetName);
yield return assetReq;
onLoaded?.Invoke(assetReq.asset as T);
}
public void Unload(string abPath)
{
if (loadedBundles.TryGetValue(abPath, out var ab))
{
ab.Unload(false);
loadedBundles.Remove(abPath);
}
}
}
5. 视频播放器与AssetBundle结合的完整流程
- 加载AssetBundle(本地或网络,异步)
- 加载视频文件资源(TextAsset或VideoClip)
- 写入本地临时文件(如.mp4)
- 传递本地路径给Native层解码
- 播放结束后卸载AssetBundle,清理临时文件
6. 总结
- AssetBundle是Unity资源热更新和动态加载的核心机制。
- 视频播放器场景下,通常将视频文件以二进制形式打包,加载后写到本地,供Native解码。
- 工程上需关注依赖管理、异步加载、平台兼容、内存与IO优化、资源清理等问题。
- 推荐封装统一的AssetBundle管理器,提升代码复用和健壮性。
下面我将继续深入,补充AssetBundle相关的实用工程细节、打包流程、依赖自动化、临时文件管理、与Addressables的对比,并给出视频播放器场景下的完整工程范例。
1. AssetBundle打包流程与自动化
1.1 基本打包脚本
在Unity编辑器下,可以用如下脚本自动化打包:
using UnityEditor;
using UnityEngine;
public class AssetBundleBuilder
{
[MenuItem("Tools/Build AssetBundles")]
public static void BuildAllAssetBundles()
{
string outputPath = "AssetBundles/" + EditorUserBuildSettings.activeBuildTarget;
if (!System.IO.Directory.Exists(outputPath))
System.IO.Directory.CreateDirectory(outputPath);
BuildPipeline.BuildAssetBundles(
outputPath,
BuildAssetBundleOptions.None,
EditorUserBuildSettings.activeBuildTarget
);
Debug.Log("AssetBundles Build Complete!");
}
}
- 资源需在Inspector中设置AssetBundle名。
- 打包后会生成主Manifest和各资源包。
1.2 依赖自动分析
- Unity会自动分析依赖并打包,但加载时需手动按依赖顺序加载。
- 可用
AssetBundleManifest
自动获取依赖列表。
2. 临时文件管理策略(视频播放器场景)
2.1 命名与路径
- 推荐用唯一ID、GUID或Hash命名,避免多视频并发时冲突。
- 路径建议用
Application.persistentDataPath
,保证可写。
string tempPath = $"{Application.persistentDataPath}/video_{videoId}.mp4";
2.2 清理策略
- 播放结束后立即删除(适合短视频、单次播放)
- 定期清理(适合缓存策略,避免频繁IO)
- 启动时清理过期文件(如只保留最近N天的文件)
示例:
if (File.Exists(tempPath))
File.Delete(tempPath);
3. 资源卸载与内存管理
ab.Unload(false)
:只卸载AssetBundle本身,已加载资源不卸载。ab.Unload(true)
:连同已加载资源一起卸载(慎用,可能导致引用丢失)。- 视频播放结束后,建议
ab.Unload(false)
,并手动清理临时文件。
4. Addressables与AssetBundle的对比
特性 | AssetBundle(原生) | Addressables(推荐) |
---|---|---|
依赖管理 | 手动 | 自动 |
资源定位 | 路径/名字 | 地址/标签 |
异步加载 | 支持 | 支持 |
远程热更 | 支持 | 支持(更易用) |
资源卸载 | 手动 | 自动/手动 |
适合场景 | 旧项目/自定义需求 | 新项目/推荐 |
结论:
- 新项目建议用Addressables,管理更方便,API更现代。
- 但自定义播放器、特殊二进制资源场景下,原生AssetBundle更灵活。
5. 视频播放器完整工程范例(伪代码)
IEnumerator PlayVideoFromAssetBundle(string abPath, string assetName, int playerId)
{
// 1. 加载AssetBundle
AssetBundleCreateRequest abReq = AssetBundle.LoadFromFileAsync(abPath);
yield return abReq;
AssetBundle ab = abReq.assetBundle;
// 2. 加载视频二进制资源
AssetBundleRequest assetReq = ab.LoadAssetAsync<TextAsset>(assetName);
yield return assetReq;
TextAsset videoAsset = assetReq.asset as TextAsset;
// 3. 写入本地临时文件
string tempPath = $"{Application.persistentDataPath}/video_{playerId}.mp4";
File.WriteAllBytes(tempPath, videoAsset.bytes);
// 4. 传递给Native层解码
gvpPrepare(playerId, tempPath, decodeType);
// 5. 播放结束后清理
ab.Unload(false);
if (File.Exists(tempPath))
File.Delete(tempPath);
}
6. 常见问题与排查建议
- 资源找不到:确认AssetBundle名、资源名、平台一致。
- 依赖丢失:确保依赖包先于主包加载。
- 内存泄漏:及时卸载AssetBundle和清理临时文件。
- IO卡顿:大文件写入建议用协程或后台线程。
7. 进阶建议
- 多平台打包自动化:用CI/CD脚本批量打包各平台AssetBundle。
- 断点续传与校验:大文件下载可用MD5校验和断点续传。
- 资源版本管理:结合Manifest或自定义配置,管理资源版本和热更。
8. 参考资料
下面我将继续深入,补充一些高级工程实践,包括:
- AssetBundle打包自动化与多平台支持
- 资源版本管理与热更新流程
- 大型项目中的依赖与生命周期管理
- 视频播放器场景下的缓存优化与多线程IO
- AssetBundle与Addressables的迁移建议
- 常见坑点与排查技巧
1. AssetBundle打包自动化与多平台支持
1.1 多平台批量打包脚本
实际项目中,通常需要为Windows、Android、iOS等多平台分别打包。可以用如下脚本自动化:
using UnityEditor;
using UnityEngine;
public class MultiPlatformABBuilder
{
[MenuItem("Tools/Build All Platforms AssetBundles")]
public static void BuildAll()
{
BuildForPlatform(BuildTarget.StandaloneWindows64, "Win64");
BuildForPlatform(BuildTarget.Android, "Android");
BuildForPlatform(BuildTarget.iOS, "iOS");
}
static void BuildForPlatform(BuildTarget target, string folder)
{
string outPath = $"AssetBundles/{folder}";
if (!System.IO.Directory.Exists(outPath))
System.IO.Directory.CreateDirectory(outPath);
BuildPipeline.BuildAssetBundles(
outPath,
BuildAssetBundleOptions.None,
target
);
Debug.Log($"AssetBundles for {folder} Build Complete!");
}
}
- 这样可以一键打包所有平台,便于CI/CD集成。
1.2 平台选择与加载
运行时根据Application.platform
选择对应平台的AssetBundle路径。
2. 资源版本管理与热更新流程
2.1 资源版本号与MD5校验
- 每次打包后,生成资源清单(如
version.json
),记录每个AssetBundle的文件名、MD5、大小、版本号。 - 客户端启动时,先下载服务器的
version.json
,对比本地,决定哪些资源需要更新。
2.2 热更新流程
- 客户端启动,下载最新资源清单
- 对比本地与服务器资源,找出需要更新的AssetBundle
- 下载并替换本地旧资源
- 加载最新AssetBundle
伪代码:
// 1. 下载version.json
// 2. 对比本地与服务器MD5
// 3. 下载缺失或有更新的AssetBundle
// 4. 加载最新资源
3. 大型项目中的依赖与生命周期管理
3.1 依赖自动加载
- 封装AssetBundle管理器,自动递归加载依赖包。
- 依赖计数,只有所有引用都释放后才真正卸载。
伪代码:
class ABRef
{
public AssetBundle bundle;
public int refCount;
}
Dictionary<string, ABRef> loadedBundles;
void LoadWithDependencies(string abName)
{
// 递归加载依赖
foreach (var dep in manifest.GetAllDependencies(abName))
LoadWithDependencies(dep);
// 加载自身
if (!loadedBundles.ContainsKey(abName))
loadedBundles[abName] = new ABRef { bundle = AssetBundle.LoadFromFile(abName), refCount = 1 };
else
loadedBundles[abName].refCount++;
}
void Unload(string abName)
{
if (loadedBundles.ContainsKey(abName))
{
loadedBundles[abName].refCount--;
if (loadedBundles[abName].refCount <= 0)
{
loadedBundles[abName].bundle.Unload(false);
loadedBundles.Remove(abName);
}
}
}
3.2 生命周期管理
- 资源用完后及时
Unload
,避免内存泄漏。 - 可用对象池管理常用资源,减少重复加载。
4. 视频播放器场景下的缓存优化与多线程IO
4.1 大文件异步写入
- 视频文件较大,写入本地磁盘时建议用多线程或异步IO,避免主线程卡顿。
C#多线程写文件示例:
void WriteFileAsync(string path, byte[] data, Action onComplete)
{
System.Threading.ThreadPool.QueueUserWorkItem(_ =>
{
System.IO.File.WriteAllBytes(path, data);
onComplete?.Invoke();
});
}
4.2 视频缓存优化
- 可设置最大缓存数量/大小,超出时自动清理最久未用的文件(LRU策略)。
- 可用本地数据库(如SQLite)记录缓存信息。
5. AssetBundle与Addressables的迁移建议
- Addressables是Unity官方推荐的新一代资源管理方案,底层仍用AssetBundle,但自动管理依赖、异步加载、远程热更等。
- 迁移建议:新项目直接用Addressables,老项目可逐步迁移,先用Addressables管理新资源,旧资源继续用原生AssetBundle。
6. 常见坑点与排查技巧
- 资源名/路径大小写敏感:不同平台(如Windows/Android)表现不同,建议统一小写。
- AssetBundle未卸载:长时间不卸载会导致内存暴涨,需定期检查。
- 依赖包未加载:Shader丢失、材质粉色,多为依赖未加载。
- 平台不兼容:AssetBundle需针对目标平台打包,不能混用。
- 资源更新后未清理缓存:热更时需清理旧版本AssetBundle,否则可能加载到旧资源。
7. 视频播放器场景下的完整流程图
8. 参考工程结构建议
/AssetBundles/
/Android/
/iOS/
/Win64/
version.json
/Cache/
video_xxx.mp4
/Scripts/
ABManager.cs
VideoPlayerManager.cs
下面我将继续补充更深层次的工程实践和细节,尤其针对大型项目、视频播放器复杂场景、热更新、缓存管理、自动化工具链等方面,给出更实用的建议和代码示例。
1. 大型项目中的AssetBundle分包与分层设计
1.1 分包策略
- 按功能分包:如UI、场景、角色、视频、音效等分别打包,便于独立更新和加载。
- 按资源类型分包:如所有视频文件单独一个包,图片一个包,Prefab一个包。
- 按业务模块分包:如“首页”、“点播”、“直播”等业务模块各自独立包。
优点:
- 降低单包体积,提升加载速度。
- 支持按需加载,节省内存。
- 热更新时只需更新变动的包。
1.2 分层管理
- 底层依赖包(如Shader、通用材质)优先加载,生命周期长。
- 业务资源包(如视频、UI)按需加载,用完即卸载。
2. 视频播放器场景下的多实例与并发管理
2.1 多实例播放器的资源隔离
- 每个播放器实例分配独立的临时文件路径和资源句柄。
- 资源管理器需支持多实例并发加载、写入和卸载。
示例:
string tempPath = $"{Application.persistentDataPath}/video_{playerId}_{Guid.NewGuid()}.mp4";
2.2 并发写入与线程安全
- 多个视频同时写入磁盘时,需保证线程安全。
- 推荐用线程池或Task异步写入,避免主线程阻塞。
示例:
async Task WriteVideoAsync(string path, byte[] data)
{
await Task.Run(() => File.WriteAllBytes(path, data));
}
3. 缓存管理与LRU淘汰机制
3.1 LRU缓存算法
- 维护一个缓存队列,记录每个视频文件的最后访问时间。
- 达到最大缓存数量或空间时,自动删除最久未用的文件。
伪代码:
class VideoCacheManager
{
Dictionary<string, DateTime> cacheDict; // path -> lastAccessTime
int maxCacheCount = 10;
void Access(string path)
{
cacheDict[path] = DateTime.Now;
if (cacheDict.Count > maxCacheCount)
{
var oldest = cacheDict.OrderBy(kv => kv.Value).First();
File.Delete(oldest.Key);
cacheDict.Remove(oldest.Key);
}
}
}
3.2 缓存持久化
- 启动和退出时,将缓存信息(如文件名、时间戳)序列化到本地(如json)。
- 下次启动时自动恢复缓存状态。
4. 热更新全流程自动化
4.1 资源清单自动生成
- 打包后自动生成
version.json
,内容包括每个包的文件名、MD5、大小、依赖关系等。
示例结构:
{
"bundles": [
{"name": "video.ab", "md5": "xxxx", "size": 123456, "deps": ["shader.ab"]},
{"name": "shader.ab", "md5": "yyyy", "size": 1234, "deps": []}
]
}
4.2 客户端热更流程
- 启动时下载服务器
version.json
。 - 对比本地,找出需要更新的包。
- 下载新包,校验MD5,替换本地旧包。
- 清理本地多余包。
- 加载最新资源。
5. 自动化工具链与CI/CD集成
5.1 命令行打包
- 用
Unity -batchmode -executeMethod
命令行自动打包,便于Jenkins、GitLab CI等集成。
示例:
Unity.exe -batchmode -projectPath "D:/MyProject" -executeMethod MultiPlatformABBuilder.BuildAll -quit
5.2 自动上传与分发
- 打包后自动上传到CDN或对象存储(如OSS、COS、S3)。
- 结合CDN加速,提升用户下载速度。
6. Addressables迁移与兼容方案
- Addressables支持标签、分组、远程包、自动依赖管理,更适合大型项目。
- 可先将新资源用Addressables管理,旧资源继续用AssetBundle,逐步迁移。
- Addressables支持自定义资源加载器,可兼容特殊场景(如视频二进制写本地)。
7. 常见问题与高级排查
- 依赖循环:避免包之间互相依赖,建议用工具分析依赖树。
- 包体积过大:定期分析包内容,拆分大包,移除冗余资源。
- 热更失败:优先检查MD5校验、路径拼写、平台兼容性。
- Native层解码失败:确认本地文件完整、路径正确、权限足够。
8. 进阶建议
- 资源加密:如有安全需求,可对AssetBundle或视频文件加密,加载时解密到内存或临时文件。
- 断点续传:大文件下载支持断点续传,提升用户体验。
- 多语言/多分辨率资源:按需加载不同语言、分辨率的视频包,节省流量和存储。
9. 参考代码结构
/AssetBundles/
/Android/
/iOS/
/Win64/
version.json
/Cache/
video_xxx.mp4
/Scripts/
ABManager.cs
VideoCacheManager.cs
HotUpdateManager.cs
VideoPlayerManager.cs
/Editor/
ABBuilder.cs
下面我将继续补充更深层次的工程实践和细节,尤其针对视频播放器场景下的资源安全、加密、断点续传、内存优化、日志与监控、自动化测试等方面,给出实用建议和代码示例。
1. 视频资源安全与加密
1.1 加密存储
- 加密AssetBundle:可在打包后对AssetBundle文件进行加密(如AES),加载时先解密到内存或临时文件再用
AssetBundle.LoadFromMemory
。 - 加密视频文件:视频二进制资源可加密存储,播放前解密写入本地临时文件。
示例:
// 加密写入
byte[] encrypted = MyAes.Encrypt(videoAsset.bytes, key, iv);
File.WriteAllBytes(tempPath, encrypted);
// 解密读取
byte[] encrypted = File.ReadAllBytes(tempPath);
byte[] decrypted = MyAes.Decrypt(encrypted, key, iv);
File.WriteAllBytes(decryptedPath, decrypted);
- 注意:加密密钥应安全存储,避免硬编码。
1.2 防止盗链与篡改
- 资源下载时可加签名参数,服务端校验。
- 客户端校验MD5,防止被篡改。
2. 断点续传与大文件下载
2.1 断点续传原理
- 下载大文件时,记录已下载的字节数,断线后从断点继续。
- 可用
HttpWebRequest
的Range
头实现。
伪代码:
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.AddRange(downloadedBytes);
2.2 断点续传管理
- 下载中间状态写入本地(如
.part
文件)。 - 下载完成后重命名为正式文件。
3. 内存优化与大文件处理
3.1 避免大文件一次性读入内存
- 对于大视频文件,避免
File.ReadAllBytes
,可用流式读写。 - Unity的
AssetBundle.LoadFromStream
可用于流式加载。
示例:
using (FileStream fs = new FileStream(path, FileMode.Open))
{
AssetBundle ab = AssetBundle.LoadFromStream(fs);
}
3.2 视频播放时的内存管理
- 播放结束后及时卸载AssetBundle和释放本地文件。
- 对于多实例播放器,合理分配内存和磁盘空间。
4. 日志与监控
4.1 关键流程日志
- 记录每次资源加载、写入、解密、卸载、缓存清理等操作。
- 关键异常(如文件不存在、解密失败、MD5不符)需详细日志。
示例:
Debug.Log($"[VideoAB] Load {abName} success, size={size}");
Debug.LogError($"[VideoAB] Decrypt failed: {ex.Message}");
4.2 远程监控与上报
- 关键错误可上报到远程日志平台,便于线上问题追踪。
5. 自动化测试与回归保障
5.1 单元测试
- 对AssetBundle加载、依赖管理、缓存淘汰、加密解密等核心逻辑编写单元测试。
5.2 自动化回归
- 每次资源打包后,自动跑一遍加载、播放、卸载、缓存清理等流程,确保无回归。
6. 资源分发与CDN优化
6.1 CDN分发
- 资源包上传到CDN,客户端根据平台和版本动态选择最近节点下载。
- CDN支持断点续传和大文件分片。
6.2 资源预热
- 新版本上线前,提前预热CDN,避免用户首次访问时CDN回源慢。
7. 资源版本回滚与灰度发布
7.1 版本回滚
- 保留历史版本资源包,出现严重问题时可快速回滚。
- 客户端支持切换资源版本。
7.2 灰度发布
- 资源清单支持多版本,部分用户先用新资源,逐步全量。
8. 典型问题与应对
- 磁盘空间不足:检测剩余空间,提示用户或自动清理缓存。
- 权限问题:Android/iOS需申请存储权限,写入失败需提示。
- 多线程冲突:多实例并发写入同一文件需加锁或避免。
9. 代码结构与职责分层建议
/Scripts/
ABManager.cs // 资源加载、依赖管理
VideoCacheManager.cs // 缓存、LRU、断点续传
VideoSecurityManager.cs // 加密、解密
HotUpdateManager.cs // 资源清单、热更
VideoPlayerManager.cs // 播放器实例管理
LogManager.cs // 日志与上报
/Editor/
ABBuilder.cs // 打包、清单生成
VersionGenerator.cs // 资源清单自动化
/Tests/
ABManagerTests.cs
VideoCacheManagerTests.cs