重复一个有时间延迟的任务?

我有一个变量在我的代码说它是“状态”。

我想在应用程序中根据这个变量值显示一些文本。这必须在特定的时间延迟下完成。

这就像,

  • 检查状态变量值

  • 显示一些文本

  • 等待10秒

  • 检查状态变量值

  • 显示一些文本

  • 等待15秒

等等。时间延迟可能会有所不同,在文本显示时设置。

我已经尝试过Thread.sleep(time delay),它失败了。还有更好的办法吗?

215168 次浏览

为此,你应该使用HandlerpostDelayed函数。它将在主UI线程上以指定的延迟运行您的代码,因此您将能够更新UI控件。

private int mInterval = 5000; // 5 seconds by default, can be changed later
private Handler mHandler;


@Override
protected void onCreate(Bundle bundle) {


// your code here


mHandler = new Handler();
startRepeatingTask();
}


@Override
public void onDestroy() {
super.onDestroy();
stopRepeatingTask();
}


Runnable mStatusChecker = new Runnable() {
@Override
public void run() {
try {
updateStatus(); //this function can change value of mInterval.
} finally {
// 100% guarantee that this always happens, even if
// your update method throws an exception
mHandler.postDelayed(mStatusChecker, mInterval);
}
}
};


void startRepeatingTask() {
mStatusChecker.run();
}


void stopRepeatingTask() {
mHandler.removeCallbacks(mStatusChecker);
}

对任何感兴趣的人来说,这里是我用inazaruk的代码创建的一个类,它创建了所需的一切(我称之为UIUpdater,因为我用它定期更新UI,但你可以叫它任何你喜欢的名字):

import android.os.Handler;
/**
* A class used to perform periodical updates,
* specified inside a runnable object. An update interval
* may be specified (otherwise, the class will perform the
* update every 2 seconds).
*
* @author Carlos Simões
*/
public class UIUpdater {
// Create a Handler that uses the Main Looper to run in
private Handler mHandler = new Handler(Looper.getMainLooper());


private Runnable mStatusChecker;
private int UPDATE_INTERVAL = 2000;


/**
* Creates an UIUpdater object, that can be used to
* perform UIUpdates on a specified time interval.
*
* @param uiUpdater A runnable containing the update routine.
*/
public UIUpdater(final Runnable uiUpdater) {
mStatusChecker = new Runnable() {
@Override
public void run() {
// Run the passed runnable
uiUpdater.run();
// Re-run it after the update interval
mHandler.postDelayed(this, UPDATE_INTERVAL);
}
};
}


/**
* The same as the default constructor, but specifying the
* intended update interval.
*
* @param uiUpdater A runnable containing the update routine.
* @param interval  The interval over which the routine
*                  should run (milliseconds).
*/
public UIUpdater(Runnable uiUpdater, int interval){
UPDATE_INTERVAL = interval;
this(uiUpdater);
}


/**
* Starts the periodical update routine (mStatusChecker
* adds the callback to the handler).
*/
public synchronized void startUpdates(){
mStatusChecker.run();
}


/**
* Stops the periodical update routine from running,
* by removing the callback.
*/
public synchronized void stopUpdates(){
mHandler.removeCallbacks(mStatusChecker);
}
}

然后你可以在你的类中创建一个UIUpdater对象,并像这样使用它:

...
mUIUpdater = new UIUpdater(new Runnable() {
@Override
public void run() {
// do stuff ...
}
});


// Start updates
mUIUpdater.startUpdates();


// Stop updates
mUIUpdater.stopUpdates();
...

如果你想使用它作为一个活动更新器,把开始调用放在onResume()方法中,停止调用放在onPause()中,这样更新就会根据活动可见性开始和停止。

我认为新的热点是使用ScheduledThreadPoolExecutor。像这样:

private final ScheduledThreadPoolExecutor executor_ =
new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
update();
}
}, 0L, kPeriod, kTimeUnit);

试试下面的例子,它可以工作!!

在onCreate()方法中使用[Handler],该方法使用postDelayed()方法,该方法会导致Runnable被添加到消息队列中,在给定的示例中,在指定的时间流逝后运行。1

public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//------------------
//------------------
android.os.Handler customHandler = new android.os.Handler();
customHandler.postDelayed(updateTimerThread, 0);
}


private Runnable updateTimerThread = new Runnable()
{
public void run()
{
//write here whaterver you want to repeat
customHandler.postDelayed(this, 1000);
}
};

> < / pre > < /代码

Timer是另一种工作方式,但如果你使用UI,一定要添加runOnUiThread

    import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Timer;
import java.util.TimerTask;


import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.app.Activity;


public class MainActivity extends Activity {


CheckBox optSingleShot;
Button btnStart, btnCancel;
TextView textCounter;


Timer timer;
MyTimerTask myTimerTask;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
optSingleShot = (CheckBox)findViewById(R.id.singleshot);
btnStart = (Button)findViewById(R.id.start);
btnCancel = (Button)findViewById(R.id.cancel);
textCounter = (TextView)findViewById(R.id.counter);


btnStart.setOnClickListener(new OnClickListener(){


@Override
public void onClick(View arg0) {


if(timer != null){
timer.cancel();
}


//re-schedule timer here
//otherwise, IllegalStateException of
//"TimerTask is scheduled already"
//will be thrown
timer = new Timer();
myTimerTask = new MyTimerTask();


if(optSingleShot.isChecked()){
//singleshot delay 1000 ms
timer.schedule(myTimerTask, 1000);
}else{
//delay 1000ms, repeat in 5000ms
timer.schedule(myTimerTask, 1000, 5000);
}
}});


btnCancel.setOnClickListener(new OnClickListener(){


@Override
public void onClick(View v) {
if (timer!=null){
timer.cancel();
timer = null;
}
}
});


}


class MyTimerTask extends TimerTask {


@Override
public void run() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat simpleDateFormat =
new SimpleDateFormat("dd:MMMM:yyyy HH:mm:ss a");
final String strDate = simpleDateFormat.format(calendar.getTime());


runOnUiThread(new Runnable(){


@Override
public void run() {
textCounter.setText(strDate);
}});
}


}


}

XML是…

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:orientation="vertical"
tools:context=".MainActivity" >


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://android-er.blogspot.com/"
android:textStyle="bold" />
<CheckBox
android:id="@+id/singleshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Single Shot"/>

另一种使用CountDownTimer的方法

new CountDownTimer(30000, 1000) {


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


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

计划一个倒计时,直到将来的某个时间,并在此过程中定期通知。在文本框中显示30秒倒计时的例子:

详细信息 .

您可以使用Handler来发布可运行的代码。https://guides.codepath.com/android/Repeating-Periodic-Tasks很好地概括了这个技巧

定时器工作正常。在这里,我使用定时器搜索1.5s后的文本和更新UI。希望这能有所帮助。

private Timer _timer = new Timer();


_timer.schedule(new TimerTask() {
@Override
public void run() {
// use runOnUiThread(Runnable action)
runOnUiThread(new Runnable() {
@Override
public void run() {
search();
}
});
}
}, timeInterval);

基于上面关于ScheduledThreadPoolExecutor的帖子,我想出了一个适合我的需求的实用程序(想要每3秒触发一个方法):

class MyActivity {
private ScheduledThreadPoolExecutor mDialogDaemon;


private void initDebugButtons() {
Button btnSpawnDialogs = (Button)findViewById(R.id.btn_spawn_dialogs);
btnSpawnDialogs.setVisibility(View.VISIBLE);
btnSpawnDialogs.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
spawnDialogs();
}
});
}


private void spawnDialogs() {
if (mDialogDaemon != null) {
mDialogDaemon.shutdown();
mDialogDaemon = null;
}
mDialogDaemon = new ScheduledThreadPoolExecutor(1);
// This process will execute immediately, then execute every 3 seconds.
mDialogDaemon.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
// Do something worthwhile
}
});
}
}, 0L, 3000L, TimeUnit.MILLISECONDS);
}
}

在我的例子中,如果以下条件之一为真,我就必须执行一个流程:如果前一个流程已经完成,或者如果5秒已经过去了。所以,我做了以下工作,并且工作得很好:

private Runnable mStatusChecker;
private Handler mHandler;


class {
method() {
mStatusChecker = new Runnable() {
int times = 0;
@Override
public void run() {
if (times < 5) {
if (process1.isRead()) {
executeProcess2();
} else {
times++;
mHandler.postDelayed(mStatusChecker, 1000);
}
} else {
executeProcess2();
}
}
};


mHandler = new Handler();
startRepeatingTask();
}


void startRepeatingTask() {
mStatusChecker.run();
}


void stopRepeatingTask() {
mHandler.removeCallbacks(mStatusChecker);
}




}

如果process1被读取,它将执行process2。如果不是,则增加变量的时间,并使Handler在一秒钟后执行。它一直保持循环,直到process1被读取或times为5。当times为5时,这意味着5秒过去了,每一秒都执行process1.isRead()的if子句。

有3种方法:

使用ScheduledThreadPoolExecutor

有点多余,因为你不需要一个线程池

   //----------------------SCHEDULER-------------------------
private final ScheduledThreadPoolExecutor executor_ =
new ScheduledThreadPoolExecutor(1);
ScheduledFuture<?> schedulerFuture;
public void  startScheduler() {
schedulerFuture=  executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
//DO YOUR THINGS
pageIndexSwitcher.setVisibility(View.GONE);
}
}, 0L, 5*MILLI_SEC,  TimeUnit.MILLISECONDS);
}




public void  stopScheduler() {
pageIndexSwitcher.setVisibility(View.VISIBLE);
schedulerFuture.cancel(false);
startScheduler();
}

使用定时器任务

旧Android风格

    //----------------------TIMER  TASK-------------------------


private Timer carousalTimer;
private void startTimer() {
carousalTimer = new Timer(); // At this line a new Thread will be created
carousalTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
//DO YOUR THINGS
pageIndexSwitcher.setVisibility(INVISIBLE);
}
}, 0, 5 * MILLI_SEC); // delay
}


void stopTimer() {
carousalTimer.cancel();
}

使用Handler和Runnable

现代安卓风格

    //----------------------HANDLER-------------------------


private Handler taskHandler = new android.os.Handler();


private Runnable repeatativeTaskRunnable = new Runnable() {
public void run() {
//DO YOUR THINGS
}
};


void startHandler() {
taskHandler.postDelayed(repeatativeTaskRunnable, 5 * MILLI_SEC);
}


void stopHandler() {
taskHandler.removeCallbacks(repeatativeTaskRunnable);
}

无泄漏的处理程序与活动/上下文

在你的Activity/Fragment类中声明一个内部Handler类不泄漏内存

/**
* Instances of static inner classes do not hold an implicit
* reference to their outer class.
*/
private static class NonLeakyHandler extends Handler {
private final WeakReference<FlashActivity> mActivity;


public NonLeakyHandler(FlashActivity activity) {
mActivity = new WeakReference<FlashActivity>(activity);
}


@Override
public void handleMessage(Message msg) {
FlashActivity activity = mActivity.get();
if (activity != null) {
// ...
}
}
}

声明一个runnable来执行Activity/Fragment类中的重复任务

   private Runnable repeatativeTaskRunnable = new Runnable() {
public void run() {
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {


//DO YOUR THINGS
}
};

在你的Activity/Fragment中初始化Handler对象(这里FlashActivity是我的Activity类)

//Task Handler
private Handler taskHandler = new NonLeakyHandler(FlashActivity.this);

在固定的时间间隔后重复一项任务

taskHandler。postDelayed(repeatativeTaskRunnable, DELAY_MILLIS);

停止重复任务

removecallbacks (repeatativeTaskRunnable);

更新:在Kotlin:

    //update interval for widget
override val UPDATE_INTERVAL = 1000L


//Handler to repeat update
private val updateWidgetHandler = Handler()


//runnable to update widget
private var updateWidgetRunnable: Runnable = Runnable {
run {
//Update UI
updateWidget()
// Re-run it after the update interval
updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
}


}


// SATART updating in foreground
override fun onResume() {
super.onResume()
updateWidgetHandler.postDelayed(updateWidgetRunnable, UPDATE_INTERVAL)
}




// REMOVE callback if app in background
override fun onPause() {
super.onPause()
updateWidgetHandler.removeCallbacks(updateWidgetRunnable);
}

对于使用Kotlin的人来说,inazaruk的回答将不起作用,IDE将要求对变量进行初始化,因此我们将在一个单独的方法中使用它,而不是在Runnable中使用postDelayed

  • 像这样初始化你的Runnable:

    private var myRunnable = Runnable {
    //Do some work
    //Magic happens here ↓
    runDelayedHandler(1000)   }
    
  • Initialize your runDelayedHandler method like this :

     private fun runDelayedHandler(timeToWait : Long) {
    if (!keepRunning) {
    //Stop your handler
    handler.removeCallbacksAndMessages(null)
    //Do something here, this acts like onHandlerStop
    }
    else {
    //Keep it running
    handler.postDelayed(myRunnable, timeToWait)
    }
    }
    
  • As you can see, this approach will make you able to control the lifetime of the task, keeping track of keepRunning and changing it during the lifetime of the application will do the job for you.

使用kotlin和它的协程非常简单,首先在你的类中声明一个作业(最好是在你的viewModel中),像这样:

private var repeatableJob: Job? = null

然后当你想要创建并启动它时,这样做:

repeatableJob = viewModelScope.launch {
while (isActive) {
delay(5_000)
loadAlbums(iImageAPI, titleHeader, true)
}
}
repeatableJob?.start()

如果你想结束它:

repeatableJob?.cancel()

注:viewModelScope只在视图模型中可用,你可以使用其他协程作用域,如withContext(Dispatchers.IO)

更多信息:在这里