《Android进阶》之第三篇 深入理解Handler

Handler相关说明:

A Handler allows you to send and process Message and Runnable objects associated with a thread‘s MessageQueue. Each Handler instance is associated with a single thread and that thread‘s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own. 

类似的还有一个类:

android.os

Class HandlerThread

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.

解释:安卓的UI线程(即OnCreate函数创建的线程)是线程非安全的。也就是说,在UI线程中,使用sleep这样的函数会导致整个线程延迟,但是我们在安卓开发中,往往会经常遇到一些延迟比较厉害的操作,(例如通过HTTP获取数据信息)如果放在主线程中,则会影响UI界面的渲染。但是如果另外新开一个线程,则由于UI线程只能在主线程中修改,而导致无法修改主线程的UI界面。这个时候Handler就出来解决这个问题。

handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程), 也就是说Handler对象初始化后,就默认与对它初始化的进程的消息队列绑定,因此可以利用Handler所包含的消息队列,制定一些操作的顺序。

Handler主要两大作用:

1. 提供post操作。post操作主要将Runnable对象放进主线程(UI)线程中的队列中操作。post还支持延迟操作。使用post后,Runnable是按照队列的形式逐个执行的。

2. handlerMessage操作。主要用于新开一个线程与主线程中的通信。新开的线程执行完毕后,可以通过sendMessage给主线程发送消息,并且传递一些参数,然后主线程就可以修改UI界面了。

Handler提供的函数:

post类方法允许你排列一个Runnable对象到主线程队列中:

post(Runnable)

postAtTime(Runnable,long)

postDelayed(Runnable long)

sendMessage类方法, 允许你安排一个带数据的Message对象到队列中:

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message,long)

sendMessageDelayed(Message,long)

应用实例:

1,传递Message。用于接受子线程发送的数据, 并用此数据配合主线程更新UI

在Android中,对于UI的操作通常需要放在主线程中进行操作。如果在子线程中有关于UI的操作,那么就需要把数据消息作为一个Message对象发送到消息队列中,然后,用Handler中的handlerMessge方法处理传过来的数据信息,并操作UI。类sendMessage(Message msg)方法实现发送消息的操作。 在初始化Handler对象时重写的handleMessage方法来接收Messgae并进行相关操作。

2,传递Runnable对象。用于通过Handler绑定的消息队列,安排不同操作的执行顺序。

Handler对象在进行初始化的时候,会默认的自动绑定消息队列。利用类post方法,可以将Runnable对象发送到消息队列中,按照队列的机制按顺序执行不同的Runnable对象中的run方法。

另外,Android的CPU分配的最小单元是线程,Handler一般是在某个线程里创建的,因而Handler和Thread就是相互绑定的,一一对应。而Runnable是一个接口,Thread是Runnable的子类。所以说,他俩都算一个进程。xml文件:

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {  

    private String TAG = "MainActivity";
      //声明两个按钮控件
     private Button startButton = null;
      private Button endButton = null;
      @Override
      public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
        //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器
        startButton = (Button)findViewById(R.id.startButton);
        startButton.setOnClickListener(new StartButtonListener());
        endButton = (Button)findViewById(R.id.endButton);
        endButton.setOnClickListener(new EndButtonListener());
        Log.i(TAG,"Activity-->" + Thread.currentThread().getId()); 

     }
     class StartButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
             //调用Handler的post方法,将要执行的线程对象添加到队列当中
            handler.post(updateThread);
         }  

     }  

     class EndButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
            handler.removeCallbacks(updateThread);
         }  

   }
    //创建一个Handler对象
    Handler handler  = new Handler();
    //将要执行的操作写在线程对象的run方法当中
    Runnable updateThread =  new Runnable(){  

         @Override
        public void run() {
            Log.i(TAG,"UpdateThread");
            Log.i(TAG,"Activity-->" + Thread.currentThread().getId());
             //在run方法内部,执行postDelayed或者是post方法
            handler.postDelayed(updateThread, 3000);
        }  

    };
 } 

点击start后,程序的运行结果就是每隔3秒钟,就会在控制台打印一行UpdateTread。这是因为实现了Runnable接口的updateThread对象进入了空的消息队列即被立即执行run方法,而在run方法的内部,又在3000ms之后将其再次发送进入消息队列中。

注意这种方法创建Handler对象并不需要重写handlerMessage方法。

从输出结果能看出来:

