使用Handler+Thread也可以执行一个异步的任务,并可以通过handler更新UI。
注:这篇文章只讲API,关于Handler,Looper,Message,MessageQueue的原理我们下一篇讨论。
使用handler+Thread的典型方式是这样的:
必须重写Handler的handleMessage方法,默认实现是空实现。
package com.example.messagedemo2; 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; public class MainActivity extends Activity implements OnClickListener { protected static final int TEST = 1; protected static final String TAG = "MainActivity"; private Button but = null; private Handler myHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case TEST: //可以执行UI操作 Log.i(TAG,(String)msg.obj); break; } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); but = (Button) findViewById(R.id.but); but.setOnClickListener(this); } @Override public void onClick(View v) { if(v.getId() == R.id.but) { new Thread(new Runnable() { @Override public void run() { //TODO 执行耗时任务 Message msg = Message.obtain(); msg.what = TEST; msg.obj = "Rowandjj"; // msg.setTarget(myHandler);//如果不设置target那么调用sendToTarget将抛空针 // msg.sendToTarget();//其实是调用target也就是handler的sendMessage方法 myHandler.sendMessage(msg);//让handlet处理message } }).start(); } } }
我们在activity中创建了一个子线程,用于处理耗时任务,耗时任务处理完成之后,可能需要更改UI,但是我们知道,在子线程中是不能更改UI线程的UI的,故而我们需要定义一个Message对象,将Message发送给handler进行处理。为什么handler就能够处理UI呢?那是因为handler在创建的时候会绑定在创建的线程之上,因为上面的例子中我们的handler是绑定到UI线程上的,故而可以操作UI。
我们需要注意的是,handler在使用的时候需要一个Looper,handler内部通过调用Looper.myLooper方法返回与本线程绑定的looper对象,如果找不到这样的looper,则会抛出Runtime异常,异常信息为:Can‘t create handler inside thread that has not called Looper.prepare()。如果存在Looper对象,handler便可以拿到Looper对象中所包含的的mQueue变量,该变量的类型是MessageQueue,即消息队列,handler的sendMessage版本以及Post版本的方法都需要一个消息队列,拿到消息队列之后,这些方法都会调用MessageQueue类的enqueueMessage方法,即将消息入队。与此同时,Looper会调用loop方法不断的抽取消息队列中的消息(死循环),然后执行MessageQueue的next方法,即出队,出队的message会交由handler处理,handler通过dispatchMessage方法将消息分发下去。我们看到,经过一个循环,消息又被handler所接收。
如果该线程没有Looper,那我们应该创建一个Looper,通过调用Looper.prepare方法创建一个Looper对象,然后调用Looper.loop方法让消息队列运转起来。就像这样:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }需要注意的是,一个线程只能创建一个Looper对象,如果有多个Looper,将会抛Runtime异常,异常信息是这样的:Only one Looper may be created per thread。
通过上面的介绍,相信大家对Handler+Thread的使用方式有了一个大致的了解,下面总结下handler的作用:
1.在工作线程(子线程)中发送消息;
2.在UI线程中获取消息,并处理消息。(当然你也可以在子线程中创建handler获取/处理消息)
下面介绍一些API,方便大家使用。
首先是Handler的API。
handler的API分为两类,一类是post方法,另一类是sendMessage方法。post方法是将一个Runnable对象作为参数,该runnable对象中的run方法是被handler调用的(handleCallback方法),如果handler所在的线程为UI线程,则run方法可以更改UI。sendMessage方法是发送一个包装好的Message。并且这两类方法都提供了延时的重载方法,即延迟处理一个消息。
使用方式:
以下载网络图片为例:
1.采用post方法
package com.example.messagedemo1; 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 org.apache.http.util.EntityUtils; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.os.Bundle; import android.os.Handler; import android.view.Gravity; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; /** * @author Rowand jj * *一个使用handler+thread的简单示例 *下载网络图片 */ public class MainActivity extends Activity implements OnClickListener { protected static final String PATH = "http://c.hiphotos.baidu.com/image/w%3D2048/sign=4facebbeb0b7d0a27bc9039dffd77709/8d5494eef01f3a29cf6d52c29b25bc315c607c11.jpg"; protected static final int SET_IMAGE = 1; private Button but_load = null; private ImageView iv_show = null; private ProgressBar pb = null; private Handler myhandler = new Handler() { public void handleMessage(android.os.Message msg) { if(msg.what == SET_IMAGE) { iv_show.setImageBitmap((Bitmap) msg.obj); pb.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); but_load.setVisibility(View.VISIBLE); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv_show = (ImageView) findViewById(R.id.iv_show); but_load = (Button) findViewById(R.id.but_load); but_load.setOnClickListener(this); pb = createProgressBar(); } @Override public void onClick(View v) { if(v.getId() == R.id.but_load) { pb.setVisibility(View.VISIBLE); iv_show.setVisibility(View.GONE); but_load.setVisibility(View.GONE); new Thread(new Runnable() { @Override public void run() { HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(PATH); try { HttpResponse resp = client.execute(get); if(resp.getStatusLine().getStatusCode() == 200) { HttpEntity entity = resp.getEntity(); if(entity != null) { byte[] data = EntityUtils.toByteArray(entity); Options opts = new Options(); opts.inSampleSize = 2; final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0,data.length,opts); myhandler.post(new Runnable() { @Override public void run() { iv_show.setImageBitmap(bitmap); pb.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); but_load.setVisibility(View.VISIBLE); } }); } } } catch (Exception e) { e.printStackTrace(); } } }).start(); } } public ProgressBar createProgressBar() { FrameLayout rootContainer = (FrameLayout) findViewById(android.R.id.content); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT); params.gravity = Gravity.CENTER; ProgressBar pb = new ProgressBar(this); pb.setLayoutParams(params); pb.setVisibility(View.GONE); rootContainer.addView(pb); return pb; } }
2.采用sendMessage方法
在上面代码的基础上,将调用post方法那一段去掉,加上这样几行代码:
Message msg = Message.obtain(myhandler,SET_IMAGE,bitmap); msg.sendToTarget();或者是这样:
Message msg = myhandler.obtainMessage(SET_IMAGE,bitmap); msg.sendToTarget();
handler类的obtainMessage方法其实内部也是调用Message的obtain方法,这样做的好处是Message不用在指定handler了。
当然最原始的是这样:
Message msg = Message.obtain(myhandler,SET_IMAGE,bitmap); myhandler.sendMessage(msg);
上面的sendToTarget方法内部会调用target(即handler)的sendMessage方法。
上面就是Handler的基本使用介绍,下面简单介绍一下Message的API,我们看到上面的例子中我们发送消息时是通过obtain方法获取的一个消息的,那我们为什么不直接new Message呢??原因是这样的,message内部其实是有个message池的,使用message的obtain方法是从池中取出message,用完之后,looper会调用recycle方法回收message,将message重新放回池中,这样就可以防止资源的浪费了,所以推荐大家使用obtain方法获取message对象。
Message类的提供了四个公有成员变量,what,obj,arg1,arg2.what即标识消息的类型,handler通过switch消息的类型,来处理不同的消息。obj,arg1,arg2都是消息所携带的数据,建议存放简单的数据类型,如果需要传递复杂数据类型,应该使用setData(Bundle)方法,将复杂数据存到Bundle中。