前言
學習android一段時間了,為了進一步了解android的應用是如何設計開發的,決定詳細研究幾個開源的android應用。從一些開源應用中吸收點東西,一邊進行量的積累,一邊探索android的學習研究方向。這里我首先選擇了jwood的
Standup Timer 項目。本文將把研究的內容筆記整理,建立一個索引列表。
關鍵詞
Android.os.Handler涉及較多的知識點,我把一些關鍵詞列舉在下面,將主要介紹Handler:
android.os.Handler
Handler在android里負責發送和處理消息。它的主要用途有:
1)按計劃發送消息或執行某個Runnanble(使用POST方法);
2)從其他線程中發送來的消息放入消息隊列中,避免線程沖突(常見于更新UI線程)
默認情況下,Handler接受的是當前線程下的消息循環實例(使用
Handler(
Looper looper)、
Handler(
Looper looper,
Handler.Callback callback)可以指定線程),同時一個消息隊列可以被當前線程中的多個對象進行分發、處理(在UI線程中,系統已經有一個Activity來處理了,你可以再起若干個Handler來處理)。在實例化Handler的時候,Looper可以是任意線程的,只要有Handler的指針,任何線程也都可以sendMessage。Handler對于Message的處理不是并發的。一個Looper 只有處理完一條Message才會讀取下一條,所以消息的處理是阻塞形式的(handleMessage()方法里不應該有耗時操作,可以將耗時操作放在其他線程執行,操作完后發送Message(通過sendMessges方法),然后由handleMessage()更新UI)。
倒計時程序
利用Timer 編寫一個倒計時程序,程序使用Timer和TimerTask來完成倒計時,同時使用sendMessages方法發送消息,然后在HanleMessage里更新UI。
Activity布局:
Layout<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:id="@+id/txt"
/>
<Button
android:id="@+id/btnStartTime"
android:text="開始計時"
android:layout_width="80dip"
android:layout_height="wrap_content"
></Button>
<Button
android:id="@+id/btnStopTime"
android:text="停止計時"
android:layout_width="80dip"
android:layout_height="wrap_content"
/>
<SeekBar android:id="@+id/SeekBar01" android:layout_width="match_parent" android:layout_height="wrap_content"></SeekBar>
</LinearLayout>
這里使用TextView 來顯示倒計時的時間變化,兩個按鈕用于控制時間的開始和停止。SeekBar主要是用于查看線程是否被阻塞(阻塞時無法拖動)。
onCreate@Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txt = (TextView) findViewById(R.id.txt);
btnStart = (Button) findViewById(R.id.btnStartTime);
btnStop = (Button) findViewById(R.id.btnStopTime);
Log.d("ThreadId", "onCread:"
+ String.valueOf(Thread.currentThread().getId()));
myHandler =new Handler(this);
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
}
在onCreate方法中初始化元素個元素,myHandler = new Handler(this); 調用的是
Handler(
Handler.Callback callback)構造函數,在回調方法callback中對發送來的消息進行處理(這樣我們就不必使用內部類的寫法來 重寫HandleMessage()方法了),因此Activity必須實現
android.os.Handler.Callback 接口。我們還在將onCreate 方法的ThreadId 記錄在了Log中用以和消息發送、處理時所作的線程進行比較。
發送消息
@Override
publicvoid onClick(View v) {
switch (v.getId()) {
case R.id.btnStartTime:
startTimer();
break;
case R.id.btnStopTime:
timer.cancel();
break;
}
}
privatesynchronizedvoid startTimer() {
timer =new Timer();
// TimerTask updateTimerValuesTask = new TimerTask() {
// @Override
// public void run() {
// updateTimerValues();
// }
//
// };
//自定義的CallBack模式。Task繼承自TimerTask
Task updateTimerValuesTask =new Task(this);
timer.schedule(updateTimerValuesTask, 1000, 1000);
}
//執行耗時的倒計時任務。
privatevoid updateTimerValues() {
total--;
Log.d("ThreadId", "send:"
+ String.valueOf(Thread.currentThread().getId()));
Message msg=new Message();
Bundle date =new Bundle();// 存放數據
date.putInt("time", total);
msg.setData(date);
msg.what=0;
myHandler.sendMessage(msg);
//另一種寫法
// Message msg=myHandler.obtainMessage();
// Bundle date = new Bundle();// 存放數據
// date.putInt("time", total);
// msg.setData(date);
// msg.what=0;
// msg.sendToTarget();
}
@Override
publicvoid TaskRun() {
updateTimerValues();
}
實現Button按鈕的事件處理以此進入倒計時操作。這里使用的Timer 來執行定時操作(其實我們完全可以另起一個線程)。Task類繼承了TimerTask類,里面增加了一個任務處理接口來實現回調模式,應此Activity需要實現該回調的接口 ITaskCallBack(這樣做是因為我比較不喜歡內部類的編寫方法)。
ICallBack接口和Task類publicinterface ITaskCallBack {
void TaskRun();
}
publicclass Task extends TimerTask {
private ITaskCallBack iTask;
public Task(ITaskCallBack iTaskCallBack)
{
super();
iTask=iTaskCallBack;
}
publicvoid setCallBack(ITaskCallBack iTaskCallBack)
{
iTask=iTaskCallBack;
}
@Override
publicvoid run() {
// TODO Auto-generated method stub
iTask.TaskRun();
}
}
這是Java的回調函數的一般寫法。
實現CallBack
/**
* 實現消息處理
*/
@Override
publicboolean handleMessage(Message msg) {
switch(msg.what)
{
case0:
Bundle date=msg.getData();
txt.setText(String.valueOf(date.getInt("time")));
Log.d("ThreadId", "HandlerMessage:"
+ String.valueOf(Thread.currentThread().getId()));
Log.d("ThreadId", "msgDate:"
+ String.valueOf(date.getInt("time")));
break;
}
returnfalse;
}
運行結果
可以看到在onCreate 方法中線程的ID是1(UI線程) 這與 HandlerMessage 進行消息處理時是所作的線程ID是一樣的,而消息發送的線程ID則為8非UI線程。
使用Threadle進行實現
自定義的線程類**
* 自定義的線程類,通過傳入的Handler,和Total 定期執行耗時操作
* @author linzijun
*
*/
publicclass TimerThread extends Thread {
publicint Total=60;
public Handler handler;
/**
* 初始化構造函數
* @param mhandler handler 用于發送消息
* @param total 總周期
*/
public TimerThread(Handler mhandler,int total)
{
super();
handler=mhandler;
Total=total;
}
@Override
publicvoid run() {
while(true)
{
Total--;
if(Total<0)
break;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg=new Message();
Bundle date =new Bundle();// 存放數據
date.putInt("time", Total);
msg.setData(date);
msg.what=0;
Log.d("ThreadId", "Thread:"
+ String.valueOf(Thread.currentThread().getId()));
handler.sendMessage(msg);
}
super.run();
}
}
這里繼承了Thread類,也可以直接實現 Runnable接口。
關于POST
Post的各種方法是把一個Runnable發送給消息隊列,它將在到達時進行處理。
使用POST的方式 是將Runnable 一起發送給處理的線程(這里為UI),如果Runnable的操作比較耗時的話那線程將進入阻塞狀態。可以看到先運行 Runnable的Run方法 然后在進入 HandleMessage() 。我還嘗試了另一種寫法,將TimerThreadPOST過去,運行結果是一樣的。
代碼
package zijunlin.me;
import java.util.Timer;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
publicclass PostHandler extends Activity implements OnClickListener, Runnable {
private TextView txt;
private Button btnStart, btnStop;
private Handler myHandler;
private Timer timer;
privateint total =60;
private TimerThread timerThread;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
txt = (TextView) findViewById(R.id.txt);
btnStart = (Button) findViewById(R.id.btnStartTime);
btnStop = (Button) findViewById(R.id.btnStopTime);
Log.d("ThreadId", "onCread:"
+ String.valueOf(Thread.currentThread().getId()));
myHandler =new Handler()
{
@Override
publicvoid handleMessage(Message msg) {
switch(msg.what)
{
case0:
Bundle date=msg.getData();
txt.setText(String.valueOf(date.getInt("time")));
Log.d("ThreadId", "HandlerMessage:"
+ String.valueOf(Thread.currentThread().getId()));
Log.d("ThreadId", "msgDate:"
+ String.valueOf(date.getInt("time")));
break;
}
}
};
btnStart.setOnClickListener(this);
btnStop.setOnClickListener(this);
}
@Override
publicvoid onClick(View v) {
switch (v.getId()) {
case R.id.btnStartTime:
//myHandler.post(this);
//myHandler.postDelayed(this, 1000);
timerThread=new TimerThread(myHandler,60);
myHandler.post(timerThread);
break;
case R.id.btnStopTime:
break;
}
}
@Override
publicvoid run() {
while(true)
{
total--;
if(total<0)
break;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg=new Message();
Bundle date =new Bundle();// 存放數據
date.putInt("time", total);
msg.setData(date);
msg.what=0;
Log.d("ThreadId", "POST:"
+ String.valueOf(Thread.currentThread().getId()));
myHandler.sendMessage(msg);
Log.d("ThreadId", "Thread:"
+ String.valueOf(Thread.currentThread().getId()));
}
}
}

可以說POST的各種方法主要是用于 “按計劃發送消息或執行某個Runnanble(使用POST方法)”。
參考文獻