Android多线程——Handler
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)
方法体里的代码的执行线程的思路
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)有如下的一张图:
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
把压入消息队列有两大体系,Post
和sendMessage
:
Post
:Post
允许把一个Runnable
对象入队到消息队列中。它的方法有:post(Runnable)
、postAtTime(Runnable,long)
、postDelayed(Runnable,long)
sendMessage
:sendMessage
允许把一个包含消息数据的Message
对象压入到消息队列中。它的方法有:sendEmptyMessage(int)
、sendMessage(Message)
、sendMessageAtTime(Message,long)
、sendMessageDelayed(Message,long)
Post
对于Handler
的Post
方式来说,它会传递一个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(); } } }}
更多相关文章
- Android即时消息介绍
- Android Handler消息机制从原理到应用详解
- Android-线程笔记
- Android线程学习
- Android GWES之Android消息系统
- Android中的消息机制:Handler消息传递机制
- Android异步消息处理机制