有时间延迟重复一项任务?

Kal*_*oda 202 android android-thread

我的代码中有一个变量,说它是"状态".

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

就像是,

  • 检查状态变量值

  • 显示一些文字

  • 等待10秒钟

  • 检查状态变量值

  • 显示一些文字

  • 等待15秒

等等.时间延迟可能会有所不同,一旦显示文本就会设置.

我试过Thread.sleep(time delay)但失败了.有没有更好的方法来完成这项工作?

ina*_*ruk 432

你应该使用Handler's 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 好的程序,工作绝对精细.但是必须从onCreate方法/ UI线程调用startRepeatingTask()(我花了一些时间才意识到这一点!),也许这一点可能已在某处提到过.问候 (19认同)

rav*_*mir 33

对于任何感兴趣的人,这是我使用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);
        }
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以在类中创建UIUpdater对象并使用它,如下所示:

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

// Start updates
mUIUpdater.startUpdates();

// Stop updates
mUIUpdater.stopUpdates();
...
Run Code Online (Sandbox Code Playgroud)

如果要将其用作活动更新程序,请将启动调用放在onResume()方法内,并将停止调用放在onPause()内,以便根据活动可见性启动和停止更新.

  • 这个课有很多问题.首先,它应该在主线程中实例化以便能够更新GUI.你可以通过将主循环器传递给处理程序构造函数来解决这个问题:`new Handler(Looper.getMainLooper())`.其次,它不验证参数,因此它吞下null Runnables和负间隔.最后,它没有考虑在`uiUpdater.run()`行中花费的时间,也没有处理该方法抛出的可能异常.它也不是线程安全的,你应该使`start`和`stop`同步方法. (5认同)
  • Upvoting for import - 需要时间来确定Handler在随便用Java编程时的来源 (4认同)
  • 编辑到参数验证部分,因为我这里没有Eclipse来测试代码.感谢您的反馈!这是你的意思吗?同步startUpdates和stopUpdates并在Handler构造函数中放入一个Looper.getMainLooper()调用(我希望你可以直接从字段声明中调用它) (2认同)
  • 我明白了:`错误:调用这个必须是构造函数中的第一条语句`也许有一个简单的解决方法。 (2认同)

Nel*_*man 23

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

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


Kai*_*ang 13

定时器工作正常.在这里,我使用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);
Run Code Online (Sandbox Code Playgroud)


Xar*_*mer 5

计时器是另一种完成工作的方式,但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);
    }});
  }

 }

}
Run Code Online (Sandbox Code Playgroud)

和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"/>
Run Code Online (Sandbox Code Playgroud)

另一种使用CountDownTimer的方法

new CountDownTimer(30000, 1000) {

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

     public void onFinish() {
         mTextField.setText("done!");
     }
  }.start();
Run Code Online (Sandbox Code Playgroud)

安排倒计时,直到将来的某个时间,并在整个过程中定期通知.在文本字段中显示30秒倒计时的示例:

详情


Hit*_*ahu 5

Handler的帮助下以Android的方式进行操作。

声明一个内部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) {
                // ...
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

声明一个可运行对象,它将在Activity / Fragment类中执行您的重复任务

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

         //DO YOUR THINGS
        }
    };
Run Code Online (Sandbox Code Playgroud)

在您的“活动/片段”中初始化Handler对象(这里的FlashActivity是我的活动类)

//Task Handler
private Handler taskHandler = new NonLeakyHandler(FlashActivity.this);
Run Code Online (Sandbox Code Playgroud)

在固定时间间隔后重复执行任务

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);
    }
Run Code Online (Sandbox Code Playgroud)


Ami*_*ian 5

使用 kotlin 及其协程非常简单,首先在您的类中声明一个作业(在您的 viewModel 中更好),如下所示:

private var repeatableJob: Job? = null
Run Code Online (Sandbox Code Playgroud)

然后当你想创建并启动它时,请执行以下操作:

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

如果你想完成它:

repeatableJob?.cancel()
Run Code Online (Sandbox Code Playgroud)

PS:viewModelScope仅在视图模型中可用,您可以使用其他协程范围,例如withContext(Dispatchers.IO)

更多信息:这里