android Handler的使用

Handler的使用(一)

Handler基本概念:
Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分逐个的在消息队列中将消息取出,然后对消息进行出来,就是发送消息和接收消息不是同步的处理。
这种机制通常用来处理相对耗时比较长的操作。

使用一个例子简单的来介绍一下Handler。
示例1:一个应用程序中有2个按钮(start、end),当点击start按钮时,执行一个线程,这个线程在控制台输出一串字符串,并且每隔3秒再执行一次线程,直到点击end按钮为止,线程停止。
下图为这个应用程序的界面:

下图为执行程序时控制台的输出:

开发步骤:
1、 新建一个Android应用程序
2、 在布局文件中添加2个Button控件标签,并为其设置属性和值
3、 在Activity中,声明控件变量并根据id获得控件对象
4、 在Activity中,创建一个Handler对象
5、 在Activity中,创建一个Runnable对象
a) 以匿名内部类的方式
b) 将要执行的操作写在Runnable对象中的run()方法中
i. 打印出一句话
ii. 调用Runnable对象的postDelayed()方法
6、 在Activity中,编写start按钮需要的监听器,并绑定
a) 在这个监听器的Onclick()方法中,调用Handler的post()方法,将要执行的线程对象放到队列当中。
7、 在Activity中,编写end按钮需要的监听器,并帮定
a) 在这个监听器的Onclick()方法中,调用Handler的removeCallbacks ()方法,删除队列当中未执行的线程对象。
b)
下面是Activity的代码:

package android.handler; 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
public class HandlerTest extends Activity { 
    /** Called when the activity is first created. */
    private Button startButton; 
    private Button endButton; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        //根据id获得控件对象 
        startButton = (Button)findViewById(R.id.startButton); 
        endButton = (Button)findViewById(R.id.endButton); 
        //为控件设置监听器 
        startButton.setOnClickListener(new StartButtonListener()); 
        endButton.setOnClickListener(new EndButtonListener()); 
    } 
    class StartButtonListener implements OnClickListener{ 
        public void onClick(View v) { 
            //调用Handler的post()方法,将要执行的线程对象放到队列当中 
            handler.post(updateThread); 
        } 
    } 
    class EndButtonListener implements OnClickListener{ 
        public void onClick(View v) { 
            //调用Handler的removeCallbacks()方法,删除队列当中未执行的线程对象 
            handler.removeCallbacks(updateThread); 
        } 
    } 
    //创建Handler对象 
    Handler handler = new Handler(); 
    //新建一个线程对象 
    Runnable updateThread = new Runnable(){ 
        //将要执行的操作写在线程对象的run方法当中 
        public void run(){ 
            System.out.println("updateThread"); 
            //调用Handler的postDelayed()方法 
            //这个方法的作用是:将要执行的线程对象放入到队列当中,待时间结束后,运行制定的线程对象 
            //第一个参数是Runnable类型:将要执行的线程对象 
            //第二个参数是long类型:延迟的时间,以毫秒为单位 
            handler.postDelayed(updateThread, 3000); 
        } 
    }; 
}

上面是一个最简单的例子,下面再看另外一个例子。
示例2:一个应用程序中有一个进度条和一个按钮,当点击按钮后,每隔一秒钟进度条前进一部分。
下图为应用程序的运行效果图:

开发步骤:
1、 新建一个Android应用程序
2、 在布局文件中添加一个progressBar和一个Button,并为其设置属性和值
3、 在Activity中,声明控件变量并根据id获得控件对象
4、 创建线程对象
a) 通过匿名内部类的方式
b) 在编写完了5、6步之后再来继续编写这个线程对象里的操作
i. 声明一个变量用来设置进度条的进度
ii. 重写线程类的run方法(),里面编写要执行的操作
1. 打印一个字符串
2. 进度条的值增加
3. 得到一个消息对象
4. 设置消息对象arg1的值
5. 让线程休眠一秒钟
6. 将消息对象放入到消息队列中
7. 判断,如果进度条的值等于100,则将线程对象从队列中移除。
5、 创建Handler对象
a) 与示例1不同的地方是,这里是通过匿名内部类的方式来声明的,而示例1是直接new出来的对象
b) 重写Handler对象的handlerMessage(Message msg)方法
i. 这个方法传入了一个Message对象,即消息对象,首先设置进度条的进度(这个值是Messag对象里面的一个成员变量arg1)。
ii. 将要执行的线程对象放入到队列当中
6、 编写Button需要的监听器,并绑定
a) 设置进度条为显示状态
b) 将要执行的线程对象放入到队列当中
下面是Activity的代码:

package android.handler; 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
import android.os.Message; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.Button; 
import android.widget.ProgressBar; 
public class ProgressBarHandlerTest extends Activity { 
    /** Called when the activity is first created. */
    private ProgressBar progressBar; 
    private Button startButton; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.main); 
        progressBar = (ProgressBar)findViewById(R.id.progressbar); 
        startButton = (Button)findViewById(R.id.startButton); 
        startButton.setOnClickListener(new ProgressBarOnClickListener()); 
    } 
    class ProgressBarOnClickListener implements OnClickListener{ 
        public void onClick(View v) { 
            //设置进度条为可见状态 
            progressBar.setVisibility(View.VISIBLE); 
            updateBarHandler.post(updateThread); 
        } 
    } 
    //使用匿名内部类来复写Handler当中的handlerMessage()方法 
    Handler updateBarHandler = new Handler(){ 
        @Override 
        public void handleMessage(Message msg) { 
            progressBar.setProgress(msg.arg1); 
            updateBarHandler.post(updateThread);    //将要执行的线程放入到队列当中 
        } 
    }; 
    //线程类,该类使用匿名内部类的方式进行声明 
    Runnable updateThread = new Runnable(){ 
        int i = 0; 
        public void run() { 
            // TODO Auto-generated method stub 
            System.out.println("Begin Thread"); 
            i+=10; 
            //得到一个消息对象,Message类是android系统提供的 
            Message msg = updateBarHandler.obtainMessage(); 
            //将Message对象的arg1参数的值设置为i 
            msg.arg1 = i;   //用arg1、arg2这两个成员变量传递消息,优点是系统性能消耗较少 
            try{ 
                Thread.sleep(1000); //让当前线程休眠1000毫秒 
            }catch(InterruptedException ex){ 
                ex.printStackTrace(); 
            } 
            //将Message对象加入到消息队列当中 
            updateBarHandler.sendMessage(msg); 
            //如果i的值等于100 
            if (i == 100){ 
                //将线程对象从队列中移除 
                updateBarHandler.removeCallbacks(updateThread);  
            } 
        } 
    }; 
}

项目源码已经上传到附件了,有需要的可下载。

不知道大家有没有弄明白哈,我自己都写得晕晕乎乎的了,没办法啊语文写作能力不咋地,汗~

这里其实有一点我没有弄明白,就是当进度条的值等于100的时候,就将线程对象从队列中移除,从而停止线程的运行;这个在示例1中是没有问题的,当点击end按钮,线程停止;但是在示例2例子中,当进度条的值等于100了,可是却没有停止,还是在继续的运行,每隔一秒就执行一个线程,不知道这是为什么,研究了好一会儿都没弄明白,希望各位知道的可以跟我说说哈,咱们有机会多探讨探讨、多交流交流!

Handler(一).rar

