线程:Message和Runnable
原文地址http://blog.csdn.net/flowingflying/article/details/6370184
程序需要相应用户的操作,最要能在200ms(0.2s)之内,如果超过5秒没有反应,ActivityManager会没有提示就kill了activity。然而,activity可能真的需要时间来进行处理,这往往会用到后台线程-background thread。后台线程可以安全地和UI线程进行交互,其中后台线程是不能修改UI的。我不太确切知晓“不能修改UI”到何种程度,例如在下面的例子进度条状态的修改是允许的,在复杂的例子中可能会出现问题,例如两个后台线程都要处理同一个widget,可能会有不可预知的情况出现,然而就编程而言,确实不应当如此处理,后台进程应避免涉及UI的处理,UI的归UI,处理的规处理。这样才是良好的编程风格或者是一种编程原则。
通过创建一个Handler子类的对象,每个acvivity只需一个Handler对象。后台进程可通过两种方式Handler进行通信:message和Runnable对象,其结果实质都是将在Handler的队列中放入内容,message是放置信息,可以传递一些参数,Handler获取这些信息并将判度如何处理,而Runnable则是直接给出处理的方法。队列就是依次执行,Handler会处理完一个消息或者执行完某个处理在进行下一步,这样不会出现多个线程同时要求进行UI处理而引发的混乱现象。
这些队列中的内容(无论Message还是Runnable)可以要求马上执行,延迟一定时间执行或者指定某个时刻执行,如果将他们放置在队列头,则表示具有最高有限级别,立即执行。这些函数包括有:sendMessage(), sendMessageAtFrontOfQueue(), sendMessageAtTime(), sendMessageDelayed()以及用于在队列中加入Runnable的post(), postAtFrontOfQueue(), postAtTime(),postDelay()。
一般而言,推荐是Messge方式,这样程序设计得可以更为灵活,而Runnable在某些简单明确的方式中使用。我们将通过三种方法编写一个小例子来学习。这个例子是一个进度条,每隔1秒,进度条步进5,如果acvity停止时,进度条归零。

