如何每隔X秒运行一个方法

163

我正在开发一个Android 2.3.3应用程序,我需要每X秒运行一次一个方法。

在iOS中,我有NSTimer,但在Android中我不知道该使用什么。

有人推荐我使用Handler;另外一个人建议我使用AlarmManager,但我不知道哪种方法更适合NSTimer

这是我想在Android中实现的代码:

timer2 = [
    NSTimer scheduledTimerWithTimeInterval:(1.0f/20.0f)
    target:self
    selector:@selector(loopTask)
    userInfo:nil
    repeats:YES
];

timer1 = [
    NSTimer scheduledTimerWithTimeInterval:(1.0f/4.0f)
    target:self
    selector:@selector(isFree)
    userInfo:nil
    repeats:YES
];

我需要一个类似于 NSTimer 的东西。

你有什么推荐吗?


1
定义“最好的”。你希望它以什么方式成为最好的? - Simon Forsberg
我不知道哪种方法更适合使用NSTimer。 - VansFannel
@VansFannel 你想要多长时间间隔? - FoamyGuy
我已经更新了问题,并提供了关于我尝试做什么的详细信息。 - VansFannel
这个问题:https://dev59.com/B2025IYBdhLWcg3wEhdb,与这个问题相似,并且有一个很好的答案。 - VansFannel
9个回答

199

你将使用的解决方案取决于每次执行函数之间需要等待多长时间。

如果你需要等待的时间超过10分钟,我建议使用AlarmManager

// Some time when you want to run
Date when = new Date(System.currentTimeMillis());

try {
    Intent someIntent = new Intent(someContext, MyReceiver.class); // intent to be launched

    // Note: this could be getActivity if you want to launch an activity
    PendingIntent pendingIntent = PendingIntent.getBroadcast(
        context,
        0, // id (optional)
        someIntent, // intent to launch
        PendingIntent.FLAG_CANCEL_CURRENT // PendingIntent flag
    );

    AlarmManager alarms = (AlarmManager) context.getSystemService(
        Context.ALARM_SERVICE
    );

    alarms.setRepeating(
        AlarmManager.RTC_WAKEUP,
        when.getTime(),
        AlarmManager.INTERVAL_FIFTEEN_MINUTES,
        pendingIntent
    );
} catch(Exception e) {
    e.printStackTrace();
}

一旦您广播了上述 Intent,您可以通过实现一个 BroadcastReceiver 来接收您的 Intent。请注意,这需要在应用清单中注册或通过 context.registerReceiver(receiver, intentFilter); 方法注册。有关 BroadcastReceiver 的更多信息,请参阅官方文档。(点击此处)

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent)
    {
        System.out.println("MyReceiver: here!") // Do your work here
    }
}

如果你等待的时间少于10分钟,我建议使用一个 Handler

final Handler handler = new Handler();
final int delay = 1000; // 1000 milliseconds == 1 second

handler.postDelayed(new Runnable() {
    public void run() {
        System.out.println("myHandler: here!"); // Do your work here
        handler.postDelayed(this, delay);
    }
}, delay);

4
为什么建议会因为时间延迟而不同? - Simon Forsberg
7
报警管理器文档中指出,不应将其用于任何小间隔重复任务以提高效率。 - Jug6ernaut
9
在Android的AlarmManager文档中,它指出Handler是更推荐和更有效的方法来处理短间隔时间: "请注意:Alarm Manager旨在用于您想让应用程序代码在特定时间运行的情况,即使您的应用程序当前未运行。对于正常的时间操作(tick、timeout等),使用Handler更容易且更高效。" - FoamyGuy
2
从可运行的代码中,您可以执行h.removeCallbacks(this);,否则您需要维护对可运行代码的引用以便能够删除它。如果希望使用第二种方法,则此处发布的方法可能不是最佳选择。 - Jug6ernaut
2
这种连续递归的形式最终不会导致堆栈溢出吗? - Andrew S
显示剩余6条评论

134

使用 Timer 每秒钟执行一次操作...

new Timer().scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        //your method
    }
}, 0, 1000);//put here time 1000 milliseconds=1 second

