Search code examples
androiddelay

How to implement a delay inside a for loop in Android Studio


I need to implement a delay inside a for loop in Android Studio, inside the loop I have a code that sends out a frame sequence, these frames must be sent with a delay of 200ms each, I have done the test without using the 200ms delay and the code runs fine, but with the delay not, what could be my error?. This is my code:

Integer i,  nroTrama;
nroTrama = 10;

final Handler delay2 = new Handler();
for (i = 0; i < nroTrama; i++) {
delay2.postDelayed(new Runnable() {
    @Override
    public void run() {
        bufferSendTrama = myBuffer.substring((20 * i), (i * 20 + 20));
        send(bufferSendTrama.toString());
        Log.e("DEBUG-->", bufferSendTrama);
    }
  }, 200);
}

Solution

  • You really should use a Thread and a Handler here. Here is an example:

    public int nroTrama = 10; //Global variable
    public Handler delay2; //Global variable
    
    
    // Inside a method
    delay2 = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg)
        {
            switch(msg.what) {
                case 1: // Do something like updating the UI, etc.
                    Log.i("DEBUG-->", "String: " + (String) msg.obj);
                break;
                default: //Do something...
                break;
            }
            //Return the Message instance to the global pool.
            msg.recycle();
        }
    };
    
    Thread myThread = new Thread(new Runnable() {
        @Override
        public void run()
        {
            for(int i = 0; i < nroTrama; i++) {
                try {
                    Thread.sleep(200L); //Delay
                    bufferSendTrama = myBuffer.substring((20 * i), (i * 20 + 20));
                    //send(bufferSendTrama.toString());
                    final Message msg = Message.obtain();
                    //If it does not work, try creating a new Message instance instead.
                    //e.g. final Message msg = new Message();
                    msg.what = 1;
                    msg.obj = bufferSendTrama.toString();
                    delay2.sendMessage(msg);
                    Log.e("DEBUG-->", bufferSendTrama);
                } catch(InterruptedException e) {}
            }
        }
    });
    
    //Start the thread.
    myThread.start();
    

    [EDITED]

    Here's another method that is way better. This one avoids creating new Handler/Thread for same repeating task and avoids memory leak as well.

    //#####################
    //#    INNER CLASS    #
    //#####################
    
    private static final class Timer implements Runnable
    {
        private final WeakReference<Handler> handler;
        private final Thread thread;
        private boolean isAlive;
        private boolean state;
    
        public final Timer(final Handler handler)
        {
            this.handler = new WeakReference<Handler>(handler);
            thread = new Thread(this);
            isAlive = true;
            thread.setPriority(Thread.NORM_PRIORITY);
            thread.start();
        }
    
        @Override
        public final void run()
        {
            while(isAlive) {
                try {
                    synchronized(this) {
                        while(!state) this.wait();
                    }
                    Thread.sleep(200L); //Delay
                    final Handler hanRef = handler.get();
                    if(hanRef == null) {
                        isAlive = false;
                        handler.clear();
                        break;
                    }
                    final Message msg = Message.obtain();
                    msg.what = 0;
                    hanRef.sendMessageAtTime(msg, SystemClock.uptimeMillis());
                } catch(final InterruptedException e) {}
            }
        }
    
        public final synchronized void resume()
        {
            if(isAlive) {
                state = true;
                this.notify();
            }
        }
    
        public final void suspend()
        {
            state = false;
            thread.interrupt();
        }
    
        public final void stop()
        {
            isAlive = false; // In case interrupt() does nothing (Thread was not in sleep nor wait mode).
            thread.interrupt();
            handler.clear();
        }
    }
    
    //#####################
    //#    INNER CLASS    #
    //#####################
    
    private static final class UIHandler extends Handler
    {
        private final WeakReference<MainActivity> myActivity;
    
        public final UIHandler(final MainActivity myActivity)
        {
            super(Looper.getMainLooper());
            this.myActivity = new WeakReference<MainActivity>(myActivity);
        }
    
        @Override
        public final void handleMessage(final Message msg)
        {
            final MainActivity referent = myActivity.get();
            if(referent != null) {
                switch(msg.what) {
                    // onUpdate() and onImageLoaded() need to be implemented in parent class.
                    // onUpdate --> refresh UI for a example.
                    case 0: referent.onUpdate(); break;
                    // onImageLoaded --> Load image from assets folder for a example.
                    case 1: referent.onImageLoaded(msg.arg1, (Bitmap)msg.obj); break;
                }
            }
        }
    }
    

    Later somewhere in your class, initialise these variables. This should be done only once.

    handler = new UIHandler(this);
    timer = new Timer(handler);
    

    To start, pause or stop the thread.

    //To start the thread for the first time or after calling timer.suspend().
    timer.resume();
    //To pause/suspend the thread for later use.
    timer.suspend();
    //To stop/kill the thread and never want to call it again.
    //Make sure to call this method somehow, somewhere to kill the thread
    //Or else the thread will never actually killed.
    timer.stop();