如何在安卓设备上设置定时器

179
13个回答

167

是的,Java的计时器可以用,但问题要求更好的方式(针对移动设备)。这个更好的方式在这里有解释。


为了StackOverflow的缘故:

由于Timer会创建一个新线程,因此它可能被视为繁重的操作,

如果你只需要在活动运行时得到回调函数,可以使用HandlerRunnable一起使用:

private final int interval = 1000; // 1 Second
private Handler handler = new Handler();
private Runnable runnable = new Runnable(){
    public void run() {
        Toast.makeText(MyActivity.this, "C'Mom no hands!", Toast.LENGTH_SHORT).show();
    }
};
...
handler.postAtTime(runnable, System.currentTimeMillis()+interval);
handler.postDelayed(runnable, interval);

或者一个Message

private final int EVENT1 = 1; 
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {         
        case Event1:
            Toast.makeText(MyActivity.this, "Event 1", Toast.LENGTH_SHORT).show();
            break;
        
        default:
            Toast.makeText(MyActivity.this, "Unhandled", Toast.LENGTH_SHORT).show();
            break;
        }
    }
};

...

Message msg = handler.obtainMessage(EVENT1);
handler.sendMessageAtTime(msg, System.currentTimeMillis()+interval);
handler.sendMessageDelayed(msg, interval);

顺便说一下,如果你想从另一个线程运行UI线程的代码,可以使用这种方法。

警告:Handler的定时器(或其他控制延迟的机制)会在CPU进入深度睡眠时暂停,但一旦CPU被唤醒(从暂停的位置继续),定时器将继续工作。

如果需要即使您的Activity没有运行也能获得回调,则可以使用AlarmManager


1
更新了链接,谈到谷歌在维护链接方面遇到了困难。 - Samuel
1
这篇文章是2007年的,我不是说它是错的,但如果一篇移动文章超过3年,我总是持怀疑态度。事物变化得非常快。 - StackOverflowed
1
如果您能在答案中引用链接的主要内容,那么它更像是StackOverflow。 - n611x007
1
@naxa,我的建议是让这个回答更像StackOverflow。 - Samuel
2
只是一个提示,导入android.os.Handler,而不是java.util.logging - Ali Bdeir
显示剩余2条评论

147

使用标准Java计时器方式,通过java.util.Timerjava.util.TimerTask在Android上可以正常工作,但您应该知道这种方法会创建一个新线程。

您可以考虑使用非常方便的Handler类(android.os.Handler)并通过sendMessageAtTime(android.os.Message, long)sendMessageDelayed(android.os.Message, long)向处理程序发送消息。一旦收到消息,您可以运行所需的任务。第二个选项是创建一个Runnable对象,并通过Handler的函数postAtTime(java.lang.Runnable, long)postDelayed(java.lang.Runnable, long)进行调度。


10
在安卓上,这是错误的做法。你应该使用Alarm Manager(developer.android.com/reference/android/app/AlarmManager.html)来安排未来较远的任务。 - Kurtis Nusbaum
9
这个问题没有说明任务的时间跨度。 - Christopher Perry
70
@KurtisNusbaum 这并不一定是正确的,它取决于上下文。关于AlarmManager的文档指出:“注意:Alarm Manager适用于您希望在特定时间运行应用程序代码的情况,即使您的应用程序当前未运行。对于正常的计时操作(如tick、timeout等),使用Handler更容易且更有效率。” - Christopher Perry
6
我明白了,好的。没问题。 - Kurtis Nusbaum
10
如果应用程序已获得 wakeLock 并且您确保手机不会进入睡眠状态,则使用 Handler 方法进行任务调度是可靠的。如果手机进入睡眠状态,则 sendMessageDelayed 和 sendMessageAtTime 将无效。在这种情况下,AlarmManager 是可靠的选择。 - crazyaboutliv
显示剩余6条评论

134

据我所见,java.util.Timer 是实现计时器最常用的方法。

对于重复任务:

new Timer().scheduleAtFixedRate(task, after, interval);

针对单个任务的一次运行:

new Timer().schedule(task, after);


task 指要执行的方法
after时间后初始执行
interval代表重复执行间隔时间)


11
好的,我会尽力进行翻译。以下是需要翻译的内容:I'll just add that time is in milliseconds.我只想补充一点,时间单位为毫秒。 - Uri
2
Android开发文档中的scheduleAtFixedRate函数。 - n611x007
1
task 可能是您的类的实例,该类继承自 java.util.TimerTask 并重写了 void run() - n611x007
2
这么多赞。你可以这样做。但是@Samuel描述了更好的方法。请查看他的“Which is explained Here”链接以了解原因。此外,无法从计时器线程更新UI!请参见其他线程中基于Handler的答案:(简单)https://dev59.com/dGw15IYBdhLWcg3wT55c#6702767,或者(显示各种替代方案)https://dev59.com/0W445IYBdhLWcg3w_PG3#4598737 - ToolmakerSteve
@quemeful 因为这不仅仅是关于便利性,而是关于代码质量和效率。采用这种方式比使用 Handler 使用更多的资源。 - inDepth

52

我希望这个可以帮助你,而且实现起来可能需要的努力会更少, Android CountDownTimer类

例如:

 new CountDownTimer(30000, 1000) {
      public void onTick(long millisUntilFinished) {
          mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
      }

      public void onFinish() {
          mTextField.setText("done!");
      }  
}.start();

19

可能是 计时器概念

