`

Android 线程: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()来确定*/
    }
};

 

分享到:
评论

相关推荐

    Android学习笔记(三一):线程:Message和Runnable.doc

    Android学习笔记(三一):线程:Message和Runnable

    Android单线程模型中Message、Handler、Message Queue、Looper之间的关系---附实例源码

    消息的类型,在Handler类中的handleMessage方法中得到单个的消息进行处理,在单线程模型下,为了线程通信问题,Android设计了一个Message Queue(消息队列), 线程间可以通过该Message Queue并结合Handler和Looper组件...

    Android Handler Looper Message 使用示例

    在主线程即UI线程外,新建一个Looper线程,并用Messenger和Handler来处理message和posted runnable。程序中,在负线程中默认加了一个3s的线程等来,来帮助理解sent message和post runnable之间的同步机制。所以在按...

    Android 线程thread的两种实现方法(必看)

    三: Handler 机制,它是Runnable和Activity交互的桥梁,在run方法中发送Message,在Handler里,通过不同的Message执行不同的任务。 下面分别给出两种线程的实现方法,其一,扩展java.lang.Thread类,也就是把run()...

    Android中子线程和UI线程通信详解

    Android中子线程和UI线程之间通信的详细解释 1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢?下面详解一下。 2.首先在开发Android应用时必须遵守单...

    android线程消息机制之Handler详解

    android线程消息机制主要由Handler,Looper,Message和MessageQuene四个部分组成。平常在开发中,我们常用来在子线程中通知主线程来更新,其实整个安卓生命周期的驱动都是通过Handler(ActivityThread.H)来实现的。 ...

    android 多线程多任务断点下载

    //对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错 private void download(final String path, final File dir){ new Thread(new Runnable...

    Android:Handler学习实例1

    Android中Handler的学习实例,在本示例中,在主线程外开启子线程(使用匿名内部类Runnable),使用Handler中的Message传递消息(高效),操作一个ProgressBar的进度。适合初学者学习和了解Handler以及ProgressBar的...

    Android 在其他线程中更新UI线程的解决方法

    方法一:Activity.runOnUiThread(Runnable )(经验之道: 这个最好用, 凡是要刷新页面的地方,Activity.runOnUiThread( new Runnable() { public void run(){更新UI}});方法二:子线程调用Handler的sendMessage...

    Android Handler的作用与用法

    1.什么是handler? Handler是Android SDK来处理异步消息的核心类。...可以通过Handler将Message和Runnable对象发送到该Handler所关联线程的MessageQueue(消息队列)中,然后该消息队列一直在循环拿出一个Message,对其

    android 面试2

    避免ANR:Android应用程序通常运行在一个单独的线程里面,称谓主线程,所以在主线程里面少做一些耗时长的程序,而是利用子线程来操作一些繁琐的事情,用Handler来把子线程处理的消息返回给主线程 22、简要解释一下...

    深入理解Android中的Handler异步通信机制

    一、问题:在Android启动后会在新进程里创建一个主线程,也叫UI线程(非线程安全)这个线程主要负责监听屏幕点击事件与界面绘制。当Application需要进行耗时操作如网络请求等,如直接在主线程进行容易发生ANR错误。...

    应用启动页自定义跳转计时器View Demo

    import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics...

    android自定义短信倒计时view

    为了保证activity销毁的同时倒计时线程依然进行同时重新创建销毁又不会导致内存泄漏,我使用了handler的弱引用将handler和runnable设置成静态,同时通过一系列变量来销毁关闭线程保存状态,话不多说先看效果图: ...

    Android自定义短信倒计时view流程分析

    为了保证activity销毁的同时倒计时线程依然进行同时重新创建销毁又不会导致内存泄漏,我使用了handler的弱引用将handler和runnable设置成静态,同时通过一系列变量来销毁关闭线程保存状态,话不多说先看效果图: ...

    BAT 大厂Android研发岗必刷真题:Android异常与性能优化相关面试问题

    今天来讲一讲在面试中碰到的Android异常与性能优化相关问题: ... 没有使用子线程的Looper的Handler的handlerMessage,post(Runnable)是执行在主线程的 AsyncTask的回调中除了doInBackground,其它都

    swt-async-handler-1.0

    由于,不是很了解android下的Handler机制,没有深层次的编写诸如looper,MessageQueue。 将在下一个版本中加入MessageQueue机制,实现多任务后台处理相应。 具体使用请参考test包下的Handler使用事例。

    安卓开发—05 Android常用的组件与消息

    handler是用来接收和处理线程的消息队列里的message/runnable对象的工具。每个handler实例关联一个单独的线程和这个的线程的消息队列,handler实例会绑定到创建这个handler的线程。从那一刻起,handler会发送message...

    基于Android中实现定时器的3种解决方法

    在Android开发中,定时器一般有以下3种实现方法:一、采用Handler与线程的sleep(long)方法二、采用Handler的postDelayed(Runnable, long)方法三、采用Handler与timer及TimerTask结合的方法下面逐一介绍:一、采用...

    Android实现定时器的3种方法

    在Android开发中,定时器一般有以下3种实现方法: 一、采用Handler与线程的sleep(long)方法 二、采用Handler的postDelayed(Runnable, long)方法 三、采用Handler与timer及TimerTask结合的方法 下面逐一介绍: 一、...

Global site tag (gtag.js) - Google Analytics