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();
}
});
}
}
代码运行结果如下图: