Handler Android应用开发消息机制
2015-10-21 21:09
567 查看
一、背景
当应用程序启动时,Android首先会开启一个主线程(即UI线程),主线程为管理界面中的UI控件、进行事件分发,比如:你要点击一个Button,Android会分发事件到Button上来响应你的操作。如果此时需要一个耗时操作,例如:联网读取数据或读取本地一个较大文本时,你不能把这些操作放在主线程中。如果你放在主线程中,界面会出现假死现象,如果5s后还没有完成的话则会收到Android系统的一个错误提示“强制关闭”。此时,我们则要把这些耗时的操作放在一个子线程中,因此此子线程涉及到了UI更新,而更新UI只能在主线程中更新,这时我们就需要引入Handler来实现该操作。它与子线程可以通过Message对象来传递消息,这个时候Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)的Message对象(里面包含数据且该消息对象会放在主线程的消息队列MQ中),然后在主线程中处理这些消息从而可以对UI控件进行更新等操作。
二、Android的消息处理有三个核心类:Looper、Handler和Message
1、Looper
Looper使一个普通的线程变成Looper线程。Looper线程就是循环工作的线程,创建looper线程代码如下:
效果图如下:
注:一个线程(Thread)最多有且只能有一个Looper对象,该Looper对象内部包含了一个消息队列MessageQueue
由上面代码可以知道Looper对象的prepare()方法将会初始化当前线程为looper线程,并且实例化Looper和初始化消息队列;
而Looper的loop()方法则是循环取消息,并分发到对应的hangdler处理消息,原理如下图:
Looper类除了prepare()和loop()方法外,还有其他的方法,如下:
Looper.myLooper() :得到当前线程的Looper对象;
getThread() :得到Looper对象所属的线程
quit() :结束looper循环
2、Handler消息处理(异步处理处理)
主要接受子线程发送的消息,并用此数据配合主线程更新UI
解释:Handler扮演了往MQ上添加消息和处理消息(只处理自己发出的消息)的角色,即:通知MQ要要执行一个任务(sendMessage),并在looper.loop()方法关联到此Handler时执行任务(handlerMessage())然后Handler处理消息,整个过程是异步执行的。
注:因为Handler创建时会关联一个looper,默认的构造方法将关联到当前线程的looper,所以上面说的:“looper.loop()方法关联到此Handler”可以理解为handler创建时关联的looper是执行looper.loop()方法的looper线程的looper。所以,Handler关联的looper是可以设置的,可以为子looper线程的looper也可以是主线程(UI线程——也是一个looper线程)的looper,Handler关联的looper是哪个线程的looper它就会在那个线程执行(即:Handler的执行线程只与它关联的looper有关,与它实例化的位置无关)。Handler的实例化代码如下:
由效果图可见:一个线程最多有且只能有一个looper,但可以有多个Handler。
上面说的在子线程更新UI界面则可以实现了,结构代码如下:
XML文件:
Java代码:
我们可以看见,当点击Button后TextView的内容已经被修改了,所以,Handler能够实现在子线程中对UI界面的更新操作。
当应用程序启动时,Android首先会开启一个主线程(即UI线程),主线程为管理界面中的UI控件、进行事件分发,比如:你要点击一个Button,Android会分发事件到Button上来响应你的操作。如果此时需要一个耗时操作,例如:联网读取数据或读取本地一个较大文本时,你不能把这些操作放在主线程中。如果你放在主线程中,界面会出现假死现象,如果5s后还没有完成的话则会收到Android系统的一个错误提示“强制关闭”。此时,我们则要把这些耗时的操作放在一个子线程中,因此此子线程涉及到了UI更新,而更新UI只能在主线程中更新,这时我们就需要引入Handler来实现该操作。它与子线程可以通过Message对象来传递消息,这个时候Handler就承担着接受子线程传过来的(子线程用sendMessage()方法传递)的Message对象(里面包含数据且该消息对象会放在主线程的消息队列MQ中),然后在主线程中处理这些消息从而可以对UI控件进行更新等操作。
二、Android的消息处理有三个核心类:Looper、Handler和Message
1、Looper
Looper使一个普通的线程变成Looper线程。Looper线程就是循环工作的线程,创建looper线程代码如下:
public class MyLoopThread extends Thread { @Override public void run() { Looper.prepare(); //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue //.....其他处理,如实例化Handler等 Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理 } }
效果图如下:
注:一个线程(Thread)最多有且只能有一个Looper对象,该Looper对象内部包含了一个消息队列MessageQueue
由上面代码可以知道Looper对象的prepare()方法将会初始化当前线程为looper线程,并且实例化Looper和初始化消息队列;
而Looper的loop()方法则是循环取消息,并分发到对应的hangdler处理消息,原理如下图:
Looper类除了prepare()和loop()方法外,还有其他的方法,如下:
Looper.myLooper() :得到当前线程的Looper对象;
getThread() :得到Looper对象所属的线程
quit() :结束looper循环
2、Handler消息处理(异步处理处理)
主要接受子线程发送的消息,并用此数据配合主线程更新UI
解释:Handler扮演了往MQ上添加消息和处理消息(只处理自己发出的消息)的角色,即:通知MQ要要执行一个任务(sendMessage),并在looper.loop()方法关联到此Handler时执行任务(handlerMessage())然后Handler处理消息,整个过程是异步执行的。
注:因为Handler创建时会关联一个looper,默认的构造方法将关联到当前线程的looper,所以上面说的:“looper.loop()方法关联到此Handler”可以理解为handler创建时关联的looper是执行looper.loop()方法的looper线程的looper。所以,Handler关联的looper是可以设置的,可以为子looper线程的looper也可以是主线程(UI线程——也是一个looper线程)的looper,Handler关联的looper是哪个线程的looper它就会在那个线程执行(即:Handler的执行线程只与它关联的looper有关,与它实例化的位置无关)。Handler的实例化代码如下:
public class MyAndroidStudyMainActivity extends Activity { private Handler mHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_android_study_main); mHandler = new Handler(getMainLooper(), new Callback() {//实例化Handler并关联主线程的looper,当new Callback() //前面没有参数时实例化的Handler关联的则是当前所处线程的looper @Override public boolean handleMessage(Message msg) { //.......处理消息(耗时操作) return true; } }); } }将Handler加入到looperThread线程类中时,则可以异步处理消息了(如:在子线程中实例化Handler时关联UI线程的looper,我们在子线程中联网取数据后把数据发送给Handler,然后Handler处理消息就可以更新UI界面了)。结构代码如下:
public class MyLoopThread extends Thread { private Handler mHandler1; private Handler mHandler2; @Override public void run() { Looper.prepare(); //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue //实例化两个Handler mHandler1 = new Handler(); mHandler2 = new Handler(); Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理 } }效果图如下:
由效果图可见:一个线程最多有且只能有一个looper,但可以有多个Handler。
上面说的在子线程更新UI界面则可以实现了,结构代码如下:
public class MyLoopThread extends Thread { private Handler mHandler1; @Override public void run() { Looper.prepare(); //将当前线程初始化为Looper线程,实例Looper,初始化消息队列MessageQueue //实例化Handler mHandler1 = new Handler(getMainLooper(),new Callback() { @Override public boolean handleMessage(Message msg) { //......更新UI界面 return true; } }); Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理 } }【例】如下图所示,要在子线程中把界面上TextView控件的内容修改
XML文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/holo_blue_dark" android:gravity="center" android:orientation="vertical" > <TextView android:id="@+id/message_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="我是启动界面" android:textColor="@android:color/white" android:textSize="24sp" /> <Button android:id="@+id/send_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="发送消息到主线程执行" /> </LinearLayout>
Java代码:
public class HandlerActivity extends Activity { private MyLoopThread myLooper;//定义子线程 private TextView mMessageTxt;//定义TextView private Button mSendMsgBtn;//定义Button private int count = 1; // 标识消息 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);//隐藏标题 setContentView(R.layout.activity_handler_layout); mMessageTxt = (TextView) findViewById(R.id.message_txt); mSendMsgBtn = (Button) findViewById(R.id.send_btn); myLooper = new MyLoopThread();//实例子线程 myLooper.start();//启动子线程 mSendMsgBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Message msg = Message.obtain(); msg.what = 11; msg.arg1 = count++; myLooper.mHandler1.sendMessage(msg);//Handler发送消息 } }); } /** * 消息在主线程执行的情况 消息在子线程执行的情况 * */ public class MyLoopThread extends Thread { public Handler mHandler1; @Override public void run() { Looper.prepare(); //实例Looper,初始化消息队列MessageQueue mHandler1 = new Handler(getMainLooper(), new Callback() { @Override public boolean handleMessage(Message msg) {//Handler处理消息,更新UI int arg1 = msg.arg1; mMessageTxt.setText("主线程执行 : 消息 " + arg1); return true; } }); Looper.loop(); //循环从消息队列MessageQueue取消息分发给Handler处理 } } }运行程序后如下图:
我们可以看见,当点击Button后TextView的内容已经被修改了,所以,Handler能够实现在子线程中对UI界面的更新操作。
相关文章推荐
- android小问题:如何在Listview中获取上下文菜单对应的item项
- 解决Android Stidio在模拟器里中文乱码的问题
- Android之Android Studio 快捷键整理分享
- 开源选型之Android三大图片缓存原理、特性对比
- Android+jsp +html 文件上传案例 已测试 成功通过
- android小问题: Notification通知栏 中用Intent传值无效
- Android读书笔记-----View动画
- Android 【百度地图】 基础配置(1)
- Android语音信息相关技术
- Android 获取手机中所有图片
- android-帧动画、布局动画
- Android 多Fragment共享数据的MVP模式实现
- Android手机Log
- Android: Type Method 'NewStringUTF' could not be resolved
- Android RecyclerView 使用完全解析 体验艺术般的控件
- Android Api Demos登顶之路(106)View-->DragAndDrop
- android开发之GPS定位详解
- Android-状态栏透明导致android:windowSoftInputMode属性失效
- live555移植到Android过程.
- 项目开发,我的名字不可能这么可爱——开篇