我已经更新了问题,并提供了关于我尝试做什么的详细信息。 - VansFannel
6
不要使用Timer(http://www.mopri.de/2010/timertask-bad-do-it-the-android-way-use-a-handler/),请使用Handler或ScheduledThreadPoolExecutor。 - AppiDevo
@AppiDevo 的链接似乎失效了,请尝试使用 https://web.archive.org/web/20200131001301/http://www.mopri.de/2010/timertask-bad-do-it-the-android-way-use-a-handler/。 - xjcl

96

您可以尝试使用此代码通过 onResume() 每 15 秒调用处理程序,并在活动不可见时通过 onPause() 停止它。

Handler handler = new Handler();
Runnable runnable;
int delay = 15*1000; //Delay for 15 seconds.  One second = 1000 milliseconds.


@Override
protected void onResume() {
   //start handler as activity become visible

    handler.postDelayed( runnable = new Runnable() {
        public void run() {
            //do something

            handler.postDelayed(runnable, delay);
        }
    }, delay);

    super.onResume();
}

// If onPause() is not included the threads will double up when you 
// reload the activity 

@Override
protected void onPause() {
    handler.removeCallbacks(runnable); //stop handler when activity not visible
    super.onPause();
}

20

如果您熟悉 RxJava,您可以使用 Observable.interval(),这非常不错。

Observable.interval(60, TimeUnits.SECONDS)
          .flatMap(new Function<Long, ObservableSource<String>>() {
                @Override
                public ObservableSource<String> apply(@NonNull Long aLong) throws Exception {
                    return getDataObservable(); //Where you pull your data
                }
            });

这样做的缺点是您必须以不同的方式架构轮询数据。然而,响应式编程方式有很多好处:

  1. 与通过回调控制数据不同,您可以创建一个数据流进行订阅。这样分离了“轮询数据”逻辑和“使用您的数据填充UI”的逻辑,使您不会混淆“数据源”代码和UI代码。
  2. 使用RxAndroid,您只需两行代码即可处理线程。

    Observable.interval(60, TimeUnits.SECONDS)
          .flatMap(...) // polling data code
          .subscribeOn(Schedulers.newThread()) // poll data on a background thread
          .observeOn(AndroidSchedulers.mainThread()) // populate UI on main thread
          .subscribe(...); // your UI code
    
    请查看RxJava。它的学习曲线较高,但它将使在Android中处理异步调用变得更加容易和清晰。

13

现在,有了 Kotlin,我们可以为此编写一个通用函数!

object RepeatHelper {
    fun repeatDelayed(delay: Long, todo: () -> Unit) {
        val handler = Handler()
        handler.postDelayed(object : Runnable {
            override fun run() {
                todo()
                handler.postDelayed(this, delay)
            }
        }, delay)
    }
}

使用时,只需执行以下操作:

val delay = 1000L
RepeatHelper.repeatDelayed(delay) {
    myRepeatedFunction()
}

6
    new CountDownTimer(120000, 1000) {

        public void onTick(long millisUntilFinished) {
            txtcounter.setText(" " + millisUntilFinished / 1000);

        }

        public void onFinish() {

            txtcounter.setText(" TimeOut  ");
            Main2Activity.ShowPayment = false;
            EventBus.getDefault().post("go-main");

        }

    }.start();

4
请勿仅发布代码作为答案。请[编辑]您的答案并添加一些解释。 - Shashanth
添加信息 - Anti Atlas Dev

2

在onCreate()方法中,我反复使用了线程。在某些情况下,计时器并不允许所有操作,而线程则是解决方案。

     Thread t = new Thread() {
        @Override
        public void run() {
            while (!isInterrupted()) {
                try {
                    Thread.sleep(10000);  //1000ms = 1 sec
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {

                            SharedPreferences mPrefs = getSharedPreferences("sam", MODE_PRIVATE);
                            Gson gson = new Gson();
                            String json = mPrefs.getString("chat_list", "");
                            GelenMesajlar model = gson.fromJson(json, GelenMesajlar.class);
                            String sam = "";

                            ChatAdapter adapter = new ChatAdapter(Chat.this, model.getData());
                            listview.setAdapter(adapter);
                           // listview.setStackFromBottom(true);
                          //  Util.showMessage(Chat.this,"Merhabalar");
                        }
                    });

                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    t.start();

如果需要的话,它可以被停止。
@Override
protected void onDestroy() {
    super.onDestroy();
    Thread.interrupted();
    //t.interrupted();
}

请描述一下我的答案不适用的情况。 - Umar Ata
你好,Umar。我需要一个圆形一直存在于应用程序中,Handler可以在Activity存活时重复执行,但是我需要在访问其他Activity时也能看到这个圆形。因此,线程是解决方案,当然它也有其困难之处。 - Samir

2

我是用这种方式实现的,而且它运行良好(代码使用Kotlin编写):

private lateinit var runnable: Runnable

private var handler = Handler(Looper.getMainLooper())

private val repeatPeriod: Long = 10000

然后从您的函数内部重新初始化可运行对象

runnable = Runnable {

    // Your code goes here

    handler.postDelayed(runnable, repeatPeriod)

}

handler.postDelayed(runnable, repeatPeriod)

请注意,如果您不将 postDelay 设置为 handler 的两倍,循环将不会无限执行!

1
在Kotlin中,您可以通过以下方式使用Runnable:

Kotlin中的代码:

private lateinit var runnable: Runnable
private var handler = Handler(Looper.getMainLooper())
private val interval: Long = 1000
private var isRunning = false

val runnable = object : Runnable {
    override fun run() {
        // Do something every second
        function()
        // Call your runnable again after interval
      handler?.postDelayed(runnable(this, interval))
    }
}
    
// Call your function once
if (!isRunning) {
    handler?.postDelayed(runnable, interval)
    isRunning = true
}

// Remove your repeatedly called function
if (isRunning) {
    handler?.removeCallbacks(runnable)
    isRunning = false
}

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