我的代码中有一个变量说它是“状态”。
我想根据此变量值在应用程序中显示一些文本。这必须在特定的时间延迟内完成。
就像是,
检查状态变量值
显示一些文字
等待10秒
检查状态变量值
显示一些文字
等待15秒
等等。时间延迟可能会有所不同,并且在显示文本后进行设置。
我尝试过Thread.sleep(time delay)
,但失败了。有什么更好的方法可以做到这一点?
我的代码中有一个变量说它是“状态”。
我想根据此变量值在应用程序中显示一些文本。这必须在特定的时间延迟内完成。
就像是,
检查状态变量值
显示一些文字
等待10秒
检查状态变量值
显示一些文字
等待15秒
等等。时间延迟可能会有所不同,并且在显示文本后进行设置。
我尝试过Thread.sleep(time delay)
,但失败了。有什么更好的方法可以做到这一点?
Answers:
为此,您应该使用Handler
的postDelayed
功能。它将在主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();
...
如果要将其用作活动更新程序,请将start调用放在onResume()方法内部,将stop调用放在onPause()内部,以便根据活动可见性开始和停止更新。
UPDATE_INTERVAL = interval;
应该在 this(uiUpdater);
in 之前UIUpdater(Runnable uiUpdater, int interval)
(因为使用的值, UPDATE_INTERVAL
并且应该作为参数传递的值interval;
)。还要尽可能避免代码中超过80个字符的宽度(几乎总是;)
new Handler(Looper.getMainLooper())
。其次,它不验证参数,因此它吞噬了空的Runnable和负间隔。最后,它没有考虑花费在uiUpdater.run()
生产线上的时间,也没有处理该方法抛出的可能异常。而且它也不是线程安全的,您应该使start
和stop
同步的方法。
error: call to this must be first statement in constructor
也许有一个简单的解决方法。
我认为新的热点是使用ScheduledThreadPoolExecutor。像这样:
private final ScheduledThreadPoolExecutor executor_ =
new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
update();
}
}, 0L, kPeriod, kTimeUnit);
Executors.newSingleThreadScheduledExecutor()
在这里可以是另一个选择。
计时器工作正常。在这里,我使用Timer在1.5秒后搜索文本并更新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);
有3种方法可以做到:
有点多余,因为您不需要线程池
//----------------------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();
}
现代Android风格
//----------------------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);
}
具有活动/上下文的非泄漏处理程序
声明一个内部Handler类,该类不会泄漏 Activity / Fragment类中的内存
/**
* 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) {
// ...
}
}
}
声明一个可运行对象,它将在Activity / Fragment类中执行您的重复任务
private Runnable repeatativeTaskRunnable = new Runnable() {
public void run() {
new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
//DO YOUR THINGS
}
};
在您的“活动/片段”中初始化Handler对象(此处FlashActivity是我的活动类)
//Task Handler
private Handler taskHandler = new NonLeakyHandler(FlashActivity.this);
在固定时间间隔后重复执行任务
taskHandler.postDelayed(repeatativeTaskRunnable,DELAY_MILLIS);
停止重复任务
taskHandler .removeCallbacks(repeatativeTaskRunnable);
//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);
}
计时器是完成工作的另一种方法,但是请注意,runOnUiThread
如果要使用UI,请务必添加计时器。
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秒倒计时的示例:
尝试下面的例子,它的工作原理!
在onCreate()方法中使用[Handler],该方法利用postDelayed()方法,该方法导致将Runnable添加到消息队列中,在给定示例中经过指定的时间量为0后运行。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);
}
};
您可以使用处理程序来发布可运行的代码。此处很好地概述了此技术:https : //guides.codepath.com/android/Repeating-Periodic-Tasks
基于以上有关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子句。
使用kotlin及其协程非常简单,首先在您的类中声明一个工作(在viewModel中更好),如下所示:
private var repeatableJob: Job? = null
然后,当您要创建并启动它时,请执行以下操作:
repeatableJob = viewModelScope.launch {
while (isActive) {
delay(5_000)
loadAlbums(iImageAPI, titleHeader, true)
}
}
repeatableJob?.start()
如果要完成它:
repeatableJob?.cancel()
PS:viewModelScope
仅在视图模型中可用,您可以使用其他协程范围,例如withContext(Dispatchers.IO)
更多信息:这里
对于使用Kotlin的人,inazaruk的答案将不起作用,IDE将要求初始化该变量,因此,我们将在单独的方法中使用它,而不是在postDelayed
内部Runnable
使用。
Runnable
像这样初始化您的:
private var myRunnable = Runnable {
//Do some work
//Magic happens here ↓
runDelayedHandler(1000) }
runDelayedHandler
像这样初始化您的方法:
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)
}
}
如您所见,这种方法将使您能够控制任务keepRunning
的生命周期,在应用程序的生命周期内跟踪和更改任务将为您完成工作。