1

I'm trying to make a simple little program that will increment a number once a second. In this case, I'm implementing a thread that should loop once per second and add 1 to "potato" each time it loops. This works fine until it gets back to the display method potatoDisp(). For some reason this causes my app to crash. Removing potatoDisp() from run() fixes the problem, but the display is not updated as "potato" increases.

public int potato = 0;

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

public void potatoDisp() {
    TextView text = (TextView) findViewById(R.id.textView1);
    text.setText("You currently have " + potato + " potatoes");
}

public void start() {
    Thread thread = new Thread(this);
    thread.start();
}

@Override
public void run() {
    while (true) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            return;
        }
        potato++;
        potatoDisp();
    }
}

I'm doing this for an Android app, if that helps. I've tried searching for an answer but I'm pretty lost when it comes to the proper way to work threads.

tshepang
  • 12,111
  • 21
  • 91
  • 136
John O.
  • 98
  • 7

4 Answers4

2

You need a runnable / handler like this:

private Runnable potatoRun = new Runnable() {
    @Override
    public void run () {
        potatoDisp();
    }
};

then change

potatoDisp();

to:

runOnUiThread(potatoRun);

You can't update the views when you're not on the UI thread.

Jim
  • 10,172
  • 1
  • 27
  • 36
0

You are probably getting an exception for updating the UI in the background. Since, potatoDisp(); is called from a background Thread but that function updates the UI it will give you problems. You need to call it with runOnUiThread().

 @Override
public void run() {
while (true) {
    try 
    {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        return;
    }
    potato++;
    runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
           potatoDisp();
        }
    });
   }
}

Something like this should work.

codeMagic
  • 44,549
  • 13
  • 77
  • 93
0

The issue is that you are trying to update the UI (calling text.setText(...)) on a thread other than the main UI thread.

While I would suggest using a TimerTask instead of calling Thread.sleep(...), there are two main ways to edit your current code to work as expected.

-- Use a Handler

Define a Handler class that will accept messages and update your UI as needed. For example:

private final String POTATO_COUNT = "num_potatoes";

Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        int numPotatoes = msg.getData.getInt(POTATO_COUNT);
        mText.setText("You currently have " + numPotatoes + " potatoes");
    }
}

Then in your code where you want to call your handler to update your text view, whether or not you are on the main UI thread, do the following:

Bundle bundle = new Bundle();
bundle.putInt(POTATO_COUNT, potato);

Message msg = new Message();
msg.setData(bundle);
handler.sendMessage(msg);

-- Call runOnUiThread(...)

@Override
public void run() {
    while (true) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            return;
        }
        potato++;
        runOnUiThread(new Runnable() 
        {
            public void run() 
            {
                potatoDisp();
            }
        }
    }
}
Yjay
  • 2,717
  • 1
  • 18
  • 11
  • 1
    *Note*: While you are unlikely to run into an issue with a 1 second pause between updates, the runOnUiThread example here (and in other solutions) contains a race condition between the main thread updating the UI and another thread updating the potato counter. The `Handler` approach (or alternatively having `potatoDisp` accept an int value for the potato count) would solve this issue. – Yjay Jan 14 '14 at 20:06
0

I think you should be using Async Task to update the UI from a thread: http://developer.android.com/reference/android/os/AsyncTask.html

  • This should probably be a comment. While `AsyncTask` could be used here, the way the OP is doing it should be ok, they just need to incorporate `runOnUiThread()`. Also, `AsyncTask` is probably overkill for counting and updating a single `TextView`. – codeMagic Jan 14 '14 at 20:02
  • But why not use the built in ability to run a Thread that will update the UI that also handles Thread pooling for you and (hopefully) won't cause any application lag? http://stackoverflow.com/questions/9296539/android-runonuithread-vs-asynctask –  Jan 14 '14 at 20:55