Search code examples
androidmultithreadingthreadpoolandroid-lifecyclenexus-5

Nexus 5 going to sleep mode makes activity life cycle buggy


I have a strange behavior on the Nexus 5 when going in and out to the sleep mode. It kills and relaunchs the app in a really strange way. I show you the log:

Going into sleep mode (pressing the power button)

17.005: E/MotherActivity(28940): onPause called
17.025: E/MotherActivity(28940): onStop called
17.315: E/MotherActivity(28940): onDestroy called
17.365: E/GameTuto1Activity(28940): MainActivity Constructor called
17.365: E/MotherActivity(28940): onCreate called
17.695: E/MotherActivity(28940): onStart called
17.705: E/MotherActivity(28940): onResume called
17.705: E/MotherActivity(28940): onPause called

Coming back from sleep mode, when unlocking the phone

755: E/MotherActivity(28940): onResume called
935: E/MotherActivity(28940): onPause called
935: E/MotherActivity(28940): onStop called

And then the phone is froozen until I kill the process. But why do I go through onPause and onStop while resuming my application (coming back from sleep mode) and why onCreate,OnStart,onResume,onPause when going to sleep mode ?

If I do the same with the Nexus7, it doesn't happen, life cycle is "normal". The log below:

Going into sleep mode (pressing the power button)

43.782: E/MotherActivity(19876): onPause called 43.822: E/MotherActivity(19876): onStop called

Coming back from sleep mode, when unlocking the phone

50.142: E/MotherActivity(19876): onRestart called 50.142: E/MotherActivity(19876): onStart called 50.172: E/MotherActivity(19876): onResume called

The question is strange and miscalaneous (perhaps) but the answer can be deeply smart. So if you have an idea? It happens only on a project where I have 3 threads : 2 loops threads (I kill in onPause) and one thread managed by a PoolExecutor (killed in onPause too). If I do a simple App, it's not reproduced. Nexus5 version=4.4.2, nexus7 version=4.2.2
I know, there is no code in that question, I can't share my project.
But thanks in advance to all people who will think about that question.
Mathias .... 5 hours latter: A lead to the answer ...... In my manifest on my activity i had:

<activity  
        android:name=".MainActivity"  
        android:label="@string/title_activity_main"  
        **android:screenOrientation="landscape"**
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >  
        <intent-filter>  
        <action android:name="android.intent.action.MAIN" />  
            <category android:name="android.intent.category.LAUNCHER" />  
        </intent-filter>  
    </activity>  

and if I remove the android:screenOrientation="landscape" and it just works fine. So any idea on why this happens ? Why can I not set my orientation to landscape and forgot about the configurationChange event.
Worst, if I listen to orientation configuration changed :

android:configChanges="orientation"

The method onConfigurationChanged is not called !!! but if I use: android:configChanges="orientation|screenSize" and overwrite onConfiguration it works....

Why on earth, the screenOrientation=landscape can not handle the screenSize configuration changes. Is there another clean way to handle that mess and just say to the system, "guy i am in landscape mode don't screw me to portrait when i put my phone to sleep mode".
If some one here knows, thanks for the answer.

Else that means if you want your application to be portrait or landscape, you need to had the android:configChanges="orientation|screenSize" to your manifest and overwrite the onConfigurationChanged in your Activity (to do nothing). oO'


Solution

  • So the bug was due to orientation change... but not really. In fact it's not "orientation" changed (in my case) but "screenSize changed" that is fired as a configurationChange event when the Nexus 5 go to sleep mode and when your activity is in Portrait. It's not happening on Nexus 7 because its normal mode is landscape when going to sleep so no configurationChange is fired.

    Solution
    It means, after HoneyComb (because screenSize has begun to be used by HC), that if, in your manifest, your are using the

     android:screenOrientation="landscape"
    

    You also need to had the following this line to your manifest:

     android:configChanges="orientation|screenSize"
    

    And in your MotherActivity (the one inherits by your activities) or in your activities, you have to overwrite the configuration change method, that way:

     /*
     * (non-Javadoc)
     * @see android.app.Activity#onConfigurationChanged(android.content.res.Configuration)
     */
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        Log.e("MotherActivity", "onConfigurationChanged called ");
        // this method is there to ensure no configuration changes when going to sleep mode
        // because the device wants my app to go on portrait and then fire screenSize changes
        // sometime some montainview code sucks
        super.onConfigurationChanged(newConfig);
        // and of course do nothing !!!
    }
    

    Can have a look here for a better understanding of the onConfigurationChange and how to handle it.@http://developer.android.com/guide/topics/resources/runtime-changes.html

    Back to my problem
    In my case, i was fighting with lost threads due to that not expected configuration changes.
    Simple trick n°1
    So a simple trick to avoid such a pain and to detect it quickly, is to add Log to your Mother activity in dev mode. So when testing you always have the log of your activities' life cycle. And it will help you avoiding life cycle mess.
    Exemple:

    /*
     * (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.e("MotherActivity", "onCreate called");
        super.onCreate(savedInstanceState);
    }
    

    Threads law
    And finally, always, i mean ALWAYS, have a deep look at your threads (if you have any) and insure there is no memory leak (threads must die) and also insure your threads are retained (onRetainNonConfigurationInstance or using a fragment without GUI and setRetainInstance(true)) when configuration changes or insures to handle configurations changes.
    Simple trick n°2
    Another trick is also listen for mcc and mnc (in your manifest android:configChanges="orientation|screenSize|mcc|mcn"), because your application most of the time doesn't care about changing telecom operator. And most of the time you did not test that case.

    Have a nice day to you all!