Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

[Unity热更新]tolua# & LuaFramework(十四):更新下载(中)

$
0
0

承接上一篇:http://blog.csdn.net/lyh916/article/details/51146693

上一篇基本实现了更新下载,很多时候,我们还需要将更新文件数,下载速度等信息显示在界面上,所以这篇就说下怎么做。


效果图:



Ps:上面的下载速度为Infinity(无穷大),具体原因不明,估计是我使用的是编辑器链接本地服务器,而且更新的文件很小,所以就快得飞起吧!


分析:

这里我把它分为两部分:

a.信息的获取:其实上面显示的信息,都可以通过GameManager.cs获取到。

b.信息的显示:在GameManager.cs中,我们可以发现,当获取到消息后,就会通过facade.SendMessageCommand把消息进行发送,然后被AppView.cs进行捕获处理。

因此目标就很明确了:修改GameManager和AppView。


1.首先,看下facade.SendMessageCommand,它可以发送各种的消息,消息的定义在NotiConst.cs。这里我们把消息的种类扩充一下。

using UnityEngine;
using System.Collections;

public class NotiConst
{
    /// <summary>
    /// Controller层消息通知
    /// </summary>
    public const string START_UP = "StartUp";                       //启动框架
    public const string DISPATCH_MESSAGE = "DispatchMessage";       //派发信息

    /// <summary>
    /// View层消息通知
    /// </summary>
    
    //解包要展示的相关参数
	public const string EXTRACT_FILE_NAME = "EXTRACT_FILE_NAME";           //解包文件名
	public const string EXTRACT_FINISH_ONE = "EXTRACT_FINISH_ONE";         //解包完一个文件   
	public const string EXTRACT_ALL_COUNT = "EXTRACT_ALL_COUNT";           //解包总文件数
	
	//更新要展示的相关参数
    public const string UPDATE_SPEED = "UPDATE_SPEED";                     //下载速度
	public const string UPDATE_FILE_NAME = "UPDATE_FILE_NAME";             //下载文件名
	public const string UPDATE_FINISH_ONE = "UPDATE_FINISH_ONE";           //下载完一个文件
	public const string UPDATE_ALL_COUNT = "UPDATE_ALL_COUNT";             //下载总文件数

    //线程相关
    public const string THREAD_EXTRACT = "THREAD_EXTRACT";                   //线程解包
    public const string THREAD_DOWNLOAD = "THREAD_DOWNLOAD";                 //线程下载
}


2.然后就是准备好解包和更新的参数,并把这些参数发送出去。

修改GameManager.cs

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using LuaInterface;
using System.Reflection;
using System.IO;

namespace LuaFramework {
    public class GameManager : Manager {
        protected static bool initialize = false;
        private List<string> downloadFiles = new List<string>();

        /// <summary>
        /// 初始化游戏管理器
        /// </summary>
        void Awake() {
            Init();
        }

        /// <summary>
        /// 初始化
        /// </summary>
        void Init() {
            if (AppConst.ExampleMode) {
                InitGui();
            }
            DontDestroyOnLoad(gameObject);  //防止销毁自己

            CheckExtractResource(); //释放资源
            Screen.sleepTimeout = SleepTimeout.NeverSleep;
            Application.targetFrameRate = AppConst.GameFrameRate;
        }

