1

I have a main menu with an action bar. On create, I run a thread that hits my server for a current status. When Complete, the thread calls a handler which kicks off a constantly running thread that cycles through the items and uses another handler call to change the test in the actionbar. The problem is that when I change views, I either get android.view.WindowLeaked or View not attached to window manager

Here is some sample code

public class MainMenuActivity extends ProtectedWithActionBarActivity{
    private int STATUS_COUNTER;
    private final int RESULT_STATUS_LOADED = 2000;
    private final int RESULT_SHOW_STATUS = 2001;
    private CurrentStatusModel currentStatus;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.mainmenu);

        ActionBar footerbar = (ActionBar)findViewById(R.id.footerbar);

        footerbar.setTitle("Currently connected to " + PreferencesHelper.getCurrentEnvironment().name());


        STATUS_COUNTER = 0;

        statusLoadThread.start();
    }

    Thread statusLoadThread = new Thread()
    {
        @Override
        public void run()
        {
            //set currentStatus with data from server
        }
    };

    Thread statusDisplayThread = new Thread()
    {
        int sleep = 5000;
        boolean threadDone = false;

        public void done()
        {
            threadDone = true;
        }

        @Override
        public void run()
        {
            while(true)
            {
                //pick message to send to handler
                //increment STATUS_COUNTER or reset to 0 when out of bounds

                try 
                {
                    sleep(sleep);
                } 
                catch (InterruptedException e) 
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    };

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch(msg.what)
        {
            case RESULT_STATUS_LOADED:
                statusDisplayThread.start();
                break;
            case RESULT_SHOW_STATUS:
                ActionBar footerbar = (ActionBar)findViewById(R.id.footerbar);

                String message = ((Object[])msg.obj)[0].toString();
                OnClickListener listener = (OnClickListener)((Object[])msg.obj)[1];

                footerbar.setTitle(message);
                    footerbar.setOnTitleClickListener(listener);

                break;
            case ActivityBase.RESULT_ERROR:

                break;
        }
    }
    };
}

I'm not sure if what I'm doing is just wrong or if there is something blatantly obvious that I am missing. What needs to happen is the threads need to stop any time I change screens. Should I use Thread.interrupt(); before starting the next activity?

Matthew
  • 44,826
  • 10
  • 98
  • 87
Josh
  • 16,286
  • 25
  • 113
  • 158

3 Answers3

1

AsyncTasc allows you to implement doInBackground(), where your thread can crank away at its task. This is similar to the functionality you'd get from Thread.

The real magic happens when you override onPreExecute() and onPostExecute(), which are both executed on the UI thread. This should keep you from getting messages about your Activity not being attached.

Edit - this answer contains a small code example for AsyncTask that could get you started.

Community
  • 1
  • 1
Matthew
  • 44,826
  • 10
  • 98
  • 87
  • Can you give me a code example of what you mean by overriding the AsyncTask methods to execute on the UI thread? – Josh Mar 07 '11 at 18:10
  • I tried that code and I get `ERROR/AndroidRuntime(566): Caused by: android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.` – Josh Mar 07 '11 at 18:40
  • you can't do UI work in the doInBackground method. Could you post your doInBackground method? Also, you should be calling myTask.execute() on the UI thread – Matthew Mar 07 '11 at 18:45
0

You are trying to update UI elements after the owning Activity has been detached from the windowing system.

You will make your life a lot simpler if you use AsyncTask instead of vanilla threads (no handler needed, for one thing) and cancel() the background tasks from your Activity.onPause().

Reuben Scratton
  • 38,595
  • 9
  • 77
  • 86
  • I get how to use the AsyncTask for retrieving the data, but how do I make it work to change the View? – Josh Mar 04 '11 at 16:27
  • Whatever you return from doInBackground() arrives as a parameter to onPostExecute(), which runs on the UI thread. You can safely update your UI in there. – Reuben Scratton Mar 04 '11 at 16:44
  • How is the UI from an activity available for altering from within an AsyncTask? You can only alter the UI from the thread that created the UI. – Josh Mar 07 '11 at 18:07
  • Only AsyncTask.doInBackground() runs on the worker thread. Other member functions such as onPostExecute() run on the UI thread. – Reuben Scratton Mar 07 '11 at 19:08
0

Can't you set a flag in onPause that each of your Threads checks for? If the flag is set then the thread drops out of its loop. Thus whenever the Activity is moved to the background each of your Threads will stop. You would need to handle restarting the threads in onResume. You could alternatively use the AsyncTask approach, but this is not guaranteed to actually cancel when you call its cancel() method, it only attempts to cancel the task.

dave.c
  • 10,910
  • 5
  • 39
  • 62
  • I tried this, but there are other issues that popup even with this depending on where it's at in the process. Sometimes the http calls fail. onPause doesn't seem to be getting called when I start another activity which doesn't stop the errors in the question. – Josh Mar 07 '11 at 18:09
  • `onPause` does get called when you start a new `Activity`. Why do you think it doesn't? What tests have you run? – dave.c Mar 08 '11 at 02:12
  • I wrapped a bunch of code in checks against the flag - the while loop, the thread sleeper, and the code that set the UI, and it still errored when I changed activities. I wrapped the ui code in a blanket try catch and that has fixed it. – Josh Mar 08 '11 at 21:03
  • @Josh that could just mean that changes to the flag in one thread were not visible in another thread, as `onPause` is almost certainly being called. Did you try making the flag `volatile`? – dave.c Mar 09 '11 at 04:04