Android : system becomes unresponsive after switching away from my app
<p>I'm writing a clock app that uses some animation logic. In onCreate(), my app Activity allocates an instance of a class that extends Thread which performs the logic processing in a loop in run(). I'm using a threaded SurfaceView (as per the LunarLander example, but with the context fix switch) to manage drawing to a Canvas.</p>

<p>The app runs fine as long as it has the focus. I suspend() the logic thread in onPause(), and resume() it in onResume() and stop() it in onDestroy() to make sure it's not consuming resources when the user switches away.</p>

<p>This app runs without issues on the emulator. I can switch away and back to the app as often as I want with no ill effects.</p>

<p>But when running under Android on physical hardware, if you switch away from my app and back a couple of times, the entire system will become unresponsive. Eventually the phone ignores all input including the sleep/power switch on the top, and must be rebooted by removing the battery. </p>

<p>As a test, I disabled creation of the logic thread but left the rendering code all the same and running a small bit of animation logic, and it fixes the problem, so I feel like it is something I'm doing with this logic processing Thread that must be Very Bad for the system. I'm new to this, so I'm probably making a noob mistake.</p>

<p>Any help or pointers to info on how I could possibly profile this problem is appreciated.</p>

<p>EDIT : Listing the Activity source. I've taken out a small amount of cruft unrelated to the thread in hopes of clarifying the listing. (sorry if I messed up the formatting, new here)</p>

<pre><code>public class EyesClockActivity extends Activity
implements SensorEventListener
{
// ---- options (shared preferences) ----
// snip, couple of booleans 

// --------------------------------------------------------
private void    LoadPreferences()
{
}   // end EyesClockActivity.LoadPreferences()

// --------------------------------------------------------
private void    SavePreferences()
{
}   // end EyesClockActivity.SavePreferences()

// ========================================================
private class EyesClockActivityThread extends Thread
{
    private long m_LastUpdateMilliSeconds;

    private boolean m_running = false;

    public void SetRunning( final boolean running ) 
    {
        // if restarting, don't want huge time leap
        if (        ( m_running == false )
                &amp;&amp;  ( running == true ) )
        {
            m_LastUpdateMilliSeconds = System.currentTimeMillis();
        }

        m_running = running; 
    }

    // ----------------------------------------------
    public EyesClockActivityThread()
    {
        m_digits = new EyeDigit[4];
        int index;
        for ( index = 0; index &lt; m_digits.length; ++index )
        {
            m_digits[ index ] = new EyeDigit();
        }

        m_digits[0].SetCurrentDigit( 0 );
        m_digits[1].SetCurrentDigit( 0 );
        m_digits[2].SetCurrentDigit( 0 );
        m_digits[3].SetCurrentDigit( 0 );

        m_TestDigit.SetCurrentDigit( m_currentDigit );

        m_LastUpdateMilliSeconds = System.currentTimeMillis();

        m_lastDigitChangeMilliseconds = System.currentTimeMillis();

        int minute = Calendar.getInstance().get(Calendar.MINUTE);
        int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY );
        SetTime( hour, minute );            
    }   // end EyesClockActivityThread constructor

    // --------------------------------------------------------------------
    // sets digits to display specified hour,minute
    private void    SetTime( final int hour, final int minute )
    {
    }   // end method EyesClockActivityThread.SetTime()

    // ----------------------------------------------
    public  void    run()
    {
        while (true)
        {
            if ( m_running )
            {
                final   float   updatesPerSecond = 30.0f;

                final   long    milliSecondsBetweenUpdates = (long)(( 1.0f / updatesPerSecond ) * 1000.0f);

                long    currentTimeMilliSeconds = System.currentTimeMillis();

                // TODO sleep here instead of checking constantly
                if ( (currentTimeMilliSeconds - m_LastUpdateMilliSeconds) &gt; milliSecondsBetweenUpdates )
                {
                    int minute = Calendar.getInstance().get(Calendar.MINUTE);

                    // don't bother setting time unless minute has changed
                    if ( (minute % 10) != m_digits[0].GetCurrentDigit() )
                    {
                        int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY );

                        SetTime( hour, minute );
                    }

                    float   secondsSinceLastUpdate = (float)((currentTimeMilliSeconds - m_LastUpdateMilliSeconds) / 1000.0);

                    for ( EyeDigit digit : m_digits )
                    {
                        digit.Update( secondsSinceLastUpdate );
                    }

                    // global update routines
                    m_LastUpdateMilliSeconds = System.currentTimeMillis();
                }
            }   // end if m_running
        }
    }   // end EyesClockActivityThread.run()

}   // end class EyesClockActivityThread