        /// <summary>
        /// 初始化GUI
        /// </summary>
        public void InitGui() {
            string name = "UI Root";
            GameObject gui = GameObject.Find(name);
            if (gui != null) return;

            GameObject prefab = Util.LoadPrefab(name);
            gui = Instantiate(prefab) as GameObject;
            gui.name = name;
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void CheckExtractResource() {
            hadExtractResource = Directory.Exists(Util.DataPath) &&
              Directory.Exists(Util.DataPath + "lua/") && File.Exists(Util.DataPath + "files.txt");
            if (hadExtractResource || AppConst.DebugMode)
            {
                ResManager.initialize(OnResourceInited);
                //StartCoroutine(OnUpdateResource());//在第二次解包后就更新
                return;   //文件已经解压过了,自己可添加检查文件列表逻辑
            }
            StartCoroutine(OnExtractResource());    //启动释放协成 
        }

        bool hadExtractResource;
        bool firstExtractResource = true;

        IEnumerator OnExtractResource() {
            string dataPath = Util.DataPath;  //数据目录
            string resPath = Util.AppContentPath(); //游戏包资源目录

            string infile = resPath + "files.txt";
            string outfile = dataPath + "files.txt";
            string message = "";

            if (firstExtractResource)
            {
                //创建Util.DataPath目录
                if (Directory.Exists(dataPath)) Directory.Delete(dataPath, true);
                Directory.CreateDirectory(dataPath);

                if (File.Exists(outfile)) File.Delete(outfile);

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

                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 File.Copy(infile, outfile, true);
                //yield return new WaitForEndOfFrame();
            }

            List<string> willExtractFileName = new List<string>();

            //释放文件到数据目录,过滤要解包的文件
            string[] files = File.ReadAllLines(outfile);
            foreach (var file in files) 
            {
                string[] fs = file.Split('|');
                infile = resPath + fs[0];  
                outfile = dataPath + fs[0];

                //start是主界面的包,需要自行修改
                bool a = fs[0].StartsWith("lua/") || fs[0].StartsWith("StreamingAssets") ||fs[0].StartsWith("start");
                if (firstExtractResource && !a) continue;
                if (!firstExtractResource && a) continue;

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

                string dir = Path.GetDirectoryName(outfile);
                if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);

                willExtractFileName.Add(fs[0]);
            }

            //第二次解包才显示信息
            if (!firstExtractResource && (willExtractFileName.Count > 0)) 
                facade.SendMessageCommand(NotiConst.EXTRACT_ALL_COUNT, willExtractFileName.Count);

            for (int i = 0; i < willExtractFileName.Count; i++)
			{
                Debug.Log("正在解包:" + willExtractFileName[i]);
                string from = resPath + willExtractFileName[i];
                string to = dataPath + willExtractFileName[i];

                if (Application.platform == RuntimePlatform.Android)
                {
                    if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FILE_NAME, willExtractFileName[i]);

                    WWW www = new WWW(from);
                    yield return www;

                    if (www.isDone)
                    {
                        File.WriteAllBytes(to, www.bytes);
                        if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FINISH_ONE, 0);
                    }
                    yield return 0;
                }
                else
                {
                    if (File.Exists(to))
                    {
                        File.Delete(to);
                    }
                    if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FILE_NAME, willExtractFileName[i]);
                    File.Copy(from, to, true);
                    if (!firstExtractResource) facade.SendMessageCommand(NotiConst.EXTRACT_FINISH_ONE, 0);
                }
                yield return new WaitForEndOfFrame();
			}
;               
            
            if (firstExtractResource)
            {
                ResManager.initialize(OnResourceInited);
            }
            else
            {
                message = "解包完成!!!";
                //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

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

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

        /// <summary>
        /// 启动更新下载,这里只是个思路演示,此处可启动线程下载更新
        /// </summary>
        IEnumerator OnUpdateResource() {
            downloadFiles.Clear();

            if (!AppConst.UpdateMode) {
                //ResManager.initialize(OnResourceInited);
                yield break;
            }
            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 www = new WWW(listUrl); yield return www;
            if (www.error != null) {
                OnUpdateFailed(string.Empty);
                yield break;
            }
            if (!Directory.Exists(dataPath)) {
                Directory.CreateDirectory(dataPath);
            }
            File.WriteAllBytes(dataPath + "files.txt", www.bytes);

            string filesText = www.text;
            string[] files = filesText.Split('\n');

            List<string> willDownLoadUrl = new List<string>();//from
            List<string> willDownLoadFileName = new List<string>();
            List<string> willDownLoadDestination = new List<string>();//to

            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;
                bool canUpdate = !File.Exists(localfile);
                if (!canUpdate) {
                    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);
                     * */
                    willDownLoadUrl.Add(fileUrl);//下载地址
                    willDownLoadFileName.Add(keyValue[0]);
                    willDownLoadDestination.Add(localfile);//目标文件路径
                }
            }

            if (willDownLoadUrl.Count > 0) facade.SendMessageCommand(NotiConst.UPDATE_ALL_COUNT, willDownLoadUrl.Count);
            for (int i = 0; i < willDownLoadUrl.Count; i++)
            {
                Debug.Log("要下载的文件:" + willDownLoadUrl[i]);
                //这里都是资源文件,用线程下载
                facade.SendMessageCommand(NotiConst.UPDATE_FILE_NAME, willDownLoadFileName[i]);
                BeginDownload(willDownLoadUrl[i], willDownLoadDestination[i]);
                //while (!(IsDownOK(willDownLoadDestination[i]))) { yield return new WaitForEndOfFrame(); }
            }
            yield return new WaitForEndOfFrame();
            message = "更新完成!!";
            //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);

            //ResManager.initialize(OnResourceInited);
        }

        /// <summary>
        /// 是否下载完成
        /// </summary>
        bool IsDownOK(string file) {
            return downloadFiles.Contains(file);
        }

        /// <summary>
        /// 线程下载
        /// </summary>
        void BeginDownload(string url, string file) {     //线程下载
            object[] param = new object[2] {url, file};

            ThreadEvent ev = new ThreadEvent();
            ev.Key = NotiConst.THREAD_DOWNLOAD;
            ev.evParams.AddRange(param);
            ThreadManager.AddEvent(ev, OnThreadCompleted);   //线程下载
        }

        /// <summary>
        /// 线程完成
        /// </summary>
        /// <param name="data"></param>
        void OnThreadCompleted(NotiData data) {
            switch (data.evName) {
                case NotiConst.THREAD_EXTRACT:  //解压一个完成
                    //
                break;
                case NotiConst.THREAD_DOWNLOAD: //下载一个完成
                    downloadFiles.Add(data.evParam.ToString());
                break;
            }
        }

        void OnUpdateFailed(string file) {
            string message = "更新失败!>" + file;
            Debug.Log(message);
            //facade.SendMessageCommand(NotiConst.UPDATE_MESSAGE, message);
        }

        /// <summary>
        /// 资源初始化结束
        /// </summary>
        public void OnResourceInited() {
            LuaManager.InitStart();
            LuaManager.DoFile("Logic/Game");            //加载游戏
            LuaManager.DoFile("Logic/Network");         //加载网络
            NetManager.OnInit();                        //初始化网络

            Util.CallMethod("Game", "OnInitOK");          //初始化完成
            initialize = true;                          //初始化完 

            //类对象池测试
            var classObjPool = ObjPoolManager.CreatePool<TestObjectClass>(OnPoolGetElement, OnPoolPushElement);
            //方法1
            //objPool.Release(new TestObjectClass("abcd", 100, 200f));
            //var testObj1 = objPool.Get();

            //方法2
            ObjPoolManager.Release<TestObjectClass>(new TestObjectClass("abcd", 100, 200f));
            var testObj1 = ObjPoolManager.Get<TestObjectClass>();

            Debugger.Log("TestObjectClass--->>>" + testObj1.ToString());

            //游戏对象池测试
            var prefab = Resources.Load("TestGameObjectPrefab", typeof(GameObject)) as GameObject;
            var gameObjPool = ObjPoolManager.CreatePool("TestGameObject", 5, 10, prefab);

            var gameObj = Instantiate(prefab) as GameObject;
            gameObj.name = "TestGameObject_01";
            gameObj.transform.localScale = Vector3.one;
            gameObj.transform.localPosition = Vector3.zero;

            ObjPoolManager.Release("TestGameObject", gameObj);
            var backObj = ObjPoolManager.Get("TestGameObject");
            backObj.transform.SetParent(null);

            Debug.Log("TestGameObject--->>>" + backObj);

            if (hadExtractResource)
            {
                StartCoroutine(OnUpdateResource());
            }
            else
            {
                firstExtractResource = false;//进行二次解包
                hadExtractResource = true;
                StartCoroutine(OnExtractResource());
            }
        }

        /// <summary>
        /// 当从池子里面获取时
        /// </summary>
        /// <param name="obj"></param>
        void OnPoolGetElement(TestObjectClass obj) {
            Debug.Log("OnPoolGetElement--->>>" + obj);
        }

        /// <summary>
        /// 当放回池子里面时
        /// </summary>
        /// <param name="obj"></param>
        void OnPoolPushElement(TestObjectClass obj) {
            Debug.Log("OnPoolPushElement--->>>" + obj);
        }

        /// <summary>
        /// 析构函数
        /// </summary>
        void OnDestroy() {
            if (NetManager != null) {
                NetManager.Unload();
            }
            if (LuaManager != null) {
                LuaManager.Close();
            }
            Debug.Log("~GameManager was destroyed");
        }
    }
}


