時隔多日,好不容易擠出點時間來寫寫博文。不容易,請送我幾朵紅花,點個贊也行。
今天呢,我們主要來擴展下通用工具類==>定時觸發器。
顧名思義,所謂的定時觸發器,就是告訴程序在過多長時間后,我要執行某個特定的任務。
比如舉個小栗子:
電飯煲,相信大家都用過,當我們出去工作或者上學的時候,我們只要設置下煮飯時間,就可以安心的離開。
電飯煲會自動的開始計時工作,等到了你設置的時間后,他就會自動的開始煮飯啊什么的。而你卻可以在遠在千里的上班。
智能化,對就是這樣的效果。我們今天就來寫寫這個智能的小東西。
首先在設計這個小功能之前,我們要明白自己需要的是什么?如何設計?
1.需要什么:
(1)肯定要有個管理定時器的類,命名TimeTaskManager。(上網查了下定時器英文可以為:TimeTask,所以就取了這個名字)
(2)既然有了這個管理類,那么這個管理類要管理什么東西?對嘍,是你所要定時執行的任務。那么這個任務要包含什么東西?
1.多久時間開始執行任務肯定要,
2.重復執行間隔(有些任務要定時的重復執行,比如像機器加工廠的機器晝夜重復一個加工動作)
ok,我們命名為TimeTask
2.如何設計:
當我們設計一個個有相關聯的類的時候,我們可能需要紙筆來打草稿,其實完全不用,學過uml的同學可以新手拈來。這里呢我推薦使用Process On這個工具。在線繪畫工具,非常好用。
這里我們邊設計邊畫圖:
首先從TimeTask下手,對於這個類,我們要想作為一個任務,而且還是定時的。那么一下就能想到,任務執行用委托。還有程序肯定有許多任務,所以要定義一個id識別這個唯一任務。
那么定時肯定也需要一些變量,
1.private uint id;//任務id
2.private uint interval;//間隔多少秒,重復這個任務
3.private Action action;//無參委托
看到這里,這個Timetask任務類,大致建立好了。
哎!細心的同學可能會發現,這個Action委托是個無參委托,那么假如說我的任務方法有帶參的怎么辦呢?哎,那么問題就來了。
那么我再設計一個帶一個參數的Timetask<T>類,然后Action<T> action不就行了。那二個參數呢,三個參數呢......?
有多少個參數,你都要設計多少個類。
所以,對於這樣的情況,我們需要把Timetask抽象成一個基類,命名為AbstractTimeTask
哎!只要我們所有無參帶參的TimeTask都繼承與AbstractTimeTask抽象類,這樣代碼的復用性就大大提高了。
設計好了之后,我們編寫代碼:
AbstractTimeTask:
using UnityEngine; using System.Collections; using System; #region 模塊信息 /*---------------------------------------------------------------- // 模塊名:AbstractTimeT // 創建者:chen // 修改者列表: // 創建日期:2015.11.5 // 模塊描述//----------------------------------------------------------------*/ #endregion public abstract class AbstractTimeTask { #region 字段 private uint m_uiTimeId;//任務id private int m_iInterval;//任務重復時間間隔,為0不重復 private ulong m_ulNextTick;//下一次觸發的時間點 #endregion #region 屬性 public uint TimeId { get { return m_uiTimeId; } set { m_uiTimeId = value; } } public int Interval { get { return m_iInterval; } set { m_iInterval = value; } } public ulong NextTick { get { return m_ulNextTick; } set { this.m_ulNextTick = value; } } /// <summary> /// 抽象屬性,給子類自定義自己的action委托 /// </summary> public abstract Action Action { get; set; } #endregion #region 公有方法 /// <summary> /// 抽象方法,給自己自己定義執行委托 /// </summary> public abstract void DoAction(); #endregion }
TimeTask:(這里主要先講無參)
using UnityEngine; using System.Collections; using System; #region 模塊信息 /*---------------------------------------------------------------- // 模塊名:TimeTask // 創建者:chen // 修改者列表: // 創建日期:2015.11.5 // 模塊描述:定時觸發任務類 //----------------------------------------------------------------*/ #endregion public class TimeTask : AbstractTimeTask { #region 字段 private Action m_action;//定義自己的委托 #endregion #region 屬性 public override Action Action { get { return m_action; } set { m_action = value; } } #endregion #region 公有方法 /// <summary> /// 重新父類的委托方法 /// </summary> public override void DoAction() { m_action(); } #endregion }
這里我們增加了NextTick字段,有什么卵用呢?主要是用來與當前程序運行時間比較,如果剛好等於這個NextTick值時,就觸發委托函數,執行任務。
細想一下,我們定時管理器類要把任務一個個加到隊列里面管理,那么肯定需要一個時間變量與task里面的時間變量進行比較。
所以,定時管理類就需要一個static uint tick變量來記錄程序運行總的時間,如果吧task加到隊列里面,task的NextTick=程序運行的總的時間tick+start(多久之后執行任務);還有就是如果task的interval的值大於0,也就是說有重復的執行,那么,就需要再加上interval的值,然后再加入到隊列里面。
分析了這么多,接着來寫管理類:
TimeTaskManager:
using UnityEngine; using System.Collections.Generic; using System.Diagnostics; using System; #region 模塊信息 /*---------------------------------------------------------------- // 模塊名:TimeTaskManager // 創建者:chen // 修改者列表: // 創建日期:2015.11.5 // 模塊描述:定時觸發器管理類 //----------------------------------------------------------------*/ #endregion public class TimeTaskManager { #region 字段 private static uint m_uiNextTimeId;//總的id,需要分配給task,也就是每加如一個task,就自增 private static uint m_uiTick;//總的時間,用來和task里面的nexttick變量來進行比較,看是否要觸發任務 private static Queue<AbstractTimeTask> m_queue; private static Stopwatch m_stopWatch;//c#自帶的計時器,不會的自行百度 private static readonly object m_queueLock = new object();//隊列鎖 #endregion #region 構造方法 private TimeTaskManager() { } static TimeTaskManager() { m_queue = new Queue<AbstractTimeTask>(); m_stopWatch = new Stopwatch(); } #endregion #region 公有方法 /// <summary> /// 吧Task加入到隊列里面來管理,既然是個管理器肯定要有個添加task的操作 /// </summary> /// <param name="start">多久之后開始執行ms</param> /// <param name="interval">重復時間間隔ms</param> /// <param name="action">任務委托</param> /// <returns>任務id</returns> public static uint AddTimer(uint start, int interval, Action action) { AbstractTimeTask task = GetTimeTask(new TimeTask(), start, interval, action); lock (m_queueLock) { m_queue.Enqueue(task); } return task.TimeId; } /// <summary> /// 周期性執行 /// </summary> public static void Tick() { TimeTaskManager.m_uiTick += (uint)(m_stopWatch.ElapsedMilliseconds); //nityEngine.Debug.Log(TimeTaskManager.m_uiTick); m_stopWatch.Reset(); m_stopWatch.Start(); while (m_queue.Count != 0) { AbstractTimeTask task; lock (m_queueLock) { task = m_queue.Peek();//這里注意隊列並沒有刪除元素,只是放回元素,元素還在隊列里面 } if (TimeTaskManager.m_uiTick < task.NextTick)//如果程序的總時間小於task要執行的時間點,就break點,繼續等待 { break; } lock (m_queueLock) { m_queue.Dequeue(); } if (task.Interval > 0)//如果需要重復的話 { task.NextTick += (ulong)task.Interval; lock (m_queueLock) { m_queue.Enqueue(task);//再次加入隊列里面,注意哦,id不變的 } task.DoAction(); } else { task.DoAction();//執行委托 } } } #endregion #region 私有方法 private static AbstractTimeTask GetTimeTask(AbstractTimeTask task,uint start,int interval,Action action) { task.Interval = interval; task.TimeId = ++TimeTaskManager.m_uiNextTimeId; task.NextTick = TimeTaskManager.m_uiTick + start; task.Action = action; return task; } #endregion }
注意:AddTimer的參數的單位是毫秒,不是秒。
接下來是實驗:
首先寫個Driver,作為驅動類。
using UnityEngine; using System.Collections; #region 模塊信息 /*---------------------------------------------------------------- // 模塊名:Driver // 創建者:chen // 修改者列表: // 創建日期:2015.11.5 // 模塊描述:驅動類 //----------------------------------------------------------------*/ #endregion public class Driver : MonoBehaviour { void Start() { TimeTaskManager.AddTimer(5000, 5000, DebugTest); InvokeRepeating("Tick", 0, 0.02f); } void Update() { } void Tick() { TimeTaskManager.Tick(); } void DebugTest() { Debug.Log("111"); } }
創建一個空物體,然后賦予它這個腳本,作為驅動所有程序腳本。
運行,發現程序在5秒之后,每隔5秒打印一個111到控制台。
這個定時類,非常的有用,就比如說網絡通信啊,我們可以定時的發送心跳包,還有彈出警告窗口,計時多少秒之后自動關閉等等