私のコードには「ステータス」と言う変数があります。
この変数値に応じて、アプリケーションにテキストを表示したい。これは、特定の時間遅延で実行する必要があります。
のような、
ステータス変数値を確認する
テキストを表示する
10秒待ちます
ステータス変数値を確認する
テキストを表示する
15秒待つ
等々。時間遅延は異なる場合があり、テキストが表示されると設定されます。
Thread.sleep(time delay)
を試しましたが失敗しました。これを行うためのより良い方法はありますか?
この目的には、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のコードを使用して作成したクラスを以下に示します(UIを定期的に更新するために使用するため、UIUpdaterと呼びましたが、好きなように呼び出すことができます)。
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()メソッド内にstart呼び出しを、onPause()メソッド内にstop呼び出しを配置し、アクティビティの可視性に従って更新が開始および停止するようにします。
新しいホットは、 ScheduledThreadPoolExecutor を使用することだと思います。そのようです:
private final ScheduledThreadPoolExecutor executor_ =
new ScheduledThreadPoolExecutor(1);
this.executor_.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
update();
}
}, 0L, kPeriod, kTimeUnit);
タイマーは正常に動作します。ここでは、タイマーを使用して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);
タイマーは作業を行う別の方法ですが、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秒のカウントダウンを表示する例:
ハンドラーを使用して、実行可能なコードをポストできます。この手法は、ここで非常にうまく概説されています: https://guides.codepath.com/Android/Repeating-Periodic-Tasks
私の場合、これらの条件のいずれかが真である場合、前のプロセスが完了した場合、または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が実行されます。そうでない場合は、変数の時間をインクリメントし、1秒後にハンドラーを実行します。 process1が読み取られるか、timesが5になるまでループを維持します。timesが5の場合、5秒が経過し、1秒ごとにprocess1.isRead()のif句が実行されることを意味します。
Handler 。の助けを借りて、Androidのやり方でそれを行う
Activity-/Fragmentクラスで Memoryをリークしない となる内部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) {
// ...
}
}
}
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が私のアクティビティクラスです)
//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);
}
それが動作する次の例を試してみてください!!!
OnCreate()メソッドで[Handler]を使用します。これは、指定された例で0である指定された時間が経過した後に実行されるRunnableをメッセージキューに追加するpostDelayed()メソッドを使用します。 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);
}
};
</ code>
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);
}
}
Kotlinを使用している場合、 inazaruk's answer は機能しません。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
を追跡し、アプリケーションのライフタイム中に変更することで、ジョブが実行されます。