Android Handler
?
【轉載】
?
原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。 http://lichen.blog.51cto.com/697816/486402
?
?
?????? 此文是關于Handler的。 Handler主要接受子線程發送的數據, 并用此數據配合主線程更新UI。 當應用程序啟動時,Android首先會開啟一個主線程 (也就是UI線程) ,主線程為管理界面中的UI控件,進行事件分發, 比如說,你要是點擊一個 Button ,Android會分發事件到Button上,來響應你的操作。如果此時需要一個耗時的操作,例如: 聯網讀取數據,或者讀取本地較大的一個文件的時候,你不能把這些操作放在主線程中,如果你放在主線程中的話,界面會出現假死現象, 如果5秒鐘還沒有完成的話,會收到Android系統的一個錯誤提示 "強制關閉"。
?
??????? 這個時候我們需要把這些耗時的操作,放在一個子線程中,因為子線程涉及到UI更新,Android主線程不是線程安全的,也就是說,更新UI只能在主線程 中更新,子線程中操作是危險的。這個時候,Handler就出現了,來解決這個復雜的問題。Handler運行在主線程中(UI線程中),它與子線程可以 通過Message對象來傳遞數據,這個時候,Handler就承擔著接受子線程傳過來的(子線程用sedMessage()方法傳遞)Message對 象(里面包含數據),把這些消息放入主線程隊列中,配合主線程進行更新UI。
?
?
????? Handler 是 android 提供的對于異步消息處理的方案。 Handler 的特點是與調用者處于同一線程,如果 Handler 里面做耗時的動作,調用者線程會阻塞。 Android UI 操作不是線程安全的,并且這些操作必須在 UI 線程中執行。 ?
?
????? 所以, Handler 經常被用來在另外的線程中更新 UI 界面。因為 UI 操作必須在 UI 線程中完成,可以通過 Handler 在別的線程中向 UI 線程發送刷新消息, UI 線程收到消息后執行相關操作。主要用到下面兩個函數:
?
public final boolean sendMessage (Message msg) ?? ???? 發送消息
public void handleMessage(Message msg) ?????????????????? 處理消息
?
?????? Handler 對于 Message 的處理不是并發的。一個 Looper? 只有處理完一條 Message 才會讀取下一條,所以消息的處理是阻塞形式的。
?
??????? Handler一些特點:Handler可以分發Message對象和Runnable對象到主線程中, 每個Handler實例,都會綁定到創建他的線程中(一般是位于主線程),它有兩個作用: (1): 安排消息或Runnable 在某個主線程中某個地方執行, (2)安排一個動作在不同的線程中執行。
?
?????? Handler中分發消息經常使用post(Runnable)和sendMessage(Message)方法。
?
?????? 先是sendMessage的例子:
?
main.xml
<?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"
>
<Button
android:id="@+id/startButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="開始演示"
android:onClick="startIt"
/>
<ProgressBar
android:id="@+id/bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:max="500"
android:progress="0"
/>
<TextView
android:id="@+id/text"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="演示信息:"
/>
</LinearLayout>
?
?
Demo.java
package com.zhouzijing.android.demo;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class Demo extends Activity{
private final static String TAG = "DEMO";
private TextView textView;
private Button startButton;
private ProgressBar progressBar;
private MyHandler myHandler;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textView=(TextView)findViewById(R.id.text);
startButton = (Button)findViewById(R.id.startButton);
progressBar=(ProgressBar)findViewById(R.id.bar);
progressBar.setMax(100);
myHandler=new MyHandler();
}
/**
* [開始演示]的按鈕事件.
* @param v
*/
public void startIt(View v) {
Button btn = (Button)v;
//禁止演示按鈕重復點擊
btn.setClickable(false);
btn.setText("演示中...");
progressBar.setProgress(0);
textView.setText("演示信息:");
new Thread(new MyThread(1)).start();
}
//在對UI進行更新時,執行時所在的線程為主UI線程
//繼承Handler類時,必須重寫handleMessage方法
class MyHandler extends Handler{
public MyHandler(){
}
public MyHandler(Looper l){
super(l);
}
@Override
public void handleMessage(Message msg) {
//執行接收到的通知,此時執行的順序是按照隊列進行,即先進先出
Log.i(TAG,"Handler--The ThreadId is: "+Thread.currentThread().getId());
int what = msg.what;
//根據message的what屬性來進行處理
switch(what){
case 100:
Bundle b=msg.getData();
String textStr1=b.getString("textStr");
textView.append(textStr1);//更改TextView中的值
int barValue=b.getInt("barValue");
progressBar.setProgress(barValue);//更改進度條當中的值
if(barValue==100){
//允許演示按鈕點擊
startButton.setText("演示結束,可以點擊重新演示");
startButton.setClickable(true);
}
break;
default:
//其他message扔回上級處理.
super.handleMessage(msg);
}
}
}
//該線程將會在單獨的線程中運行
class MyThread implements Runnable{
int index=1;
public MyThread(int index) {
this.index = index;
}
@Override
public void run() {
while(index<11){
Log.i(TAG,"Thread--The ThreadId is: "+Thread.currentThread().getId());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int what = 100;
//從系統message池中取出1個message對象,并設置what屬性.
Message msg = myHandler.obtainMessage(what);
Bundle b=new Bundle();
b.putString("textStr", "線程運行"+index+"次");
b.putInt("barValue", index*10);
index++;
msg.setData(b);
myHandler.sendMessage(msg);//通過sendMessage向Handler發送更新UI的消息
}
}
}
}
?
?
簡單的說,Activity的onCreate方法里啟動一個線程,在這個線程的run方法里使用一個Message對象并使用Handler的 sendMessage方法發送到隊列中,最后在Activity里new一個Handler的內部類實現handMessage方法,使用這個方法把隊 列中的Message對象取出來以實現異步操作。
?
得到Message對象的方法:(1)new Message();(2)handler對象的obtainMessage();前者需要新建一個Message對象,而后者直接從系統的Message池中獲取1個(不用創建對象)。建議使用后者!
?
?
?
????? 然后是post的例子,這里稍微說一下,直接使用new Handler().post(Runnable)這樣的寫法并沒有新開線程。
?
xml配置文件
<?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"
>
<EditText
android:id="@+id/edtDownloadUrl"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="http://www.zhouzijing.com/"
/>
<Button
android:id="@+id/btnHandlerPostRunnable"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="開始下載"
android:onClick="startIt"
/>
<ProgressBar
android:id="@+id/pBarDownload"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="?android:attr/progressBarStyleHorizontal"
/>
<TextView
android:id="@+id/tvProgressBarPercent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</LinearLayout>
?
由于下面代碼要訪問網絡和在sdcard上寫文件,因此需要在AndroidManifest.xml配置權限:
?
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
?
?
代碼文件:
package com.zhouzijing.android.demo;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import android.app.Activity;
import android.app.AlertDialog;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
public class HandlerPostRunnable extends Activity {
private EditText edtDownloadUrl;
private ProgressBar pBarDownload;
private TextView tvProgressBarPercent;
private Button btnHandlerPostRunnable;
private int fileSize = 0;
private int progress = 0;
private final String TAG = "HandlerPostRunnable";
private Handler handler = new Handler();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.handler_post_runnable);
//定義組件
edtDownloadUrl = (EditText)findViewById(R.id.edtDownloadUrl);
pBarDownload = (ProgressBar)findViewById(R.id.pBarDownload);
tvProgressBarPercent = (TextView)findViewById(R.id.tvProgressBarPercent);
btnHandlerPostRunnable = (Button)findViewById(R.id.btnHandlerPostRunnable);
}
//開始演示-按鈕事件
public void startIt(View v) {
Button btn = (Button)v;
btn.setText("下載中...");
btn.setClickable(false);
downloadFile(edtDownloadUrl.getText().toString());
}
/**
* 獲取文件名.
* @param url
* @return
*/
private String getFileNameFromUrl(String url) {
String fileName = url.substring(url.lastIndexOf("/") + 1);
return fileName;
}
/**
* 下載文件.
* @param url
*/
private void downloadFile(final String url) {
new Thread() {
public void run() {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(url);
HttpResponse response = null;
FileOutputStream fos = null;
try {
response = client.execute(get);
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
if (is != null) {
File file = new File(
Environment.getExternalStorageDirectory(),
getFileNameFromUrl(url));
fos = new FileOutputStream(file);
fileSize = (int)entity.getContentLength();
progress = 0;
downloadStart();
byte[] buf = new byte[1024];
int ch = -1;
while ((ch = is.read(buf)) != -1) {
fos.write(buf, 0, ch);
progress = progress+ch;
downloading();
}
}
fos.flush();
fos.close();
fos = null;
downloadFinished();
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
downloadError(e);
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
Log.e(TAG, e.getMessage(), e);
}
fos = null;
}
}
}
}.start();
}
// 下載開始
private void downloadStart() {
handler.post(new Runnable() {
public void run() {
pBarDownload.setMax(fileSize);
pBarDownload.setProgress(0);
}
});
}
/**
* 設置下載進度百分比,
*/
private void setDownloadPercent() {
float percent = (float)progress/(float)fileSize*100;
int iPercent = (int)percent;
tvProgressBarPercent.setText(String.valueOf(iPercent)+"%");
}
// 下載中.
private void downloading() {
handler.post(new Runnable() {
public void run() {
pBarDownload.setProgress(progress);
setDownloadPercent();
}
});
}
// 下載成功結束
private void downloadFinished() {
handler.post(new Runnable() {
public void run() {
pBarDownload.setProgress(fileSize);
btnHandlerPostRunnable.setText("下載成功,點擊再開始下載");
btnHandlerPostRunnable.setClickable(true);
setDownloadPercent();
new AlertDialog.Builder(HandlerPostRunnable.this)
.setTitle("提示").setMessage("下載成功")
.setPositiveButton("確定", null).show();
}
});
}
// 下載失敗
private void downloadError(final Exception err) {
handler.post(new Runnable() {
public void run() {
btnHandlerPostRunnable.setText("下載失敗,點擊再開始下載");
btnHandlerPostRunnable.setClickable(true);
new AlertDialog.Builder(HandlerPostRunnable.this)
.setTitle("提示").setMessage("下載失敗:"+err.getMessage())
.setPositiveButton("確定", null).show();
}
});
}
}
?
代碼運行結果如下圖:
?
?
?
?
?
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

