Android定时器实现的几种方式整理及removeCallbacks失效问题解决
程序员文章站
2023-12-03 14:41:40
实现定时器有很多种方式,在这里我简单的介绍几种方式 (1)使用handler + runnable的方式 复制代码 代码如下: handler handler = new...
实现定时器有很多种方式,在这里我简单的介绍几种方式
(1)使用handler + runnable的方式
handler handler = new handler();
runnable runnable = new runnable() {
@override
public void run() {
//你要做的事
//......
system.out.println(thread.currentthread().getname());
handler.postdelayed(runnable, 1000);
}
};
然后调用handler.post(runnable);就能启动定时器,这里是每隔1s打印线程名字,从打印中我们可以知道,他并没有另开线程,而是运行在ui线程当中,当你要取消定时器的时候,只需要调用handler.removecallbacks(runnable)就可以了。
上面中有一个问题,有时候你会发现removecallbacks有时候会失效,不能从消息队列中移除,看下面的demo
图:两个按钮,一个将runnable加到消息队列中,一个将runnable从消息队列中移除。该runnable每1秒钟打印一次日志。
<span style="font-family: courier new">package com.example.demoactivity;
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 timeractivity extends activity{
handler handler = new handler();
runnable runnable = new runnable() {
@override
public void run() {
system.out.println("update...");
handler.postdelayed(runnable, 1000);
}
};
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.timer);
button mbuttonstart = (button) findviewbyid(r.id.button1);
button mbuttonstop = (button) findviewbyid(r.id.button2);
mbuttonstart.setonclicklistener(new onclicklistener() {
@override
public void onclick(view v) {
handler.post(runnable);
}
});
mbuttonstop.setonclicklistener(new onclicklistener() {
@override
public void onclick(view v) {
handler.removecallbacks(runnable);
}
});
}
}</span><span style="font-family: georgia, 'times new roman', times, san-serif">
</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只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,我们做如下修改就能解决上面的这个问题
static handler handler = new handler();
static runnable runnable = new runnable() {
@override
public void run() {
system.out.println("update...");
handler.postdelayed(runnable, 1000);
}
};
(2)使用timer的方式
timer timer = new timer();
timer.schedule(new timertask() {
@override
public void run() {
system.out.println("update....");
}
}, 0, 1000);
上面的每一秒打印语句,run方法是运行在子线程,不能直接在里面更新ui操作,这里需要注意下,取消的话调用timer.cancel()就能移除任务了
(3)采用handle与线程的sleep(long )方法
1.定义一个handler类,用于处理接受到的message
handler handler = new handler() {
public void handlemessage(message msg) {
super.handlemessage(msg);
system.out.println("update...");
}
}
2.新建一个实现runnable接口的线程类,用一个boolean 来控制线程开始和结束 boolean islive = true如下:
public class mythread implements runnable {
@override
public void run() {
while (islive) {
try {
thread.sleep(1000);// 线程暂停1秒,单位毫秒
message message = new message();
message.what = 1;
handler.sendmessage(message);// 发送消息
} catch (interruptedexception e) {
e.printstacktrace();
}
}
}
}
3.在需要启动线程的地方加入下面语句
new thread(new mythread()).start();
4.取消的话将islive设置为false就行了
今天主要介绍这三种方法,写的不好的地方希望大家指出,谢谢!
(1)使用handler + runnable的方式
复制代码 代码如下:
handler handler = new handler();
runnable runnable = new runnable() {
@override
public void run() {
//你要做的事
//......
system.out.println(thread.currentthread().getname());
handler.postdelayed(runnable, 1000);
}
};
然后调用handler.post(runnable);就能启动定时器,这里是每隔1s打印线程名字,从打印中我们可以知道,他并没有另开线程,而是运行在ui线程当中,当你要取消定时器的时候,只需要调用handler.removecallbacks(runnable)就可以了。
上面中有一个问题,有时候你会发现removecallbacks有时候会失效,不能从消息队列中移除,看下面的demo
图:两个按钮,一个将runnable加到消息队列中,一个将runnable从消息队列中移除。该runnable每1秒钟打印一次日志。
复制代码 代码如下:
<span style="font-family: courier new">package com.example.demoactivity;
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 timeractivity extends activity{
handler handler = new handler();
runnable runnable = new runnable() {
@override
public void run() {
system.out.println("update...");
handler.postdelayed(runnable, 1000);
}
};
@override
protected void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.timer);
button mbuttonstart = (button) findviewbyid(r.id.button1);
button mbuttonstop = (button) findviewbyid(r.id.button2);
mbuttonstart.setonclicklistener(new onclicklistener() {
@override
public void onclick(view v) {
handler.post(runnable);
}
});
mbuttonstop.setonclicklistener(new onclicklistener() {
@override
public void onclick(view v) {
handler.removecallbacks(runnable);
}
});
}
}</span><span style="font-family: georgia, 'times new roman', times, san-serif">
</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只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,我们做如下修改就能解决上面的这个问题
复制代码 代码如下:
static handler handler = new handler();
static runnable runnable = new runnable() {
@override
public void run() {
system.out.println("update...");
handler.postdelayed(runnable, 1000);
}
};
(2)使用timer的方式
复制代码 代码如下:
timer timer = new timer();
timer.schedule(new timertask() {
@override
public void run() {
system.out.println("update....");
}
}, 0, 1000);
上面的每一秒打印语句,run方法是运行在子线程,不能直接在里面更新ui操作,这里需要注意下,取消的话调用timer.cancel()就能移除任务了
(3)采用handle与线程的sleep(long )方法
1.定义一个handler类,用于处理接受到的message
复制代码 代码如下:
handler handler = new handler() {
public void handlemessage(message msg) {
super.handlemessage(msg);
system.out.println("update...");
}
}
2.新建一个实现runnable接口的线程类,用一个boolean 来控制线程开始和结束 boolean islive = true如下:
复制代码 代码如下:
public class mythread implements runnable {
@override
public void run() {
while (islive) {
try {
thread.sleep(1000);// 线程暂停1秒,单位毫秒
message message = new message();
message.what = 1;
handler.sendmessage(message);// 发送消息
} catch (interruptedexception e) {
e.printstacktrace();
}
}
}
}
3.在需要启动线程的地方加入下面语句
复制代码 代码如下:
new thread(new mythread()).start();
4.取消的话将islive设置为false就行了
今天主要介绍这三种方法,写的不好的地方希望大家指出,谢谢!