简体   繁体   中英

I have two threads that two almost identical things but get CalledFromWrongThreadException error on only one of them. why?

Im a beginner android developer, so bear with me:

Im getting this error: "CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views"

Anyways, i have a media player with two threads, the first one updates a circular progress bar and the second one updates a text view that i want to use to show the time in the mp3 file. The first thread gives me no errors and runs perfectly fine. (I implemented this before the textview update)

The second thread however gives me the error in the title. I've looked into handlers asynctasks and runonuithread but I can't figure out how to utilize any of them since im using a while loop that's constantly updating it.

Also, why is only the second one giving me an error?

new Thread(new Runnable() {
        @Override
        public void run() {
            ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
            int currentPosition = 0;
            int total = mediaPlayer.getDuration();
            myProgress.setMax(total);
            while (mediaPlayer != null && currentPosition < total) {
                try {
                    Thread.sleep(1000);
                    currentPosition = mediaPlayer.getCurrentPosition();
                } catch (InterruptedException e) {
                    return;
                } catch (Exception e) {
                    return;
                }
                myProgress.setProgress(currentPosition);
            }
        }
    }).start();

    new Thread(new Runnable() {
        @Override
        public void run() {
            TextView currentTime = (TextView) findViewById(R.id.textView9);
            int currentPosition = 0;
            int total = mediaPlayer.getDuration();
            while (mediaPlayer != null && currentPosition < total) {
                try {
                    Thread.sleep(1000);
                    currentPosition = mediaPlayer.getCurrentPosition();
                } catch (InterruptedException e) {
                    return;
                } catch (Exception e) {
                    return;
                }
                currentTime.setText(getTimeString(currentPosition));
            }
        }
    }).start();

And here's the code for getTimeString:

private String getTimeString(long millis) {
    StringBuffer buf = new StringBuffer();

    int hours = (int) (millis / (1000*60*60));
    int minutes = (int) (( millis % (1000*60*60) ) / (1000*60));
    int seconds = (int) (( ( millis % (1000*60*60) ) % (1000*60) ) / 1000);

    buf
            .append(String.format("%02d", hours))
            .append(":")
            .append(String.format("%02d", minutes))
            .append(":")
            .append(String.format("%02d", seconds));

    return buf.toString();
}

do

 myProgress.setMax(total);

in

runOnUIThread(new Runnable () {

                    @Override
                    public void run () {

                     myProgress.setMax(total);

                    }
                });

Explanation

Views in android are only work/change/created on UI threads only. Other worker thread donot modify UI elements in Android, because Android is single threaded application.

You can also use AsyncTask which have onPreExecute() and onPostExecute() methods which run on UI thread to post updates to UI

Do you Ui work in run On Ui thread :

example:

new Thread(new Runnable() {
        @Override
        public void run() {
    int currentPosition = 0;
    int total = mediaPlayer.getDuration();

    while (mediaPlayer != null && currentPosition < total) {
                try {
                    Thread.sleep(1000);
                    currentPosition = mediaPlayer.getCurrentPosition();
                } catch (InterruptedException e) {
                    return;
                } catch (Exception e) {
                    return;
                }
runOnUIThread(new Runnable () {

                @Override
                public void run () {
ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
        myProgress.setMax(total);
                myProgress.setProgress(currentPosition);

                }
            });

            }
        }
    }).start();

As ρяσѕρєя K say , you can't update view in non-ui thread(Main Thread), so you can use a handler to finish this work

private Handler handler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
        // TODO : change currentTime and myProgress as a class number //
        TextView currentTime = (TextView) findViewById(R.id.textView9);
        ProgressBar myProgress = (ProgressBar) findViewById(R.id.circle_progress_bar);
        if (msg.what == 0) {
            currentTime.setText(getTimeString(msg.arg1));
        } else if (msg.what == 1) {
            myProgress.setProgress(msg.arg1);
        }
    }
};

Then in you two thread, replace

// myProgress.setProgress(currentPosition);
handler.obtainMessage(1, currentPosition, 0).sendToTarget();

// currentTime.setText(getTimeString(currentPosition));
// param1 -> msg.what, param2 -> msg.arg1, parm3 -> msg.arg2
handler.obtainMessage(0, currentPosition, 0).sendToTarget();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM