Android中的Handler信息传递机制
2016-09-18 15:16
316 查看
在介绍Handler之前有必要介绍一下:主线程(UI线程)
当应用启动,系统会创建一个主线程(mainthread)。这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components
from the
android.widgetand
android.viewpackages))发生交互。所以main thread也叫UI thread也即UI线程
系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。
结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。
当App做一些比较重(intensive)的工作的时候,除非你合理地实现,否则单线程模型的performance会很poor。
特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application
not responding)的对话框。
另外,Andoid UI toolkit并不是线程安全的,所以你不能从非UI线程来操纵UI组件。你必须把所有的UI操作放在UI线程里,所以Android的单线程模型有两条原则:
1.不要阻塞UI线程。
2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:
and
android.view)。
所以注意!!!
只有在UI线程中的对象才能操作UI线程中的对象,为了将非UI线程中的数据传送到UI线程,可以使用一个 Handler运行在UI线程中。
用一副图来简单表示上述:
正题开始Handler机制是怎么样的呢?
说到Handler就必须说到他的兄弟们:
Message,MessageQueue,Looper
Handler:作用是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理。
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入 队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
Message:Handler接收与处理的消息对象
MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;Message Queue是一个消息队列,用来存放通过Handler发布的消息。Android在第一次启动程序时会默认会为UI thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers
等等。
简单点说:
当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送消息,而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!
·sendEmptyMessage(int what):发送空消息
·sendEmptyMessageDelayed(int what,longdelayMillis):指定延时多少毫秒后发送空信息
·sendMessage(Message msg):立即发送信息
·sendMessageDelayed(Message msg,long
delayMillis):指定延时多少毫秒后发送信息 注意:延迟的消息,不是延迟发送,而是延迟处理
·final boolean hasMessage(intwhat):检查消息队列中是否包含what属性为指定值的消息如果是参数为(int
what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息
移除消息
移除指定what标识的消息:removeMessages(int what)
移除所有未执行的消息:removeCallbacksAndMessages(null)
1 )直接调用Looper.prepare()方法即可为当前线程创建Looper对象,而它的构造器会创建配套的MessageQueue; 2
)创建Handler对象,重写handleMessage( )方法就可以处理来自于其他线程的信息了! 3
)调用Looper.loop()方法启动Looper
使用示例:输入一个数,计算后通过Toast输出在这个范围内的所有质数
实现代码: main.xml:
MainActivity.java:
当应用启动,系统会创建一个主线程(mainthread)。这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components
from the
android.widgetand
android.viewpackages))发生交互。所以main thread也叫UI thread也即UI线程
系统不会为每个组件单独创建线程,在同一个进程里的UI组件都会在UI线程里实例化,系统对每一个组件的调用都从UI线程分发出去。
结果就是,响应系统回调的方法(比如响应用户动作的onKeyDown()和各种生命周期回调)永远都是在UI线程里运行。
当App做一些比较重(intensive)的工作的时候,除非你合理地实现,否则单线程模型的performance会很poor。
特别的是,如果所有的工作都在UI线程,做一些比较耗时的工作比如访问网络或者数据库查询,都会阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程blocked的时间太长(大约超过5秒),用户就会看到ANR(application
not responding)的对话框。
另外,Andoid UI toolkit并不是线程安全的,所以你不能从非UI线程来操纵UI组件。你必须把所有的UI操作放在UI线程里,所以Android的单线程模型有两条原则:
1.不要阻塞UI线程。
2.不要在UI线程之外访问Android UI toolkit(主要是这两个包中的组件:
android.widget
and
android.view)。
所以注意!!!
只有在UI线程中的对象才能操作UI线程中的对象,为了将非UI线程中的数据传送到UI线程,可以使用一个 Handler运行在UI线程中。
用一副图来简单表示上述:
正题开始Handler机制是怎么样的呢?
说到Handler就必须说到他的兄弟们:
Message,MessageQueue,Looper
Handler:作用是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理。
Looper扮演着一个Handler和消息队列之间通讯桥梁的角色。程序组件首先通过Handler把消息传递给Looper,Looper把消息放入 队列。Looper也把消息队列里的消息广播给所有的Handler,Handler接受到消息后调用handleMessage进行处理。
Message:Handler接收与处理的消息对象
MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;Message Queue是一个消息队列,用来存放通过Handler发布的消息。Android在第一次启动程序时会默认会为UI thread创建一个关联的消息队列,可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,activities,broadcast receivers
等等。
简单点说:
当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送消息,而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!
Handler的相关方法:
·void handleMessage(Messagemsg):处理消息的方法,通常是用于被重写!·sendEmptyMessage(int what):发送空消息
·sendEmptyMessageDelayed(int what,longdelayMillis):指定延时多少毫秒后发送空信息
·sendMessage(Message msg):立即发送信息
·sendMessageDelayed(Message msg,long
delayMillis):指定延时多少毫秒后发送信息 注意:延迟的消息,不是延迟发送,而是延迟处理
·final boolean hasMessage(intwhat):检查消息队列中是否包含what属性为指定值的消息如果是参数为(int
what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息
移除消息
移除指定what标识的消息:removeMessages(int what)
移除所有未执行的消息:removeCallbacksAndMessages(null)
Handler的使用示例:
1)Handler写在主线程中
在主线程中,因为系统已经初始化了一个Looper对象,所以我们直接创建Handler对象,就可以进行信息的发送与处理了!01
public class HandlerTestActivity extends Activity { |
02 | private TextView tv; |
03 | private static final int UPDATE = 0 ; |
04 | private Handler handler = new Handler() { |
05 |
06 | @Override |
07 | public void handleMessage(Message msg) { |
08 | // TODO 接收消息并且去更新UI线程上的控件内容 |
09 | if (msg.what == UPDATE) { |
10 | // Bundle b = msg.getData(); |
11 | // tv.setText(b.getString("num")); |
12 | tv.setText(String.valueOf(msg.obj)); |
13 | } |
14 | super .handleMessage(msg); |
15 | } |
16 | }; |
17 |
18 | /** Called when the activity is first created. */ |
19 | @Override |
20 | public void onCreate(Bundle savedInstanceState) { |
21 | super .onCreate(savedInstanceState); |
22 | setContentView(R.layout.main); |
23 | tv = (TextView) findViewById(R.id.tv); |
24 |
25 | new Thread() { |
26 | @Override |
27 | public void run() { |
28 | // TODO 子线程中通过handler发送消息给handler接收,由handler去更新TextView的值 |
29 | try { |
30 | for ( int i = 0 ; i < 100 ; i++) { |
31 | Thread.sleep( 500 ); |
32 | Message msg = new Message(); |
33 | msg.what = UPDATE; |
34 | // Bundle b = new Bundle(); |
35 | // b.putString("num", "更新后的值:" + i); |
36 | // msg.setData(b); |
37 | msg.obj = "更新后的值:" + i; |
38 | handler.sendMessage(msg); |
39 | } |
40 | } catch (InterruptedException e) { |
41 | e.printStackTrace(); |
42 | } |
43 | } |
44 | }.start(); |
45 | } |
46 |
47 | } |
2)Handler写在子线程中
如果是Handler写在了子线程中的话,我们就需要自己创建一个Looper对象了!创建的流程如下:1 )直接调用Looper.prepare()方法即可为当前线程创建Looper对象,而它的构造器会创建配套的MessageQueue; 2
)创建Handler对象,重写handleMessage( )方法就可以处理来自于其他线程的信息了! 3
)调用Looper.loop()方法启动Looper
使用示例:输入一个数,计算后通过Toast输出在这个范围内的所有质数
实现代码: main.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/etNum"
android:inputType="number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入上限"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="cal"
android:text="计算"/>
</LinearLayout>
MainActivity.java:
publicclassCalPrimeextendsActivity
{
staticfinalString UPPER_NUM ="upper";
EditText etNum;
CalThread calThread;
// 定义一个线程类
classCalThreadextendsThread
{
publicHandler mHandler;
publicvoid run()
{
Looper.prepare();
mHandler =newHandler()
{
// 定义处理消息的方法
@Override
publicvoid handleMessage(Message msg)
{
if(msg.what ==0x123)
{
int upper = msg.getData().getInt(UPPER_NUM);
List<Integer> nums =newArrayList<Integer>();
// 计算从2开始、到upper的所有质数
outer:
for(int i =2; i <= upper ; i++)
{
// 用i处于从2开始、到i的平方根的所有数
for(int j =2; j <=Math.sqrt(i); j++)
{
// 如果可以整除,表明这个数不是质数
if(i !=2&& i % j ==0)
{
continue outer;
}
}
nums.add(i);
}
// 使用Toast显示统计出来的所有质数
Toast.makeText(CalPrime.this, nums.toString()
,Toast.LENGTH_LONG).show();
}
}
};
Looper.loop();
}
}
@Override
publicvoid onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
etNum =(EditText)findViewById(R.id.etNum);
calThread =newCalThread();
// 启动新线程
calThread.start();
}
// 为按钮的点击事件提供事件处理函数
publicvoid cal(View source)
{
// 创建消息
Message msg =newMessage();
msg.what =0x123;
Bundle bundle =newBundle();
bundle.putInt(UPPER_NUM ,
Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
// 向新线程中的Handler发送消息
calThread.mHandler.sendMessage(msg);
}
}
相关文章推荐
- 浅谈Android中拍照、从相册选择图片并截图相关知识点
- android 监听软键盘的显示与隐藏
- Android 窗口管理服务WindowManagerService 简介
- Android中对ThreadPoolExcutor的简单使用
- Android 内存泄露实践分析
- Android Content Provider Tutorial--安卓内容提供者系列1--内容提供者介绍
- AndroidStudio配置JavaCV环境
- Android TextUtils类常用方法
- android:screenOrientation属性
- 分享一个酷炫广播轮播demo
- [Android开发] Xposed 插件开发之二: Xposed一些知识
- 【android】悬浮球
- Android应用使用自定义字体
- 理解Fragment的生命周期
- usb serial for android
- onIntercepteTouchEvent()和onTouchEvent()
- Android:使用MediaRecorder录制音频
- JS判断请求来自Android手机还是iPhone手机,根据不同的手机跳转到不同的链接。
- 启动页面播放视频
- Android编译详解之lunch命令