I am trying to make a loop in Android app which is triggered by a button click. After reading tips on making loops/delays on SO ( for example here), I decided to use message handler approach instead of Runnable.
In the code below, toastLoop() is executed and it prints "starting in x" seconds. However, the message does not seem to be posted with that delay. Or, the message is posted but the handler does not receive it. I am a newbie and I am probably making a silly mistake somewhere. What am I missing in the code below? Or is this code totally stupid?
package com.example.testapp;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.NavUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;
public class ExecActivity extends FragmentActivity {
static Context context = null;
String LOG_TAG = "FTR";
static boolean test_status = false;
ToastLoop toast_loop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_exec);
// Show the Up button in the action bar.
setupActionBar();
}
/**
* Set up the {@link android.app.ActionBar}, if the API is available.
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void setupActionBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getActionBar().setDisplayHomeAsUpEnabled(true);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.exec, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// This ID represents the Home or Up button. In the case of this
// activity, the Up button is shown. Use NavUtils to allow users
// to navigate up one level in the application structure. For
// more details, see the Navigation pattern on Android Design:
//
// http://developer.android.com/design/patterns/navigation.html#up-vs-back
//
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
class ToastLoop {
private final int loop_max_duration = 60; // in seconds
final int TOAST = 1;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i(LOG_TAG, "Handler(): msg: " + msg.what);
switch (msg.what) {
case TOAST:
Toast.makeText( ExecActivity.this, "Doing my thing", Toast.LENGTH_SHORT).show();
if ( test_status) { // test is still running
toastLoop();
}
break;
default:
Toast.makeText(ExecActivity.this, "Unhandled", Toast.LENGTH_SHORT).show();
break;
}
}
};
public boolean toastLoop() {
if ( test_status) { // test is still running
long curr_time_milli = System.currentTimeMillis();
long window_position_sec = (long)( ((long)(curr_time_milli/1000))/loop_max_duration); // fraction discarded
long loop_start_time_sec = (window_position_sec + 1 ) * loop_max_duration;
long actual_start_time_milli = loop_start_time_sec * 1000;
Log.i(LOG_TAG, "toastLoop(): starting in " + ((actual_start_time_milli - curr_time_milli)/1000) );
Message msg = handler.obtainMessage( TOAST);
handler.sendMessageAtTime( msg, actual_start_time_milli );
return true;
}
return false;
}
}
public boolean beginTest( View view) {
Log.i(LOG_TAG, "in beginTest()");
test_status = true;
toast_loop = new ToastLoop();
toast_loop.toastLoop();
return true;
}
public boolean endTest( View view) {
Log.i(LOG_TAG, "in endTest()");
test_status = false;
return true;
}
}