【安卓笔记】Handler+Thread使用浅析

之前介绍了AsyncTask,今天介绍Handler+Thread的使用方式。
使用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中。



【安卓笔记】Handler+Thread使用浅析,布布扣,bubuko.com

【安卓笔记】Handler+Thread使用浅析

上一篇:爱立信交换培训总结(第四天)


下一篇:一个App调用另一个App的Activity的方法