3.接着就是把信息显示出来了。

修改AppView.cs

using UnityEngine;
using LuaFramework;
using System.Collections.Generic;

public class AppView : View {
    private string message;

    ///<summary>
    /// 监听的消息
    ///</summary>
    List<string> MessageList {
        get {
            return new List<string>()
            { 
                NotiConst.EXTRACT_FILE_NAME,
                NotiConst.EXTRACT_FINISH_ONE,
                NotiConst.EXTRACT_ALL_COUNT,

                NotiConst.UPDATE_SPEED,
                NotiConst.UPDATE_FILE_NAME,
                NotiConst.UPDATE_FINISH_ONE,
                NotiConst.UPDATE_ALL_COUNT,
            };
        }
    }

    void Awake() {
        RemoveMessage(this, MessageList);
        RegisterMessage(this, MessageList);
    }

    /// <summary>
    /// 处理View消息
    /// </summary>
    /// <param name="message"></param>
    public override void OnMessage(IMessage message) {
        string name = message.Name;
        object body = message.Body;

        this.message = message.Name;
        switch (name) {
            case NotiConst.EXTRACT_FILE_NAME:
                extractFileName = body.ToString();
            break;

            case NotiConst.EXTRACT_FINISH_ONE:
                extractNowCount++;
            break;

            case NotiConst.EXTRACT_ALL_COUNT:
                extractAllCount = (int)body;
            break;

            case NotiConst.UPDATE_SPEED:
                updateSpeed = body.ToString();
            break;

            case NotiConst.UPDATE_FILE_NAME:
                updateFileName = body.ToString();
            break;

            case NotiConst.UPDATE_FINISH_ONE:
                updateNowCount++;
            break;

            case NotiConst.UPDATE_ALL_COUNT:
                updateAllCount = (int)body;
            break;
        }
    }

    string extractFileName;
    int extractNowCount = 0;
    int extractAllCount = 0;

    string updateFileName;
    int updateNowCount = 0;
    int updateAllCount = 0;
    string updateSpeed;

    void OnGUI()
    {
        if (string.IsNullOrEmpty(message)) return;
        if (message.StartsWith("EXTRACT_"))
        {
            GUILayout.Label("正在解包的文件:" + extractFileName);
            GUILayout.Label("当前解包数:" + extractNowCount);
            GUILayout.Label("总的解包数:" + extractAllCount);
        }
        else if (message.StartsWith("UPDATE_"))
        {
            GUILayout.Label("正在下载的文件:" + updateFileName);
            GUILayout.Label("当前下载数:" + updateNowCount);
            GUILayout.Label("总的下载数:" + updateAllCount);
            GUILayout.Label("下载速度:" + updateSpeed);
        }
        
    }

}


4.最后,因为消息类型变了,所以还要修改ThreadManager.cs

using System.Collections;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Net;
using System;
using Debug = UnityEngine.Debug;

public class ThreadEvent {
    public string Key;
    public List<object> evParams = new List<object>();
}