private EyesClockActivityThread     m_EyesActivityThread;

private EyesClockSurfaceView        m_EyesSurfaceView;

// ---- sensor interface ----
private SensorManager m_SensorManager;
private Sensor m_Accelerometer;


// -------------------------------------------------------------------------------
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) 
{
    EyeDigit.InitDigitDescriptors();

    m_EyesActivityThread = new EyesClockActivityThread();

    // No Title bar
    requestWindowFeature(Window.FEATURE_NO_TITLE);

    m_EyesActivityThread.SetRunning(true);
    m_EyesActivityThread.start();

    LoadPreferences();

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    m_EyesSurfaceView = (EyesClockSurfaceView)findViewById(R.id.eyesclocksurfaceview);

    Configuration config = getResources().getConfiguration();

    m_EyesSurfaceView.SetOrientation( config.orientation );

    m_EyesSurfaceView.SetDigits( m_EyesActivityThread.GetDigits() );

    m_SensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
    m_Accelerometer = m_SensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}   // end method EyesClockActivity.onCreate()

// ----------------------------------------------
protected void onPause() 
{
    super.onPause();

    // pause activity thread
    m_EyesActivityThread.SetRunning(false);
    m_EyesActivityThread.suspend();

    // quit sensor listening
    m_SensorManager.unregisterListener(this);
}   // end method EyesClockActivity.onPause()

// ----------------------------------------------------
protected void onResume() 
{
    super.onResume();

    // resume activity thread
    m_EyesActivityThread.SetRunning(true);
    m_EyesActivityThread.resume();

    m_SensorManager.registerListener(this, m_Accelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}   // end method EyesClockActivity.onResume()

// ------------------------------------------------------
protected   void    onDestroy()
{
    super.onDestroy();

    m_EyesActivityThread.SetRunning(false);
    m_EyesActivityThread.stop();

    m_SensorManager.unregisterListener(this);

    // quit sensor listening
    m_SensorManager.unregisterListener(this);
}   // end method EyesClockActivity.onDestroy()

// -------------------------------------------------------
public void onAccuracyChanged(Sensor sensor, int accuracy) 
{
}

// ----------------------------------------------------
public void onSensorChanged(SensorEvent event) 
{
    if (        ( event.values[0] &gt; 15.0f )
            ||  ( event.values[1] &gt; 15.0f )
            ||  ( event.values[2] &gt; 15.0f ) )
    {
        m_EyesActivityThread.StartGoogleyEvent();
    }
}   // end method EyesClockActivity.onSensorChanged()

// ---- options menu ----
@Override
public boolean onCreateOptionsMenu(Menu menu) 
{
    // add cursor blink toggle item
    menu.add( Menu.NONE, R.id.toggle_blink, Menu.NONE, R.string.string_blink_cursor);

    if ( GetTwelveHourDisplay())
    {
        menu.add( Menu.NONE, R.id.toggle_twelve_hour_display, Menu.NONE, R.string.string_24_hour_display );
    }
    else
    {
        menu.add( Menu.NONE, R.id.toggle_twelve_hour_display, Menu.NONE, R.string.string_12_hour_display );
    }

    return true;
}    

@Override
public boolean onPrepareOptionsMenu(Menu menu) 
{
    super.onPrepareOptionsMenu(menu);

    MenuItem displayItem = menu.getItem(1);

    if ( displayItem != null )
    {
        if ( GetTwelveHourDisplay())
        {
            displayItem.setTitle(R.string.string_24_hour_display);
        }
        else
        {
            displayItem.setTitle(R.string.string_12_hour_display);
        }
    }

    return true;
}    

@Override
public boolean onOptionsItemSelected(MenuItem item) 
{
    // Handle item selection
    switch (item.getItemId()) 
    {
    case R.id.toggle_twelve_hour_display:
        ToggleTwelveHourDisplay();

        int minute = Calendar.getInstance().get(Calendar.MINUTE);
        int hour = Calendar.getInstance().get(Calendar.HOUR_OF_DAY );
        m_EyesActivityThread.SetTime( hour, minute );

        SavePreferences();

        return true;
    case R.id.toggle_blink :
        ToggleBlinkCursor();

        SavePreferences();

        return true;
    default:
        return super.onOptionsItemSelected(item);
    }
} 

}
</code></pre>
