定時任務實現總結
在Android中這算是一個常用的功能了,,有興趣一起來探討下可以
Android消息機制
首先來了解一下Android的消息處理機制
即Handlerd的運行機制,handler的運行需要底層的MessageQueue和Looper的支撐。MessageQueue(消息隊列),它的內部存儲了一些消息,以隊列的形式對外提供插入和刪除的操作(實際為單鏈表存儲)。Looper(消息循環),配合MessageQueue實現實現消息的不斷入隊和出隊工作。
一個關系圖:
這里寫圖片描述
通過Handler可以很容易將任務切換到其他線程中執行,以減少主線程的負擔,因此Handler常用來進行UI更新。這里只是簡單的進行一些概述。對應handler還不清楚的強烈建議參考以下博客:
Android 異步消息處理機制
Android AsyncTask完全解析
當然,現在已經有更好的消息處理辦法了,了解handler和Asynctask可以更好的理解Android內部消息的處理機制。
推薦:EventBus,高度解耦,代碼簡潔明了,有興趣的可以自行參考使用。
1.采用Handle與線程的sleep(long)方法
1) 定義一個Handler類,用於處理接受到的Message。
Handler handler = new Handler() {
public void handleMessage(Message msg) {
// 要做的事情
super.handleMessage(msg);
}
};
1
2
3
4
5
6
2) 新建一個實現Runnable接口的線程類,如下:
public class MyThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
try {
Thread.sleep(10000);// 線程暫停10秒,單位毫秒
Message message = new Message();
message.what = 1;
handler.sendMessage(message);// 發送消息
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
3) 在需要啟動線程的地方加入下面語句:
new Thread(new MyThread()).start();
1
分析:純正的java原生實現,在sleep結束后,並不能保證競爭到cpu資源,這也就導致了時間上必定>=10000的精度問題。
2.采用Handler的postDelayed(Runnable, long)方法
1)定義一個Handler類
Handler handler=new Handler();
Runnable runnable=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
//要做的事情
handler.postDelayed(this, 2000);
}
};
1
2
3
4
5
6
7
8
9
10
11
2) 啟動與關閉計時器
handler.postDelayed(runnable, 2000);//每兩秒執行一次runnable.
1
handler.removeCallbacks(runnable);
1
分析:嗯,看起蠻不錯,實現上也簡單了,和sleep想必還不會產生阻塞,注意等待和間隔的區別。
3.采用Handler與timer及TimerTask結合的方法
1) 定義定時器、定時器任務及Handler句柄
private final Timer timer = new Timer();
private TimerTask task;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
// 要做的事情
super.handleMessage(msg);
}
};
1
2
3
4
5
6
7
8
9
10
11
2) 初始化計時器任務
task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
1
2
3
4
5
6
7
8
9
3) 啟動和關閉定時器
timer.schedule(task, 2000, 3000);
1
timer.cancel();
1
此外,Timer也可以配合runOnUiThread實現,如下
private TimerTask mTimerTask = new TimerTask() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
//處理延時任務
}
});
}
};
1
2
3
4
5
6
7
8
9
10
11
12
分析:timer.schedule(task, 2000, 3000);意思是在2秒后執行第一次,之后每3000秒在執行一次。timer不保證精確度且在無法喚醒cpu,不適合后台任務的定時。
采用AlarmManger實現長期精確的定時任務
AlarmManager的常用方法有三個:
set(int type,long startTime,PendingIntent pi);//一次性
setExact(int type, long triggerAtMillis, PendingIntent operation)//一次性的精確版
setRepeating(int type,long startTime,long intervalTime,PendingIntent
pi);//精確重復
setInexactRepeating(int type,long startTime,long
intervalTime,PendingIntent pi);//非精確,降低功耗
type表示鬧鍾類型,startTime表示鬧鍾第一次執行時間,long intervalTime表示間隔時間,PendingIntent表示鬧鍾響應動作
對以上各個參數的詳細解釋
鬧鍾的類型:
AlarmManager.ELAPSED_REALTIME:休眠后停止,相對開機時間
AlarmManager.ELAPSED_REALTIME_WAKEUP:休眠狀態仍可喚醒cpu繼續工作,相對開機時間
AlarmManager.RTC:同1,但時間相對於絕對時間
AlarmManager.RTC_WAKEUP:同2,但時間相對於絕對時間
AlarmManager.POWER_OFF_WAKEUP:關機后依舊可用,相對於絕對時間
絕對時間:1970 年 1月 1 日 0 點
startTime:
鬧鍾的第一次執行時間,以毫秒為單位,一般使用當前時間。
SystemClock.elapsedRealtime():系統開機至今所經歷時間的毫秒數
System.currentTimeMillis():1970 年 1 月 1 日 0 點至今所經歷時間的毫秒數
intervalTime:執行時間間隔。
PendingIntent :
PendingIntent用於描述Intent及其最終的行為.,這里用於獲取定時任務的執行動作。
詳細參考譯文:PendingIntent
利用AlarmManger+Service+BarocastReceiver實現5s一次打印操作
服務類:
public class HorizonService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
Log.d("TAG", "打印時間: " + new Date().
toString());
}
}).start();
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
int five = 5000; // 這是5s
long triggerAtTime = SystemClock.elapsedRealtime() + five;
Intent i = new Intent(this, AlarmReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
return super.onStartCommand(intent, flags, startId);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
廣播接受器
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, HorizonService.class);
context.startService(i);
}
}
1
2
3
4
5
6
7
啟動定時任務:
Intent intent = new Intent(this,HorizonService.class);
startService(intent);
1
2
效果Demo:
這里寫圖片描述
本例通過廣播接收器和服務的循環調用實現了無限循環的效果,當然,你也可以直接利用setRepeating實現同樣的效果。
注意:不要忘了在manifest文件中注冊服務和廣播接收器。
AlarmManager的取消方法:AlarmManger.cancel();
分析:該方式可喚醒cpu甚至實現精確定時,適用於配合service在后台執行一些長期的定時行為。
本文總結:不建議使用第一種方式,短期的定時任務推薦第二、三種方式實現,長期或者有精確要求的定時任務則可以配合Service在后台執行。
有其他疑問可下方提出或者直接鏈接官方文檔AlarmService
---------------------
作者:痕跡天涯119
來源:CSDN
原文:https://blog.csdn.net/u014492609/article/details/51475254
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!