public class NotiData {
    public string evName;
    public object evParam;

    public NotiData(string name, object param) {
        this.evName = name;
        this.evParam = param;
    }
}

namespace LuaFramework {
    /// <summary>
    /// 当前线程管理器,同时只能做一个任务
    /// </summary>
    public class ThreadManager : Manager {
        private Thread thread;
        private Action<NotiData> func;
        private Stopwatch sw = new Stopwatch();
        private string currDownFile = string.Empty;

        static readonly object m_lockObj = new object();
        static Queue<ThreadEvent> events = new Queue<ThreadEvent>();

        delegate void ThreadSyncEvent(NotiData data);
        private ThreadSyncEvent m_SyncEvent;

        void Awake() {
            m_SyncEvent = OnSyncEvent;
            thread = new Thread(OnUpdate);
        }

        // Use this for initialization
        void Start() {
            thread.Start();
        }

        /// <summary>
        /// 添加到事件队列
        /// </summary>
        public void AddEvent(ThreadEvent ev, Action<NotiData> func) {
            lock (m_lockObj) {
                this.func = func;
                events.Enqueue(ev);
            }
        }

        /// <summary>
        /// 通知事件
        /// </summary>
        /// <param name="state"></param>
        private void OnSyncEvent(NotiData data) {
            if (this.func != null) func(data);  //回调逻辑层
            facade.SendMessageCommand(data.evName, data.evParam); //通知View层
        }

        // Update is called once per frame
        void OnUpdate() {
            while (true) {
                lock (m_lockObj) {
                    if (events.Count > 0) {
                        ThreadEvent e = events.Dequeue();
                        try {
                            switch (e.Key) {
                                case NotiConst.THREAD_EXTRACT: {     //解压文件
                                    OnExtractFile(e.evParams);
                                }
                                break;
                                case NotiConst.THREAD_DOWNLOAD: {    //下载文件
                                    OnDownloadFile(e.evParams);
                                }
                                break;
                            }
                        } catch (System.Exception ex) {
                            UnityEngine.Debug.LogError(ex.Message);
                        }
                    }
                }
                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// 下载文件
        /// </summary>
        void OnDownloadFile(List<object> evParams) {
            string url = evParams[0].ToString();    
            currDownFile = evParams[1].ToString();

            using (WebClient client = new WebClient()) {
                sw.Start();
                client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(ProgressChanged);
                client.DownloadFileAsync(new System.Uri(url), currDownFile);
            }
        }

        private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e) {
            //UnityEngine.Debug.Log(e.ProgressPercentage);
            /*
            UnityEngine.Debug.Log(string.Format("{0} MB's / {1} MB's",
                (e.BytesReceived / 1024d / 1024d).ToString("0.00"),
                (e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00")));
            */
            //float value = (float)e.ProgressPercentage / 100f;

            string value = string.Format("{0} kb/s", (e.BytesReceived / 1024d / sw.Elapsed.TotalSeconds).ToString("0.00"));
            NotiData data = new NotiData(NotiConst.UPDATE_SPEED, value);
            if (m_SyncEvent != null) m_SyncEvent(data);

            if (e.ProgressPercentage == 100 && e.BytesReceived == e.TotalBytesToReceive)
            {
                sw.Reset();

                data = new NotiData(NotiConst.UPDATE_FINISH_ONE, 0);
                if (m_SyncEvent != null) m_SyncEvent(data);
            }
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        void OnExtractFile(List<object> evParams) {
            Debugger.LogWarning("Thread evParams: >>" + evParams.Count);

            ///------------------通知更新面板解压完成--------------------
            //NotiData data = new NotiData(NotiConst.UPDATE_DOWNLOAD, null);
            //if (m_SyncEvent != null) m_SyncEvent(data);
        }

        /// <summary>
        /// 应用程序退出
        /// </summary>
        void OnDestroy() {
            thread.Abort();
        }
    }
}


作者:lyh916 发表于2016/8/14 16:29:41 原文链接
阅读:44 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>