Android總結Handler非同步更新UI介面(轉載)
轉載地址:https://blog.csdn.net/qq_21004057/article/details/51582412
本篇文章通過三種方式來實現UI控制元件的更新,Handler非同步更新UI在安卓開發中最常用也非常實在。這篇文章注重實現思路,所以我就不在介面方面進行美化了,都是最原始的控制元件。有需要的可以收藏下,。雖說搜尋引擎上關於Handler訊息機制的文章已經數不盡數了,但是我寫這篇文章也是希望在開發中能幫助自己記憶起Handler的用法。
學會使用Handler來更新UI,由於在主執行緒中直接更新UI會阻塞執行緒,造成假死現象,所以我們通常採用Handler訊息機制在UI執行緒中來更新UI控制元件。至於Handler訊息機制,在這裡簡單介紹一下。本來還打算寫一種的,這裡就不詳細說了,通過在子執行緒使用Bundle封裝屬性到Message資料中,其次在Handler中解封裝得到Message資料再顯示到控制元件中。其原理與方法三無太大差別。
Handler訊息機制原理簡介:通過Handler物件向訊息佇列中Message Queue中傳送訊息Message,通過Looper物件來管理Queue中的Message。具體的大家可以檢視Handler的原始碼。
好了,看到我們的效果圖,三種方式實現的最終效果一致。
專案UI介面實現:3個Button,1個EditText,1個TextView。
專案實現原理:Handler機制實現UI更新。
專案邏輯實現:通過點選按鈕獲取輸入框的時間並顯示在一個TextView上,然後通過點選開始計時按鈕開始倒計時,可以通過停止計時按鈕停止計時。
實現方式一:Handler+Timer+TimerTask
通過該方式也是比較實用的,顧名思義,TimerTask計時器任務。由於Timer和TimerTask是同時出現的,TimerTask實現了Runnable介面,並且要求實現run方法。
首先,我們先編寫我們的佈局檔案activity_main.xml,三種實現方式統一使用了該佈局。
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/inputTime"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:hint="@null"/>
<Button
android:id="@+id/ensureTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點選" />
<TextView
android:id="@+id/showTime"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/startTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開始計時" />
<Button
android:id="@+id/stopTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止計時" />
</LinearLayout>
接下來就是我們的Activity實現步驟了。三種方式實現程式碼如下:
MainActivity.java
package com.mero.countTime;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private EditText inputTime;
private TextView showTime;
private Button ensureTime,startTime,stopTime;
private Timer timer = null;
private TimerTask task = null;
private int i;//顯示的倒計時數字
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();//例項化控制元件
}
/**例項化控制元件方法*/
private void initView() {
inputTime = (EditText) findViewById(R.id.inputTime);
showTime = (TextView) findViewById(R.id.showTime);
ensureTime = (Button) findViewById(R.id.ensureTime);
startTime = (Button) findViewById(R.id.startTime);
stopTime = (Button) findViewById(R.id.stopTime);
/**註冊監聽事件*/
ensureTime.setOnClickListener(this);
startTime.setOnClickListener(this);
stopTime.setOnClickListener(this);
};
@Override
public void onClick(View v) {
switch (v.getId()) {
/**當選擇點選按鈕的監聽事件*/
case R.id.ensureTime:
showTime.setText(inputTime.getText().toString());
i=Integer.parseInt(inputTime.getText().toString());
break;
/**當選擇開始計時按鈕的監聽事件*/
case R.id.startTime:
startTime();
break;
case R.id.stopTime:
stopTime();
break;
}
}
/**當選擇停止計時按鈕的監聽事件*/
private Handler handler=new Handler(){
/**重寫handleMessage方法*/
@Override
public void handleMessage(Message msg) {
showTime.setText(msg.arg1+"");
startTime();//執行計時方法
}
};
/**開始計時方法*/
private void startTime(){
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
i--;
Message message = handler.obtainMessage();//獲取Message物件
message.arg1 = i;//設定Message物件附帶的引數
handler.sendMessage(message);//向主執行緒傳送訊息
}
};
timer.schedule(task, 1000);//執行計時器事件
};
/**停止計時方法*/
private void stopTime(){
timer.cancel();//登出計時器事件
};
}
實現方式二:Handler+postDelayed+post
MainActivity.java
package com.mero.countTime;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private EditText inputTime;//宣告輸入框
private TextView showTime;//宣告用於顯示當前計時的時間
private Button ensureTime,startTime,stopTime;//宣告計時按鈕,停止計時按鈕和點選按鈕
private int i;//顯示的倒計時數字
private Runnable update;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();//例項化控制元件
}
/**例項化控制元件方法*/
private void initView() {
inputTime = (EditText) findViewById(R.id.inputTime);
showTime = (TextView) findViewById(R.id.showTime);
ensureTime = (Button) findViewById(R.id.ensureTime);
startTime = (Button) findViewById(R.id.startTime);
stopTime = (Button) findViewById(R.id.stopTime);
/**註冊監聽事件*/
ensureTime.setOnClickListener(this);
startTime.setOnClickListener(this);
stopTime.setOnClickListener(this);
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ensureTime:
showTime.setText(inputTime.getText().toString());//獲取輸入框上的時間並設定到顯示文字控制元件上
i=Integer.parseInt(inputTime.getText().toString());
break;
case R.id.startTime:
startTime();//開始計時
handler.post(update);
break;
case R.id.stopTime:
stopTime();//停止計時
break;
}
}
final Handler handler=new Handler();
/**開始計時方法*/
private void startTime(){
update=new Runnable(){
@Override
public void run() {
i--;
showTime.setText(i+"");
handler.postDelayed(update, 1000);//每隔1s將執行緒提交到執行緒佇列中
}
};
}
/**停止計時方法*/
private void stopTime(){
handler.removeCallbacks(update);//移除Runnable物件
};
}
實現方式三:Handler+Thread
MainActivity.java
package com.mero.countTime;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener{
private EditText inputTime;
private TextView showTime;
private Button ensureTime,startTime,stopTime;
private int i;//顯示的倒計時數字
private boolean flag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();//例項化控制元件
}
private void initView() {
inputTime = (EditText) findViewById(R.id.inputTime);
showTime = (TextView) findViewById(R.id.showTime);
ensureTime = (Button) findViewById(R.id.ensureTime);
startTime = (Button) findViewById(R.id.startTime);
stopTime = (Button) findViewById(R.id.stopTime);
/**註冊監聽事件*/
ensureTime.setOnClickListener(this);
startTime.setOnClickListener(this);
stopTime.setOnClickListener(this);
};
@Override
public void onClick(View v) {
switch (v.getId()) {
/**點選按鈕事件監聽*/
case R.id.ensureTime:
showTime.setText(inputTime.getText().toString());
i=Integer.parseInt(inputTime.getText().toString());
break;
/**開始按鈕事件監聽*/
case R.id.startTime:
flag=true;
startTime();
break;
/**停止按鈕事件監聽*/
case R.id.stopTime:
stopTime();
break;
}
}
final Handler handler=new Handler(){
public void handleMessage(Message msg) {
int p=msg.what;
showTime.setText(p+"");
};
};
/**開始計時方法*/
private void startTime() {
/**開啟一個新執行緒*/
new Thread(){
public void run() {
/**每睡眠1秒後傳送Message給Handler處理*/
for(int j=i;j>=0;j--){
if(flag==true){
try {
Thread.sleep(1000);
Message msg=new Message();
msg.what=j;//設定Message附帶的引數
handler.sendMessage(msg);//傳送Message物件給Handler
i=j;//將當前的時間傳遞給全域性時間變數
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}.start();
}
/**停止計時方法,通過設定boolean標誌為false來停止*/
@SuppressLint("NewApi")
private void stopTime(){
flag=false;
};
}
通過上面的程式碼,我們來總結一下。
方法一:簡單實用,尤其定時重新整理控制元件,效果非常good,使用簡單。注意Timer和TimerTask必須同時使用。使用Timer的schedule(task,delayed)方法提交TimerTask執行緒訊息。由Handler處理執行緒訊息。通過Timer.cancel(task)方式進行移除執行緒任務。
方法二:同方式一,簡單實用,原理實質一致。通過例項化Runnable物件來構造實現run建立新執行緒,在新執行緒中不斷將執行緒加入Looper池中進行處理。在主執行緒中通過post提交執行緒進行處理。通過handler.removeCallbacks(runnable)方式移除執行緒任務。
方法三:很經典實用的一種方式。通過for迴圈加上執行緒睡眠不斷建立新訊息。缺點是不易於控制,本文通過標誌進行控制。
好了,本篇文章就到此結束了。如果還有什麼問題的話,可以在下面留言。大家共同討論共同進步。謝謝閱讀 !
---------------------
作者:Mero技術部落格
來源:CSDN
原文:https://blog.csdn.net/qq_21004057/article/details/51582412
版權宣告:本文為博主原創文章,轉載請附上博文連結!