post方法虽然发送的是一个实现了Runnable接口的类对象,但是它并非创建了一个新线程,而是执行了该对象中的run方法。也就是说,整个run中的操作和主线程处于同一个线程。这样对于那些简单的操作,似乎并不会影响。但是对于耗时较长的操作,就会出现“假死”。为了解决这个问题,就需要使得handler绑定到一个新开启线程的消息队列上,在这个处于另外线程的上的消息队列中处理传过来的Runnable对象和消息。Runnable对象只是作为一个封装了操作的对象被传递,并未产生新线程。

下面这种写法也是可以的:

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {

     TestThread t = null;
     private String TAG = "MainActivity";
      //声明两个按钮控件
     private Button startButton = null;
      private Button endButton = null;
      @Override
      public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
        //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器
        startButton = (Button)findViewById(R.id.startButton);
        startButton.setOnClickListener(new StartButtonListener());
        endButton = (Button)findViewById(R.id.endButton);
        endButton.setOnClickListener(new EndButtonListener());
        Log.i(TAG,"onCreate-->" + Thread.currentThread().getId());
        t= new TestThread(1);

     }
     class StartButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
             //调用Handler的post方法,将要执行的线程对象添加到队列当中
            handler.post(t);
         }  

     }  

     class EndButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
            handler.removeCallbacks(t);
         }  

   }
    //创建一个Handler对象
    Handler handler  = new Handler();

    class TestThread extends Thread{
            int prime;
            TestThread(int prime) {
                this.prime = prime;
            }
        @Override
        public void run() {
                 //在run方法内部,执行postDelayed或者是post方法
                   handler.postDelayed(t, 3000);
                   Log.i(TAG,"TestThread-->" + Thread.currentThread().getId());
            }
        }
 } 

虽然创建了一个Thread,但是并没有执行Thread的start()方法。考虑到Thread和Runnable之间的关系,上面的两种代码并无实质差别,所以logcat中甚至都没出现启动新线程的日志。

然而,如果稍加修改:加上启动方法

  class StartButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
             //调用Handler的post方法,将要执行的线程对象添加到队列当中
            handler.post(t);
            t.start();
         }
     }
         

可以明显看到,虽然启动了新线程,但post仍然可以把这个线程推到主线程里面去,线程由虚拟机自动结束。

所以,在UI线程(主线程)中:

mHandler=new Handler();

mHandler.post(new Runnable(){

void run(){

//执行代码..

}

});

这个线程其实是在UI线程之内运行的,并没有新建线程。

常见的新建线程的方法是:参考J2SE文档的

1、

 class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
  }
  • The following code would then create a thread and start it running:

         PrimeThread p = new PrimeThread(143);
         p.start();
    

2、

   class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
  • The following code would then create a thread and start it running:

         PrimeRun p = new PrimeRun(143);
         new Thread(p).start();
    

尽量按照上面给出的两种方式做,不要受网上影响简单的从Threa创建,那样不能做到传递参数。

static void sleep(long millis)

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.

代码验证:

package com.example.androidexpriment;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {  

    private String TAG = "MainActivity";
      //声明两个按钮控件
    private Button startButton = null;
    private Button endButton = null;
    TestThread t = null;
    int flag = 0;
      @Override
      public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
        //根据控件的ID得到代表控件的对象,并为这两个按钮设置相应的监听器
        startButton = (Button)findViewById(R.id.startButton);
        startButton.setOnClickListener(new StartButtonListener());
        endButton = (Button)findViewById(R.id.endButton);
        endButton.setOnClickListener(new EndButtonListener());
        Log.i(TAG,"onCreate-->" + Thread.currentThread().getId()); 

        t= new TestThread(1);

     }
     class StartButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
             //调用Handler的post方法,将要执行的线程对象添加到队列当中
             t.start();
         }  

     }  

     class EndButtonListener implements OnClickListener{  

         @Override
         public void onClick(View v) {
             handler.sendEmptyMessage(33);
             flag = 5;
         }  

   }  

     class TestThread extends Thread{
         int prime;
         TestThread(int prime) {
             this.prime = prime;
         }
        @Override
        public void run() {
             //在run方法内部,执行postDelayed或者是post方法
            try {
                while(true) {
                    Log.i(TAG,"TestThread-->" + Thread.currentThread().getId());
                    //handler.sendEmptyMessageDelayed(22,3000);
                    Thread.sleep(3000);
                    handler.sendEmptyMessage(22);
                    if(flag  == 5)  //线程最佳的退出方法,就是自杀,也就是在线程的函数里面自然的return 出来
                        return;
                }
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

     }
    //创建一个Handler对象
    Handler handler  = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            super.handleMessage(msg);
            switch(msg.what) {
                case 22:
                    Log.i(TAG,"StartButton");
                    Log.i(TAG,"Handler-->" + Thread.currentThread().getId());
                    break;
                case 33:
                    Log.i(TAG,"EndButton");
                    Log.i(TAG,"Handler-->" + Thread.currentThread().getId());
                    break;
            }

        }

    };

 } 