new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

 public void onTick(long millisUntilFinished) {
  }
  public void onFinish() {
 }
}.start();

第二种方法 ::

编程定时器

添加一个名为time的int变量,并将其设置为0。 在MainActivity.java的onCreate函数中添加以下代码。

//Declare the timer
Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {

    @Override
    public void run() {
        //Called each time when 1000 milliseconds (1 second) (the period parameter)
    }

},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
1000);

进入run方法并添加以下代码。
//We must use this function in order to change the text view text
runOnUiThread(new Runnable() {

    @Override
    public void run() {
        TextView tv = (TextView) findViewById(R.id.main_timer_text);
        tv.setText(String.valueOf(time));
        time += 1;
    }

});

11

开始吧.. 我们需要两个类。我将发布一段代码,该代码每隔5秒(5000毫秒)更改移动音频配置文件...

我们的第一个类

public class ChangeProfileActivityMain extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        Timer timer = new Timer();
        TimerTask updateProfile = new CustomTimerTask(ChangeProfileActivityMain.this);
        timer.scheduleAtFixedRate(updateProfile, 0, 5000);
    }

}

我们的第二课

public class CustomTimerTask extends TimerTask {

    private AudioManager audioManager;
    private Context context;
    private Handler mHandler = new Handler();

    // Write Custom Constructor to pass Context
    public CustomTimerTask(Context con) {
        this.context = con;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        // your code starts here.
        // I have used Thread and Handler as we can not show Toast without starting new thread when we are inside a thread.
        // As TimePicker has run() thread running., So We must show Toast through Handler.post in a new Thread. Thats how it works in Android..
        new Thread(new Runnable() {
            @Override
            public void run() {
                audioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
                            audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
                            Toast.makeText(context, "Ringer Mode set to Normal", Toast.LENGTH_SHORT).show();
                        } else {
                            audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
                            Toast.makeText(context, "Ringer Mode set to Silent", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        }).start();

    }

}

11

这取决于情况。

Android文档建议,如果您的应用程序可能没有运行,则使用AlarmManager来注册将在指定时间触发的意图。

否则,您应该使用Handler。

注意:Alarm Manager适用于您希望在特定时间运行应用程序代码的情况,即使您的应用程序当前未运行。对于正常的计时操作(如滴答声、超时等),使用Handler更容易,也更高效。


5
我是一名 Android 新手,但这里是我根据上面的答案创建的计时器类。它适用于我的应用程序,但我欢迎任何建议。
使用示例:
...{
public Handler uiHandler = new Handler();

  private Runnable runMethod = new Runnable()
    {
        public void run()
        {
              // do something
        }
    };

    timer = new UITimer(handler, runMethod, timeoutSeconds*1000);       
        timer.start();
}...

public class UITimer
{
    private Handler handler;
    private Runnable runMethod;
    private int intervalMs;
    private boolean enabled = false;
    private boolean oneTime = false;

    public UITimer(Handler handler, Runnable runMethod, int intervalMs)
    {
        this.handler = handler;
        this.runMethod = runMethod;
        this.intervalMs = intervalMs;
    }

    public UITimer(Handler handler, Runnable runMethod, int intervalMs, boolean oneTime)
    {
        this(handler, runMethod, intervalMs);
        this.oneTime = oneTime;
    }

    public void start()
    {
        if (enabled)
            return;

        if (intervalMs < 1)
        {
            Log.e("timer start", "Invalid interval:" + intervalMs);
            return;
        }

        enabled = true;
        handler.postDelayed(timer_tick, intervalMs);        
    }

    public void stop()
    {
        if (!enabled)
            return;

        enabled = false;
        handler.removeCallbacks(runMethod);
        handler.removeCallbacks(timer_tick);
    }

    public boolean isEnabled()
    {
        return enabled;
    }

    private Runnable timer_tick = new Runnable()
    {
        public void run()
        {
            if (!enabled)
                return;

            handler.post(runMethod);

            if (oneTime)
            {
                enabled = false;
                return;
            }

            handler.postDelayed(timer_tick, intervalMs);
        }
    }; 
}

4

我正在使用handler和runnable创建一个计时器。我将它包装在一个抽象类中。只需派生/实现它,你就可以开始了:

 public static abstract class SimpleTimer {
    abstract void onTimer();

    private Runnable runnableCode = null;
    private Handler handler = new Handler();

    void startDelayed(final int intervalMS, int delayMS) {
        runnableCode = new Runnable() {
            @Override
            public void run() {
                handler.postDelayed(runnableCode, intervalMS);
                onTimer();
            }
        };
        handler.postDelayed(runnableCode, delayMS);
    }

    void start(final int intervalMS) {
        startDelayed(intervalMS, 0);
    }

    void stop() {
        handler.removeCallbacks(runnableCode);
    }
}

请注意,在执行代码之前调用handler.postDelayed - 这将使计时器更加接近预期的时间。但是,在计时器运行太频繁且任务(onTimer())太长的情况下,可能会出现重叠。如果您想在任务完成后开始计算intervalMS,请将onTimer()调用提前一行。

2
我认为在Android上实现此操作的方法是需要一个后台服务在运行。 在这个后台应用程序中创建计时器。 当计时器“滴答”(设置等待多长时间),启动您想要启动的活动 。 http://developer.android.com/guide/topics/fundamentals.html (<--此文章解释了Android开发的其他核心基础知识,如活动,服务,意图和其他基本原则)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接