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元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
