android – Update SeekBar as the audio plays

Question:

I created a seekBar to see the progress of my audio that is running in MediaPlayer , but of all the methods I used to update the seekBar , the only one that worked was this one below:

new Timer().schedule(new TimerTask() {
        @Override
        public void run() {
            mySeekBar.setProgress(mp.getCurrentPosition());
        }
    }, 0, 1000);

All the others, such as those mentioned below, did not work.

Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mySeekBar.setProgress(mp.getCurrentPosition());
        }
    }, 1000);

        new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                mySeekBar.setProgress(mp.getCurrentPosition());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

    new Thread(new TimerTask() {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                runOnUiThread(new TimerTask() {
                    @Override
                    public void run() {
                        mySeekBar.setProgress(mp.getCurrentPosition());
                    }
                });
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });

So, I would like to know why the others don't work, because, for me, they all should work. Here is the full app code:

package com.gabrielm.testseekbar;

import android.media.MediaPlayer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.SeekBar;

import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends AppCompatActivity {

    SeekBar mySeekBar;
    Button btnPlay;
    MediaPlayer mp;

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

        mySeekBar = (SeekBar) findViewById(R.id.mySeekBar);
        btnPlay = (Button) findViewById(R.id.btnPlay);
        mp = MediaPlayer.create(this, R.raw.sleep);

        mySeekBar.setMax(mp.getDuration());

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                mySeekBar.setProgress(mp.getCurrentPosition());
            }
        }, 0, 1000);

        btnPlay.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mp.start();
            }
        });



        mySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                Log.i("Audio position", Integer.toString(progress));
                mp.seekTo(progress);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {

            }
        });
    }
}

Answer:

Codigo 1:

Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mySeekBar.setProgress(mp.getCurrentPosition());
        }
    }, 1000);

It doesn't do what you intend because SeekBar is only updated once, postDelayed(Runnable r, long delayMillis) runs Runnable r once with a delay of 1000 milliseconds.

This can be solved by adding the Runnable again to the queue inside the run() method:

final Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mySeekBar.setProgress(mp.getCurrentPosition());
            handler.postDelayed(this, 1000);
        }
    }, 1000);

Code 2:

    new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            mySeekBar.setProgress(mp.getCurrentPosition());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

It doesn't work because the code is never executed: the start() method of the Thread remains to be called. Fixed this would not work either because it is only allowed to change Views in UIThread . On the other hand the run() method would only be called once.

Code 3:

new Thread(new TimerTask() {
    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            runOnUiThread(new TimerTask() {
                @Override
                public void run() {
                    mySeekBar.setProgress(mp.getCurrentPosition());
                }
            });
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

The code doesn't compile, the Thread constructor receives a Runnable and not a TimerTask .
As in code 2, the start() method still needs to be called, besides that it would only work once, however, it solves the UIThread issue, using the runOnUiThread() method.

Scroll to Top