uLua+SimpleFramework的资源下载分析


请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com

本文分析使用的SimpleFramework的版本是0.4.0.2 for NGUI版本,基于Unity5.2.1环境测试

SimpleFramework的资源下载管理是在GameManager类里面,核心的函数就是CheckExtractResourceOnUpdateResourceOnExtractResource。下面就是这三个函数的注解分析:

CheckExtractResource函数

/// <summary>
/// 释放资源
/// </summary>
public void CheckExtractResource()
{
    // 如果资源目录存在,并且资源目录下的lua文件夹存在,
    // 也就是说,如果初始安装一个包,则起码该包要包含一个files目录,并且资源目录下的files.txt存在,直接做更新操作
    bool isExists = Directory.Exists(Util.DataPath) &&
              Directory.Exists(Util.DataPath + "lua/") && File.Exists(Util.DataPath + "files.txt");

    // 表示第一次已经解压了
    if (isExists || AppConst.DebugMode) 
    {
        StartCoroutine(OnUpdateResource());
        return;   //文件已经解压过了,自己可添加检查文件列表逻辑
    }

    // 如果没有解压过的话,就先解压,而后再做一次update resource
    StartCoroutine(OnExtractResource());    //启动释放协程
}

OnUpdateResource函数

/// <summary>
/// 启动更新下载,这里只是个思路演示,此处可启动线程下载更新
/// </summary>
IEnumerator OnUpdateResource()
{
    downloadFiles.Clear(); // 清空要下载的文件的列表数组
    /* 如果开启更新模式,前提必须启动框架自带服务器端。
    否则就需要自己将StreamingAssets里面的所有内容
    复制到自己的Webserver上面,并修改下面的WebUrl。*/
    // 如果不在线更新了,就直接进入初始化资源的操作
    if (!AppConst.UpdateMode) {
        ResManager.initialize(OnResourceInited);
        yield break;
    }

    // 下面的代码是在线更新的模式
    /*
    Util.DataPath的值如下:
    如果在Android平台,则在/data/data/xxx.xxx.xxx/files/game
    目录下,xxx.xxx.xxx为包名

    如果在iOS平台下,则在
    Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents
    目录下如果在Windows平台下,并且build了一个独立的程序出来的,
    则返回C:\Users\用户名\AppData\LocalLow\DefaultCompany\test

    如果当前处于调试模式,并且是在编辑器上执行的话,就返回你的
    工程目录/Assets/StreamingAssets/

    否则直接返回c:/simpleframework*/

    string dataPath = Util.DataPath;  //数据目录
    string url = AppConst.WebUrl;
    string random = DateTime.Now.ToString("yyyymmddhhmmss");
    string listUrl = url + "files.txt?v=" + random;
    Debug.LogWarning("LoadUpdate---->>>" + listUrl);

    // 使用WWW下载files.txt文件。
    WWW www = new WWW(listUrl); yield return www;
    if (www.error != null) {
        OnUpdateFailed(string.Empty);
        yield break;
    }

    if (!Directory.Exists(dataPath)) {
        Directory.CreateDirectory(dataPath);
    }

    // 把数据流写入到dataPath的files.txt文件中
    File.Write AllBytes(dataPath + "files.txt", www.bytes);

    // 以文本流的方式,读取到一个字符串中,然后分割出每个文件的信息
    string filesText = www.text;
    string[] files = filesText.Split('\n');
    string message = string.Empty;

    for (int i = 0; i < files.Length; i++) {
        if (string.IsNullOrEmpty(files[i])) continue;
        string[] keyValue = files[i].Split('|');
        string f = keyValue[0];
        string localfile = (dataPath + f).Trim();
        string path = Path.GetDirectoryName(localfile);
        if (!Directory.Exists(path)) {
            Directory.CreateDirectory(path);
        }

        string fileUrl = url + keyValue[0] + "?v=" + random;

        // 判断本地文件是否存在,如果不存在,则表示可以update
        bool canUpdate = !File.Exists(localfile);
        if (!canUpdate)
        {    
            // 如果本地文件已经存在了,那么检查远端文件和本地文件的md5
            // 码值,如果本地文件的MD5码值不等于远端文件的MD5值,表示需要更新
            string remoteMd5 = keyValue[1].Trim();
            string localMd5 = Util.md5file(localfile);
            canUpdate = !remoteMd5.Equals(localMd5);
            if (canUpdate) File.Delete(localfile);// 如果需要更新了,就先把本地文件给删除掉
        }

        if (canUpdate) {   //本地缺少文件
            Debug.Log(fileUrl);
            message = "downloading>>" + fileUrl;
            facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

        /*www = new WWW(fileUrl); yield return www;
        if (www.error != null) {
            OnUpdateFailed(path);   //
        yield break;
        }
        File.WriteAllBytes(localfile, www.bytes);*/


            BeginDownload(fileUrl, localfile);   // 这里都是资源文件,用线程下载
            //  用后台线程下载资源
            while (!(IsDownOK(localfile))) { yield return new WaitForEndOfFrame(); }
        }
    }
    yield return new WaitForEndOfFrame();
    message = "更新完成!!";
    facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
    // 更新完成了,就开始
    ResManager.initialize(OnResourceInited);
}

