時隔多日,好不容易擠出點時間來寫寫博文。不容易,請送我幾朵紅花,點個贊也行。
今天呢,我們主要來擴展下通用工具類==>定時觸發器。
顧名思義,所謂的定時觸發器,就是告訴程序在過多長時間后,我要執行某個特定的任務。
比如舉個小栗子:
電飯煲,相信大家都用過,當我們出去工作或者上學的時候,我們只要設置下煮飯時間,就可以安心的離開。
電飯煲會自動的開始計時工作,等到了你設置的時間后,他就會自動的開始煮飯啊什么的。而你卻可以在遠在千里的上班。
智能化,對就是這樣的效果。我們今天就來寫寫這個智能的小東西。
首先在設計這個小功能之前,我們要明白自己需要的是什么?如何設計?
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到控制台。
這個定時類,非常的有用,就比如說網絡通信啊,我們可以定時的發送心跳包,還有彈出警告窗口,計時多少秒之后自動關閉等等