Handler的使用(二)
一、 Handler与线程的关系
Handler在默认情况下,实际上它和调用它的Activity是处于同一个线程的。
例如在Handler的使用(一)的示例1中,虽然声明了线程对象,但是在实际调用当中它并没有调用线程的start()方法,而是直接调用当前线程的run()方法。
通过一个例子来证实一下
示例1:一个Android应用程序,在Activity中创建Handler和线程对象,并且在Activity的onCreate()方法中输出当前线程的id和名字,然后在线程对象的run方法中也打印输出下当前线程的id和名字。如果说,Activity输出的结果与线程对象输出的结果是一样的,那么就表示它们使用的是同一个线程。
下面是Activity代码:

package android.handler; 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Handler; 
public class HandlerTwo extends Activity { 
    /** Called when the activity is first created. */
    Handler handler = new Handler(); 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        //在设置布局文件之前先调用post方法, 
        //表示在执行完线程之后才会显示布局文件中的内容,而线程中又设置了休眠10秒钟, 
        //所以最终效果为,先显示应用程序主界面,等待10秒钟之后才显示布局文件中的内容 
        handler.post(r);      
        setContentView(R.layout.main); 
        System.out.println("activity id--->"+Thread.currentThread().getId()); 
        System.out.println("activity name--->"+Thread.currentThread().getName()); 
    } 
    Runnable r = new Runnable(){ 
        public void run() { 
            //输出当前线程的id和name 
            //如果这里输出的线程id、name与上面onCreate()方法中输出的线程id、name相同的话, 
            //那么则表示,他们使用的是同一个线程 
            System.out.println("runnable_id--->"+Thread.currentThread().getId()); 
            System.out.println("runnable_name--->"+Thread.currentThread().getName()); 
            try{ 
                Thread.sleep(10000);    //让线程休眠10秒 
            }catch(InterruptedException e){ 
                e.printStackTrace(); 
            } 
        } 
    }; 
}

下图是执行的结果:

根据结果可以看出,两个输出的id和name都相同,它们使用的是同一个线程。

现在将Activity中的代码修改一下,新建一个线程Thread,然后调用线程的start()方法,再观察一下控制台的输出结果。

这里只要将上面的代码稍微修改一下就可以了
1、先将handler.post(r)注释掉
2、再添加下面两句代码就OK了

//handler.post(r);      
Thread t = new Thread(r); 
t.start();

输出结果:

从这个输出结果中可以看出,这次线程对象的id、name与activity里的线程id、name完全不一样了,由此可见,它们现在使用的不是同一个线程。

这个例子中还掩饰了一个效果,就是平时我们是将Handler的post()方法放在setContentView(R.layout.main)这个方法之后调用,将设置完布局之后再执行其他的操作,而在这个例子中,是将Handler的post()方法放在setContent()方法之前调用,而post里传递的线程对象的run()方法呢,又执行了休眠线程10秒钟,所以运行实现的效果会是,当程序运行后,首先Activity上没有任何内容,过来10秒之后,才会显示Activity里的内容。

二、 Bundle和如何在新线程中处理消息
首先介绍一下Bundle:
Bundle它是一个以string为键,可以由其他数据类型作为值的一个mapping,相当于把数据当成一个包。在初学的阶段可以将它当成特殊的一个HashMap对象,不过HashMap的键和值都是Object类型的,而Bundle的键却是String类型。
通过一个例子来使用一下Bundle和如何在新线程中处理消息
示例2:一个Android应用程序,先打印Activity当前使用的线程id,然后再创建一个新线程,使用Bundl存储值,最后打印出线程的id和Bundle中存储的值。
看一下输出结果:

唉,这个好复杂好多了,懒得一步步写了,大家直接看源码吧,汗~

项目已经打包上传到附件了,有需要的可以下载。
注:示例1和示例2是在同一个应用程序中,当运行程序的时候默认的是启动示例1的Activity,如果要运行示例2的Activity,那么就将应用程序中的AndroidManifest.xml文件里的Activity标签的name修改成HandlerTest2就OK了。

Handler_Two.rar

转自 http://byandby.iteye.com/blog/832467

http://byandby.iteye.com/blog/832915本来是两篇文章我将他们合在了一起。