0

I have created a simple interval running timer and it works. The only problem is when the screen orientation changes (which could happen when running) the handler to the UI thread is lost (I think) and terefore the UI doesn't get updated when the run is over.

Can you guys help me out?

Here's my code:

public class MainActivity extends Activity {

    private Thread workerThread;
    private Handler uiHandler;

    private EditText input;
    private Button start, stop;
    private ImageView image;

    @Override protected void onCreate (Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Getting references to views
        input = (EditText) findViewById(R.id.input);
        start = (Button) findViewById(R.id.start);
        stop = (Button) findViewById(R.id.stop);
        image = (ImageView) findViewById(R.id.flash);

        //Other vars
        uiHandler = new Handler();
        final String tag = "MainActivity";
        final Pattern regex = Pattern.compile("^" +
                                                      "\\d+ (" +
                                                      "  (\\.\\d+) |" +
                                                      "  (\\(" +
                                                      "   \\d+((\\.\\d+)?  (,\\d+((\\.\\d+)?))*" +
                                                      "  \\))" +
                                                      "))?" +

                                                      "(,\\d+ (" +
                                                      "  (\\.\\d+) |" +
                                                      "  (\\(" +
                                                      "   \\d+((\\.\\d+)?  (,\\d+((\\.\\d+)?))*" +
                                                      "  \\))" +
                                                      "))?)*" +
                                                      "$"
                                                     , Pattern.COMMENTS);

        //Restore previous state
        try {
            setUIRunning(savedInstanceState.getBoolean("running"));
        } catch (Exception e) {
            //New instance, do nothing
        }

        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick (View v) {

                final List<Double> schedule;

                //Process the input
                try {
                    schedule = processInput(regex, input.getText().toString());

                //Bad input, show a toast and stop
                } catch (RegexFailedException e) {
                    Toast.makeText(getApplicationContext(), e.msg, Toast.LENGTH_SHORT).show();
                    return;
                }

                Log.v(tag, "Current running list: " + schedule.toString());

                workerThread = new Thread(new Runnable() {
                    @Override
                    public void run () {

                        //SetUiRunning, but as a Runnable
                        class setUiRunningRunnable implements Runnable {

                            boolean par;

                            setUiRunningRunnable (boolean par) {
                                this.par = par;
                                uiHandler.post(this);
                            }

                            @Override public void run () { setUIRunning(par); }

                        }

                        class Flash {

                            public void run () {

                                uiHandler.post(new Runnable() {
                                    @Override
                                    public void run () {
                                        //Show red
                                        image.setVisibility(View.VISIBLE);
                                        //Beep
                                        (new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100))
                                                .startTone(ToneGenerator.TONE_CDMA_ABBR_ALERT, 500);
                                    }
                                });

                                try {
                                    Thread.sleep(500);
                                } catch (InterruptedException e) {
                                    Log.d(tag, "Sleeping (in flash) interrupted");
                                    return;
                                }

                                //Hide red
                                uiHandler.post(new Runnable() {
                                    @Override
                                    public void run () {
                                        image.setVisibility(View.GONE);
                                    }
                                });
                            }
                        }
                        final Flash flash = new Flash();

                        class Main {
                            public void run () {

                                for (double interval : schedule) {

                                    Long sleep = Math.round(interval * 60 * 1000);

                                    try {
                                        Log.d(tag, "sleeping for " + sleep + " ms");
                                        Thread.sleep(sleep);
                                    } catch (InterruptedException e) {
                                        Log.d(tag, "Sleeping interrupted");
                                        return;
                                    }

                                    flash.run();
                                    Log.d(tag, "run flash");
                                }

                            }
                        }
                        final Main main = new Main();

                        //Thread started, updating the UI to running
                        new setUiRunningRunnable(true);
                        Log.d(tag, "thread started");

                        main.run();

                        //Thread started, updating the UI to idle
                        new setUiRunningRunnable(false);
                        Log.d(tag, "thread stopped");

                    }
                });
                workerThread.start();

            }
        });

        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick (View v) {
                setUIRunning(false);
                workerThread.interrupt();
            }
        });

    }

Any general advice is appreciated as well :)

  • 2
    why not just lock the screen orientation. this happens to my with Bluetooth when the orientation changes the connection is lost. i fixed it by locking screen orientation. http://stackoverflow.com/questions/1111980/how-to-handle-screen-orientation-change-when-progress-dialog-and-background-thre?rq=1 – rush2sk8 Jan 11 '15 at 18:57
  • your code is really hard to read you make new classes inside the onCreate method and a new Listener for everything instead of implementing OnClickListener in the class and using the class itself with this as Listener. – tung Jan 11 '15 at 18:58
  • That may be a better way to do it @tung. I'll try to do that in my next project (I come from JavaScript in which it's best practise to make everything as local as possible) –  Jan 11 '15 at 19:03

1 Answers1

0

When the orientation changes the system recreates the activity, so try to add this line to

avoid from recreating in your AndroidMainfest.xml :

android:configChanges="orientation|screenSize|keyboardHidden"

it's might solve your problem.

Josef
  • 442
  • 7
  • 16