Android多线程——Handler

参考:

  • Android 多线程编程的总结
  • Android–多线程之Handler
  • [译] 探索 Android 大杀器——Handler

如下的例子,button的点击事件中,在子线程中修改UI:

        button.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                new Thread(new Runnable() {                    @Override                    public void run() {                        try {                            Log.w("MainActivity", Thread.currentThread().getName());                            Thread.sleep(6000);                        } catch (InterruptedException e) {                            e.printStackTrace();                        }                        textView.setText("Changed From Thread");                    }                }).start();            }        });

此时Logcat会提示如下的错误:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

在Android多线程中有如下的原则:

  • 不要阻塞UI线程
  • 不要在UI线程之外访问UI组件

1.Handler.sendXXXMessage()等方法

在上面的Activity中定义一个Handler,重写handleMessage

Handler mHandler = new Handler(){          @Override          public void handleMessage(Message msg){              if(msg.what == 0x123){                  text.setText("Task Done!!");              }          }      };  

然后将工作线程的代码改为下面的样子

mRunnable = new Runnable() {              @Override              public void run() {                  try {                      Thread.sleep(5000);//模拟耗时任务                  } catch (InterruptedException e) {                      e.printStackTrace();                  }                  mHandler.sendEmptyMessage(0x123);//关于发消息的方法有很多,比如sendMessage(Message msg),sendMessageDelayed(Message msg, long delayMills)等等,可按具体需求选择,这里不作扩展              }          };  

一个线程只有一个Looper, 而一个Looper持有一个MessageQueue, 当调用Looper.prepare()时,Looper就与当前线程关联起来了(在Activity里没有显示调用Looper.prepare()是因为系统自动在主线程里帮我们调用了),而Handler是与Looper的线程是绑定的,查看Handler类的源码可以发现它几个构造函数,其中有接收一个Looper参数的,也有不接收Looper参数的,从上面的代码上看,我们没有为Handler指定Looper,那么Handler就默认更当前线程(即主线程)的Looper关联起来了,之所以啰嗦那么多就是因为这决定了Handler.handlerMessage(msg)方法体里的代码到底在哪个线程里执行,我们再梳理一下,Looper.prepare调用决定了Looper与哪个线程关联,间接决定了与这个Looper相关联的Handler.handlerMessage(msg)方法体里的代码执行的线程。(太啰嗦了)
现在回到上面的代码,我们的Handler是在主线程里的定义的,所以也默认跟主线程的Looper相关联,即handlerMessage方法的代码会在UI线程执行,因此更新TextView就不会报错了。下面这张图是弄清handlerMessage(msg)方法体里的代码的执行线程的思路
Android多线程——Handler_第1张图片

2.Handler.post(Runnable)

只要将上面代码中的

 mHandler.sendEmptyMessage(0x123);  

改成

mHandler.post(new Runnable() {      @Override      public void run() {          text.setText("Task Done!!");                         }   });  

Handler由以下部分组成:

  • Handler
  • Message
  • Message Queue
  • Looper

在Android 异步通信:手把手教你使用Handler消息传递机制(含实例Demo)有如下的一张图:
Android多线程——Handler_第2张图片

Handler

在Activity中定义一个Handler

Handler允许你发送和处理与线程的MessageQueue关联的Message和Runnable对象。每个Handler实例都与一个线程和该线程的消息队列(message queue)相关联。当创建一个新的Handler时,它被绑定到创建它的线程/消息队列,它将messages和runnables传递给该消息队列并执行

There are two main uses for a Handler: (1) to schedule messages and runnables to be executed at some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
Handler有两个主要用途:(1)安排messages和runnables在将来的某个时刻执行; (2)在不同于自己的线程上执行的操作。

Handler可以把一个Message对象或者Runnable对象压入到消息队列中,进而在UI线程中获取Message或者执行Runnable对象,所以Handler把压入消息队列有两大体系,PostsendMessage

  • PostPost允许把一个Runnable对象入队到消息队列中。它的方法有:post(Runnable)postAtTime(Runnable,long)postDelayed(Runnable,long)
  • sendMessagesendMessage允许把一个包含消息数据的Message对象压入到消息队列中。它的方法有:sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message,long)sendMessageDelayed(Message,long)

Post

对于HandlerPost方式来说,它会传递一个Runnable对象到消息队列中,在这个Runnable对象中,重写run()方法。一般在这个run()方法中写入需要在UI线程上的操作

        btnMes1.setOnClickListener(new View.OnClickListener() {                        @Override            public void onClick(View v) {                // 新启动一个子线程                new Thread(new Runnable() {                                        @Override                    public void run() {                        // tvMessage.setText("...");                        // 以上操作会报错,无法再子线程中访问UI组件,UI组件的属性必须在UI线程中访问                        // 使用post方式修改UI组件tvMessage的Text属性                        handler.post(new Runnable() {                                                @Override                            public void run() {                                tvMessage.setText("使用Handler.post在工作线程中发送一段执行到消息队列中,在主线程中执行。");                                                    }                        });                                                    }                }).start();            }        });

Message

Message是容纳任意数据的容器。生产线程发送消息给 Handler,Handler 将消息加入到消息队列中。

如果对于一般的数据,Message提供了getData()setData()方法来获取与设置数据,其中操作的数据是一个Bundle对象

还有另外一种方式在Message中传递对象,那就是使用Message自带的obj属性传值,它是一个Object类型,所以可以传递任意类型的对象,Message自带的有如下几个属性:

  • int arg1:参数一,用于传递不复杂的数据,复杂数据使用setData()传递。
  • int arg2:参数二,用于传递不复杂的数据,复杂数据使用setData()传递。
  • Object obj:传递一个任意的对象。
  • int what:定义的消息码,一般用于设定消息的标志。

对于Message对象,一般并不推荐直接使用它的构造方法得到,而是建议通过使用Message.obtain()这个静态的方法或者Handler.obtainMessage()获取

package com.bgxt.datatimepickerdemo;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.app.ProgressDialog;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.view.View;import android.widget.Button;import android.widget.ImageView;public class HandlerMessageActivity1 extends Activity {    private Button btnDown;    private ImageView ivImage;    private static String image_path = "http://ww4.sinaimg.cn/bmiddle/786013a5jw1e7akotp4bcj20c80i3aao.jpg";    private ProgressDialog dialog;    private static int IS_FINISH = 1;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.asynctask_activity);        btnDown = (Button) findViewById(R.id.btnDown);        ivImage = (ImageView) findViewById(R.id.ivSinaImage);        dialog = new ProgressDialog(this);        dialog.setTitle("提示信息");        dialog.setMessage("正在下载,请稍后...");        dialog.setCancelable(false);        btnDown.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                    new Thread(new MyThread()).start();                    dialog.show();            }        });    }    private  Handler handler = new Handler() {        // 在Handler中获取消息,重写handleMessage()方法        @Override        public void handleMessage(Message msg) {                        // 判断消息码是否为1            if(msg.what==IS_FINISH){                byte[] data=(byte[])msg.obj;                Bitmap bmp=BitmapFactory.decodeByteArray(data, 0, data.length);                ivImage.setImageBitmap(bmp);                dialog.dismiss();            }        }    };    public class MyThread implements Runnable {        @Override        public void run() {            HttpClient httpClient = new DefaultHttpClient();            HttpGet httpGet = new HttpGet(image_path);            HttpResponse httpResponse = null;            try {                httpResponse = httpClient.execute(httpGet);                if (httpResponse.getStatusLine().getStatusCode() == 200) {                    byte[] data = EntityUtils.toByteArray(httpResponse                            .getEntity());                    // 获取一个Message对象,设置what为1                    Message msg = Message.obtain();                    msg.obj = data;                    msg.what = IS_FINISH;                    // 发送这个消息到消息队列中                    handler.sendMessage(msg);                }            } catch (Exception e) {                e.printStackTrace();            }        }    }}

更多相关文章

  1. Android即时消息介绍
  2. Android Handler消息机制从原理到应用详解
  3. Android-线程笔记
  4. Android线程学习
  5. Android GWES之Android消息系统
  6. Android中的消息机制:Handler消息传递机制
  7. Android异步消息处理机制

随机推荐

  1. Android控件——ViewFlipper的使用,垂直滚
  2. android 框架之WIFI系统和系统架构全面总
  3. Android之Handler、Looper、MessageQueue
  4. android Service 简单例子
  5. Android线程间通信-Handler消息机制
  6. 简单android拨号器的实现
  7. 单例模式之传递Context参数
  8. android是32-bit系统还是64-bit系统
  9. 自定义简单的ProgressBar
  10. Android内存分配的注意事项