时间: 2024-11-06 03:48:56

《Android进阶》之第三篇 深入理解Handler的相关文章

Android开源项目第三篇——优秀项目篇

本文为那些不错的Android开源项目第三篇——优秀项目篇,主要介绍那些还不错的完整Android项目. Android开源项目系列汇总已完成,包括: Android开源项目第一篇——个性化控件(View)篇 Android开源项目第二篇——工具库篇 Android开源项目第三篇——优秀项目篇 Android开源项目第四篇——开发及测试工具篇 Android开源项目第五篇——优秀个人和团体篇 记录的项目主要依据是项目有意思或项目分层规范比较好. Linux 项目地址:https://github

[深入理解Android卷一全文-第三章]深入理解init

因为<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第3章  深入理解init 本章主要内容 ·  深入分析init. 本章涉及的源代码文件名称及位置 以下是本章分析的源代码文件名称及其位置. ·  init.c system/core/init/init.c ·  parser.c system/core/init/parser.c ·  builtins.

Android基础学习第三篇—Intent的用法

写在前面的话: 1. 最近在自学Android,也是边看书边写一些Demo,由于知识点越来越多,脑子越来越记不清楚,所以打算写成读书笔记,供以后查看,也算是把自己学到所理解的东西写出来,献丑,如有不对的地方,希望大家给与指正. 2. 由于类似于读书笔记,可能格式神马的会比较随(hen)意(chou),大家看着受不了,可以使劲吐槽. *************************************我只是分割线***************************************

[深入理解Android卷二 全文-第三章]深入理解SystemServer

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版,而知识的传播不应该因为纸质媒介的问题而中断,所以我将在osc博客中全文转发这两本书的全部内容 第3章  深入理解SystemServer 本章主要内容: ·  分析SystemServer ·  分析EntropyService.DropBoxManagerService.DiskStatsService ·  分析DeviceStorageMonitorService.SamplingProfilerS

《Android进阶》之第一篇 在Java中调用C库函数

在Java代码中通过JNI调用C函数的步骤如下: 第一步:编写Java代码 1 class HelloJNI{ 2 native void printHello(); 3 native void printString(String str); 4 5 static {System.loadLibrary("hellojni");} 6 7 public static void main(String args[]) { 8 HelloJNI myJNI = new HelloJNI(

《Android进阶》之第二篇 launcher

1 public boolean addViewToCellLayout(View child, int index, int childId, LayoutParams params, 2 boolean markCells) { 3 final LayoutParams lp = params; 4 5 // Hotseat icons - remove text 6 if (child instanceof BubbleTextView) { 7 BubbleTextView bubble

Android进阶——强大的异步不可或缺的组件Handler详解(一)

引言 从一开始开通博客就计划一周至少写两篇博客,把自己的以前记在本地下的知识点和经验,全部搬到自己的博客里,无论是复杂难易程度只要是自己想写的就搬到这里来,好好总结下形成自己的技术体系,因为有些东西不用就会一时之间想不起来了.ORZ-好了进入正题,对于Java开发来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可.或者从Runnable接口继承,但对于Android来说在底层UI操作部分代码没有设计成为线程安全类型,所以需要引入一些同步的机制来实现线程间的通信使其刷

《Android进阶》之第七篇 NDK的使用

<Android进阶>之第一篇 在Java中调用C库函数 这一篇列举的方法是在NDK没有出来时候用的方式 在Android发布NDK之后,可以这样使用 一.首先下载android-ndk 下载后把压缩包解压出来,例如:D:\android-ndk-r10e,目录下的ndk-build.cmd就是用来编译的批处理命令. 为了编译方便,推荐添加环境变量 Path目录下添加D:\android-ndk-r10e; 以NDK自带的例子\samples\hello-jni为例 将例子导入安卓工程: He

Android开源项目第四篇——开发及测试工具篇

本文为那些不错的Android开源项目第四篇——开发工具篇,主要介绍Android开发工具和测试工具相关的开源项目. Android开源项目系列汇总已完成,包括: Android开源项目第一篇——个性化控件(View)篇 Android开源项目第二篇——工具库篇 Android开源项目第三篇——优秀项目篇 Android开源项目第四篇——开发及测试工具篇 Android开源项目第五篇——优秀个人和团体篇 1.Buck facebook开源的Android编译工具,效率是ant的两倍.主要优点在于