Android XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout ...... />
<ProgressBar android:id="@+id/c15_progress"
style="?android:attr/progressBarStyleHorizontal" <!-- 这表明采用传统水平进度条的方式-->
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</LinearLayout>
例子一:线程开启,采用Message传递后台线程和UI主线程之间的信息
public class Chapter15Test1 extends Activity{
private ProgressBar bar = null;
private boolean isRunning = false;
/* 我们为这个Acivity创建一个用于和后台程序通信的handler,简单地,只要一收到message,就将progressbar进度增加5。*/
/* 步骤1:创建Handler,并通过handleMessage()给出当收到消息是UI需要进行如何处理,例子简单不对msg的内容进行分析*/
Handler handler= new Handler(){
public void handleMessage(Message msg) {
bar.incrementProgressBy(5);
}
};
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chapter_15_test1);
bar=(ProgressBar)findViewById(R.id.c15_progress);
}/*on Start是UI初始化并显示时调用*/
protected void onStart() {
super.onStart();
bar.setProgress(0);
/*步骤2:建立后台线程处理,采用Thread,其中run()的内容,就是线程并行处理的内容,Thread是Runnable的implements*/
Thread background = new Thread(new Runnable(){
public void run() {
try{
for(int i = 0; i < 20 && isRunning; i ++){
Thread.sleep(1000);
/* 步骤2.1:发送Message到队列中,参数中的obtainMessage()是用于给出一个新Message,本例无参数,对应的在handler在队列中收到这条消息时,则通过handleMessage()进行处理*/
handler.sendMessage(handler.obtainMessage());
}
}catch(Throwable t){
//jest end the thread
}
}
});
isRunning = true;
/*步骤3:启动线程*/
background.start();
}/*onStop是UI停止显示时调用,例如我们按了返回键*/
protected void onStop() {
super.onStop();
isRunning = false;
}
}
例子2:采用Runnable
我们在上面的例子的基础上进行修改,如下
/*步骤1:由于不需要处理Message,也即不需要处理handleMessage()*/
Handler handler= new Handler();
/*步骤1.1:定义处理动作,采用Runnable的实例,通过implements run()来定制处理,这里是简单将进度条步进5。由于我们将在Thread中使用这个实例,所以考虑采用final的方式*/
final Runnable r = new Runnable(){
public void run(){
bar.incrementProgressBy(5);
}
};
/* ... ...在onStart()中的步骤2:线程的处理,和提供message不同,对于runnable方式,采用post */
Thread background = new Thread(new Runnable(){
public void run() {
try{
for(int i = 0; i < 20 && isRunning; i ++){
Thread.sleep(1000);
handler.post(r);
}
}catch(Throwable t){
//jest end the thread
}
}
});
background.start();
例子3:可以用延迟处理实现定时触发,让程序更为简单
在这里例子,事实我们是进行定时的处理,利用Handler队列可以设置延期处理的方式,我们并不需要创建一个后台运行的线程,也可以实现
Handler handler= new Handler();
... ... 在onStart() ... ...
//利用handler.postDelayed(r,1000),在队列中要求延迟1秒后进行r的处理,而在r的处理中, 最后在handler的队列中加 入一个要求延迟1秒的处理,如是,就可以实现每隔1秒的定期处理。
handler.postDelayed(new Runnable(){
public void run() {
if(isRunning && Chapter15Test2.step < 20){
step ++;
bar.incrementProgressBy(5);
handler.postDelayed(this, 1000);
}
}
},1000);
在这个例子中,我们基础某种判度,自动停止向队列加入处理。如果有某种情况,我们需要清除队列中的消息或者理,可以使用removMessages()或者removeCallbacks()的处理,这种对于延迟处理方式是非常有用的,可以中断定期的处理。当然,一般来讲我们希望能够得到某种判度,以使得定期处理能够优雅地结束,而不是简单地从队列中将消息或者处理删除。
例子4:不知道在UI主线程还是在后台线程
有时候,我们并不清楚代码将在UI线程还是后台线程运行,例如这些代码封装为一个JAR,提供给其他人调用,我们并不清楚其他人如何使用这些代码。为了解决这个问题Android在activity中提供了runOnUiThread(),如果在UI线程,则马上执行,如果在后台线程,则将Runnable的执行内容加入到主线程的队列中,这样无论代码在UI线程还是后台线程都能安全地执行。
我们在例子1的基础上进行试验:
1、建立一个Runnable,以便我们将在UI和后台Thread中进行试验
Runnable runAction = new Runnable(){
public void run(){
//注意,我们不能使用Toast.makeText(this,....),因为我们无法确定Runnable具体运行的context
Toast.makeText(getApplicationContext(),"Hello!",Toast.LENGTH_SHORT).show();
//Log.d("WEI","runAction .... is called");
}
};
由于Toast的显示和隐藏需要一定的时间,而间隔1秒显然不够,我们将例子1的间隔时间1000ms,改为5000ms这样会比较清晰,当然可以采用Log.d的方式来替代。
2、在UI线程中执行该操作,在后台线程中增加该操作,这个操作无论是在UI还是在后台线程都是可以正确执行的。
protected void onStart() {
... ...
Thread background = new Thread(new Runnable(){
public void run() {
try{
for(int i = 0; i < 20 && isRunning; i ++){
Thread.sleep(5000);
handler.sendMessage(handler.obtainMessage());
runOnUiThread(runAction);
}
}catch(Throwable t){
//jest end the thread
}
}
});
isRunning = true;
background.start();
runOnUiThread(runAction);
}
例子5:HandlerThread
在上面的例子中,无论是否使用了后台线程(例子1-2),Handler的处理实际就是UI主线程的处理,一般的使用方式为我们通过后台线程执行某些操作,如果需要进行UI的互动,将消息或者处理方式到Handler的的队列中,然手在UI主线程中进行处理。这是我们通用的情况。
之前我们讨论过为何UI的归UI,处理的处理,然而,可能有这样的需求,举个例子,在某些情况下,Handler收到消息触发的处理中可能会有Sleep(),这会导致main线程进入sleep状态,不是我们期待的。因此我们希望通过一个线程专门处理Hanlder的消息,这个线程也是依次从Handler的队列中获取信息,逐个进行处理,保证安全,不会出现混乱引发的异常。
针对此Android提供的HandlerThread。方式使用方法如下:
//步骤1:创新HandlerThread的一个对象,并开启这个线程,HandlerThread将通过Looper来处理Handler对来中的消息,也就是如果发现Handler中有消息,将在HandlerThread这个线程中进行处理。
HandlerThread ht = new HandlerThread("hander_thread");
//步骤2:启动handerhandler这个线程;
ht.start();//步骤3:创建handler中,带上Looper的参数,即handlerThread.getLooper()。注意,此处理必须在HandlerThread启动后才能调用,否则会报错 ,getLooper()会返回null,则程序异常出错
Handler handler = new Handler(ht.getLooper()){
....
public void handleMessage(Message msg){
... ... /*这里的处理,将不在主线程中执行,而在HandlerThread线程中执行,可以通过Thread.currentThread().getId()或者Thread.currentThread().getName()来确定*/
}
};
线程:Message和Runnable的更多相关文章
- 安卓性能优化之清除Handler的Message和Runnable
安卓性能优化之清除Handler的Message和Runnable Handler是由系统所提供的一种异步消息处理的常用方式,一般情况下不会发生内存泄露. 但既然是调优,当在A_Activity中使用 ...
- java的线程中的Runnable
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thr ...
- JAVA与多线程开发(线程基础、继承Thread类来定义自己的线程、实现Runnable接口来解决单继承局限性、控制多线程程并发)
实现线程并发有两种方式:1)继承Thread类:2)实现Runnable接口. 线程基础 1)程序.进程.线程:并行.并发. 2)线程生命周期:创建状态(new一个线程对象).就绪状态(调用该对象的s ...
- Android线程间通信机制——深入理解 Looper、Handler、Message
在Android中,经常使用Handler来实现线程间通信,必然要理解Looper , Handler , Message和MessageQueue的使用和原理,下面说一下Looper , Handl ...
- 2017-11-29 由runnable说起Android中的子线程和主线程
1.首先纠正一个观点,就是runnable运行在子线程中是错误的观念.runnable只是创建了一个执行任务的对象,但是它本身并不会创建一个新的子线程,Runable只是给你接口让你实现工作线程的工作 ...
- Android Runnable 运行在那个线程
Runnable 并不一定是新开一个线程,比如下面的调用方法就是运行在UI主线程中的: Handler mHandler=new Handler(); mHandler.post(new Runnab ...
- 使用Runnable接口创建线程-3
实现Runnable接口的类必须使用Thread类的实例才能创建线程.通过Runnable接口创建线程分为两步: 1. 将实现Runnable接口的类实例化. 2. 建立一个Thread对象,并将第一 ...
- 创建线程的两种方式:继承Thread类和实现Runnable接口
第一种方式:继承Thread类 步骤:1.定义类继承Thread 2.覆写Threa类的run方法. 自定义代码放在run方法中,让线程运行 3.调用线程的star方法, 该线程有两个作用:启动线程, ...
- JAVA之旅(十二)——Thread,run和start的特点,线程运行状态,获取线程对象和名称,多线程实例演示,使用Runnable接口
JAVA之旅(十二)--Thread,run和start的特点,线程运行状态,获取线程对象和名称,多线程实例演示,使用Runnable接口 开始挑战一些难度了,线程和I/O方面的操作了,继续坚持 一. ...
随机推荐
- Delete website with command.
1.AppCmd.exe 2.http://www.windowsnetworking.com/articles-tutorials/windows-server-2008/Configuring-I ...
- Main function
Main function A program shall contain a global function named main, which is the designated start of ...
- PHPExcel 生成excel统计表格
db.php为数据库操作类, $config为数据库配置,PHPExcel版本为PHPExcel_1.8.0, php代码: $dir = dirname(__FILE__); require $d ...
- ipa制作
打包ipa步骤: 项目名称 -> edit scheme -> 如图选择release 点击close后,选择真机 然后command+b编译程序,右击app,show in Finder ...
- cyq.data开源
终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了 前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也 ...
- VPN各种方案
http://www.maimiaovpn.com 大家都是用什么方法FQ的?我原来用的XskyWalker浏览器 但我用的电信网现在不行了.大家用的什么方法可否告诉我一下我用yes-vpn,10元一 ...
- jsp中forward和redirect的区别(转)
一.调用方式 我们知道,在servlet中调用转发.重定向的语句如下: request.getRequestDispatcher("new.jsp").forward(reques ...
- 学习笔记,发QQ邮件
首先设置自己的邮箱 设置 账号 吧这些全部选中就可以了 POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务 public static void Sender() ...
- 基于visual Studio2013解决C语言竞赛题之0407最大值最小值
题目 解决代码及点评 这道题考察循环和比较 /*********************************************************************** ...
- 颜色空间RGB与HSV(HSL)的转换
一般的3D编程只需要使用RGB颜色空间就好了,但其实美术人员更多的是使用HSV(HSL),因为可以方便的调整饱和度和亮度. 有时候美术需要程序帮助调整饱和度来达到特定风格的渲染效果,这时候就需要转换颜 ...