• 【转】Android 定时器实现的几种方式和removeCallbacks失效问题详解--不错


    原文网址:http://blog.csdn.net/xiaanming/article/details/9011193

    实现定时器有很多种方式,在这里我简单的介绍几种方式

    (1)使用Handler + Runnable的方式

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Handler handler = new Handler();  
    2. Runnable runnable = new Runnable() {  
    3.       
    4.     @Override  
    5.     public void run() {  
    6.         //你要做的事  
    7.         //......  
    8.         System.out.println(Thread.currentThread().getName());  
    9.         handler.postDelayed(runnable, 1000);  
    10.     }  
    11. };  

    然后调用handler.post(runnable);就能启动定时器,这里是每隔1s打印线程名字,从打印中我们可以知道,他并没有另开线程,而是运行在UI线程当中,当你要取消定时器的时候,只需要调用handler.removeCallbacks(runnable)就可以了。

    上面中有一个问题,有时候你会发现removeCallbacks有时候会失效,不能从消息队列中移除,看下面的demo

    图:两个按钮,一个将Runnable加到消息队列中,一个将Runnable从消息队列中移除。该Runnable每1秒钟打印一次日志。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. <span style="font-family:Courier New;">package com.example.demoactivity;  
    2.   
    3. import android.app.Activity;  
    4. import android.os.Bundle;  
    5. import android.os.Handler;  
    6. import android.view.View;  
    7. import android.view.View.OnClickListener;  
    8. import android.widget.Button;  
    9.   
    10. public class TimerActivity extends Activity{  
    11.     Handler handler = new Handler();  
    12.     Runnable runnable = new Runnable() {  
    13.           
    14.         @Override  
    15.         public void run() {  
    16.             System.out.println("update...");  
    17.             handler.postDelayed(runnable, 1000);  
    18.         }  
    19.     };  
    20.   
    21.     @Override  
    22.     protected void onCreate(Bundle savedInstanceState) {  
    23.         super.onCreate(savedInstanceState);  
    24.         setContentView(R.layout.timer);  
    25.           
    26.         Button mButtonStart = (Button) findViewById(R.id.button1);  
    27.         Button mButtonStop = (Button) findViewById(R.id.button2);  
    28.           
    29.         mButtonStart.setOnClickListener(new OnClickListener() {  
    30.               
    31.             @Override  
    32.             public void onClick(View v) {  
    33.                 handler.post(runnable);  
    34.             }  
    35.         });  
    36.           
    37.         mButtonStop.setOnClickListener(new OnClickListener() {  
    38.               
    39.             @Override  
    40.             public void onClick(View v) {  
    41.                 handler.removeCallbacks(runnable);  
    42.             }  
    43.         });  
    44.     }  
    45.       
    46. }</span><span style="font-family: Georgia, 'Times new roman', Times, san-serif;">  
    47. </span>  

    结果:
    (1)start –>  输出 –> stop–> 停止输出
    (2)start –> 输出 –>  Background –> Front –> stop->继续输出

    当Activity进入后台运行后再转入前台运行,removeCallbacks无法将updateThread从message queue中移除。
    这是为什么呢?
    在Activity由前台转后台过程中,线程是一直在运行的,但是当Activity转入前台时会重新定义Runnable runnable;也就是说此时从message queue移除的runnable与原先加入message queue中的runnable并非是同一个对象。如果把runnable定义为静态的则removeCallbacks不会失效,对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,我们做如下修改就能解决上面的这个问题

     “在Activity由前台转后台过程中,线程是一直在运行的,但是当Activity转入前台时会重新定义Runnable runnable”应该不会重新定义Runnable runnable。

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. static Handler handler = new Handler();  
    2. static Runnable runnable = new Runnable() {  
    3.       
    4.     @Override  
    5.     public void run() {  
    6.         System.out.println("update...");  
    7.         handler.postDelayed(runnable, 1000);  
    8.     }  
    9. };  

    (2)使用Timer的方式

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Timer timer = new Timer();  
    2. timer.schedule(new TimerTask() {  
    3.                       
    4.     @Override  
    5.     public void run() {  
    6.         System.out.println("update....");  
    7.     }  
    8. }, 0, 1000);  

    上面的每一秒打印语句,run方法是运行在子线程,不能直接在里面更新UI操作,这里需要注意下,取消的话调用timer.cancel()就能移除任务了

    (3)采用Handle与线程的sleep(long )方法

    1.定义一个Handler类,用于处理接受到的Message

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. Handler handler = new Handler() {  
    2.         public void handleMessage(Message msg) {  
    3.             super.handleMessage(msg);  
    4.             System.out.println("update...");  
    5.         }  
    6.     }  

    2.新建一个实现Runnable接口的线程类,用一个boolean 来控制线程开始和结束  boolean isLive = true如下:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. public class MyThread implements Runnable {  
    2.         @Override  
    3.         public void run() {  
    4.             while (isLive) {  
    5.                 try {  
    6.                     Thread.sleep(1000);// 线程暂停1秒,单位毫秒  
    7.                     Message message = new Message();  
    8.                     message.what = 1;  
    9.                     handler.sendMessage(message);// 发送消息  
    10.                 } catch (InterruptedException e) {  
    11.                     e.printStackTrace();  
    12.                 }  
    13.             }  
    14.         }  
    15.     }  

    3.在需要启动线程的地方加入下面语句

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. new Thread(new MyThread()).start();  

    4.取消的话将isLive设置为false就行了

    今天主要介绍这三种方法,写的不好的地方希望大家指出,谢谢!

  • 相关阅读:
    Flume应用场景及架构原理
    遍历Map的四种方法
    zookeeper集群某个follower启动失败
    HDFS 和YARN HA 简介
    cdh集群数据恢复
    原!总结 quartz集群 定时任务 测试运行ok
    原!!junit mockito 自定义参数匹配 -- ArgumentMatcher
    log4j 日志相关
    转!!SQL左右连接中的on and和on where的区别
    转!!java泛型
  • 原文地址:https://www.cnblogs.com/wi100sh/p/4518255.html
Copyright © 2020-2023  润新知