OnExtractResource函数

IEnumerator OnExtractResource() 
{
    string dataPath = Util.DataPath;  // 包体外的数据存放目录

    // 直接从游戏的安装包里面解压出资源来
    string resPath = Util.AppContentPath(); // 游戏包资源目录

    // 如果旧数据存在,就将旧数据都删掉它
    if (Directory.Exists(dataPath)) 
        Directory.Delete(dataPath, true);

    // 重新创建一个目录,然后把files.txt文件从包里面解包出来
    Directory.CreateDirectory(dataPath);

    // infile就是jar:file:///data/app/xxx.xxx.xxx.apk/!/assets/files.txt
    string infile = resPath + "files.txt";

    string outfile = dataPath + "files.txt";
    if (File.Exists(outfile))
        File.Delete(outfile);

    string message = "正在解包文件:>files.txt";
    Debug.Log(message);
    facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

    // 如果在Android平台,使用WWW去解包files.txt文件
    if (Application.platform == RuntimePlatform.Android) 
    {
        WWW www = new WWW(infile);
        yield return www;

        if (www.isDone)
        {
            File.WriteAllBytes(outfile, www.bytes);
        }

        yield return 0;
    } 
    else // 其他平台,包括iOS平台,则使用文件IO去操作它
    {
        File.Copy(infile, outfile, true);
    }

    yield return new WaitForEndOfFrame();

    // 打开files.txt,释放所有文件到数据目录
    string[] files = File.ReadAllLines(outfile);

    foreach (var file in files)
    {
        string[] fs = file.Split('|');
        infile = resPath + fs[0];  //
        outfile = dataPath + fs[0];

        message = "正在解包文件:>" + fs[0];
        Debug.Log("正在解包文件:>" + infile);
        facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

        string dir = Path.GetDirectoryName(outfile);

        // 创建对应的子目录
        if (!Directory.Exists(dir)) 
            Directory.CreateDirectory(dir);

        if (Application.platform == RuntimePlatform.Android)
        {
            WWW www = new WWW(infile);
            yield return www;

            if (www.isDone) 
            {
                File.WriteAllBytes(outfile, www.bytes);
            }

            yield return 0;
        }
        else
        {
            if (File.Exists(outfile))
            {
                File.Delete(outfile);
            }

            File.Copy(infile, outfile, true);
        }

        yield return new WaitForEndOfFrame();
    }

    message = "解包完成!!!";
    facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

    yield return new WaitForSeconds(0.1f);
    message = string.Empty;

    // 释放完成,开始启动更新资源
    StartCoroutine(OnUpdateResource());
}

返回首页