Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-7360] Android: App - pause and resume events not fired

GitHub Issuen/a
TypeBug
PriorityHigh
StatusClosed
ResolutionInvalid
Resolution Date2012-02-09T21:52:48.000+0000
Affected Version/sRelease 1.8.0.1
Fix Version/sn/a
ComponentsAndroid
Labelsregression
ReporterPete Berry
AssigneeBill Dawson
Created2012-01-18T07:05:04.000+0000
Updated2012-02-09T21:52:48.000+0000

Description

Problem

The Android pause and resume events are not executed when the application is paused and resumed. Note that these events fire as expected using version 1.7.5

Test case

Start the application. The OnAppResume function executes as expected. *The OnAppPause function executes unexpectedly.*

Pressing the Android Home button pauses the application. However, *the OnAppPause function does not execute.*

Pressing the applications icon on the Apps screen resumes the application. However, *the OnAppResume function does not execute.*

Pressing the Android Back button ends the application and the OnAppDestroy function executes as expected. However, *the OnAppPause function does not execute.*

Titanium.UI.setBackgroundColor('#000');

var win1 = Titanium.UI.createWindow({
  title:'Test',
  backgroundColor:'#fff',
  fullscreen:true,
  exitOnClose:true});

function OnAppResume()
{
  Ti.API.info('***---> OnAppResume');
}

function OnAppPause()
{
  Ti.API.info('***---> OnAppPause');
}

function OnAppDestroy()
{
  Ti.API.info('***---> OnAppDestroy');
}

win1.open();

Ti.Android.currentActivity.addEventListener('resume', OnAppResume);
Ti.Android.currentActivity.addEventListener('pause', OnAppPause);
Ti.Android.currentActivity.addEventListener('destroy', OnAppDestroy);

Comments

  1. Bill Dawson 2012-01-24

    Still investigating, but I'm generally surprised to see it works in 1.7.5 (and I've run it in 1.7.5 to verify that.) My expectation would have been that this would not have worked in 1.7.5. Window opening (the call to win1.open() in this case) is asynchronous. It switches from a runtime thread (which is what you're on when you call open()) to the UI thread (which is required to get view elements like a window up on screen). Since it's async, I would actually expect that if the next line after open() is grabbing Ti.Android.currentActivity, then it's going to get the Activity that runs app.js, meaning the "root" activity, rather than the Activity that will be created to service the new heavyweight window. Indeed, this is what is happening in 1.8.0.1. In other words, the 1.8.0.1 behavior is what I would expect. Interesting that it differs from 1.7.5. Still looking...
  2. Shawn Lipscomb 2012-01-24

    After reading Bill's comment, I'm wondering if this (Ti.Android.currentActivity.addEventListener('pause', OnAppPause)) is the correct way to detect the "pause" of an app on Android? Note that we're talking about pausing the whole app, like when the Android "Home" button is pressed.
  3. Bill Dawson 2012-01-24

    BTW, I believe the proper approach for that particular use case is to concentrate more on being sure to get the new window's Activity (i.e., using win1.activity) rather than the "current activity" at the time app.js is being processed. For example, this re-write achieves that:
       Titanium.UI.setBackgroundColor('#000');
       
       function OnAppResume()
       {
         Ti.API.info('***---> OnAppResume');
       }
       
       function OnAppPause()
       {
         Ti.API.info('***---> OnAppPause');
       }
       
       function OnAppDestroy()
       {
         Ti.API.info('***---> OnAppDestroy');
       }
       
       var win1 = Titanium.UI.createWindow({
         title:'Test',
         backgroundColor:'#fff',
         fullscreen:true,
         exitOnClose:true});
       
       win1.addEventListener("open", function() {
       	var activity = win1.activity;
       	activity.addEventListener('resume', OnAppResume);
       	activity.addEventListener('pause', OnAppPause);
       	activity.addEventListener('destroy', OnAppDestroy);
       });
       
       
       win1.open();
       
    The only thing you'd miss out on for that is the very first resume event, since that happens just before the window open event is fired. But of course the window open event itself can stand in its place for that purpose. You still get the resume events you would expect when you, for example, pop over to the home screen and pop back again. And of course you get the pause and destroy events when expected. The reason that I find this approach "more correct" is that it distinguishes between "the current activity when all this code in app.js is running" (so to speak :)) and "the activity I'm really interested in, which is the one belonging to this heavyweight window i'm creating as the main window of my application." Nevertheless it's still interesting that the original fail case above worked in 1.7.5, and i'll be discussing with my colleagues tomorrow. Meanwhile I think my example in this comment is probably a sufficient work around for most use cases.
  4. Bill Dawson 2012-01-24

    There's no concept of an application pause in Android. Only at the activity level. There are definitely pure native (i.e., non-Titanium) Android developers out there who are desperate for such a feature, i.e., knowing when the whole darn app goes in the background. Some resort to trying to keep an integer at the application level (in their Application class) and incrementing/decrementing when activities resume/pause. Then when the number hits 0, they wait a while to see if another activity is opening, then check the number again. If the number is still 0 at that point, they assume the whole app has been backgrounded (i.e., all activities have been paused, not just the ones below the topmost.) Horrible hack! :) It's usually suggested that they re-architect their apps to not need to know this information. Needless to say, not everybody is satisfied with that suggestion. But the fact remains, it's simply not supported in Android. Of course, if you architect a Titanium app so that it only uses one Activity, you can just rely on the listeners for that activity. When wanting to open other windows above that heavyweight window, you can open these subsequent windows as "lightweight" windows. The one big thing you lose by doing that is the back button functionality, since back button by default closes the current activity, and that's going to close your heavyweight window as well, not just your light window that you've put atop it. But in Titanium we've given you a way to hook into the back button (see the android:back event on [Titanium.UI.Window](http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.UI.Window-object#events)). So if you intercept the back button while you're lightweight window is open, you can then just .close() the window and fall back to the heavy. Of course, there might be other reasons you have for wanting a multiple-activity application in Titanium, besides back button functionality. You may want one window to be fullscreen and navbarhidden, but another to be just navbarhidden, or something like that. In those cases, it's going to be very hard to know when the whole app goes in the background.
  5. Opie Cyrus 2012-01-26

    The work around Bill mentions is the correct route for this kind of behavior due to how native Android apps are structured. The current activity is associated with the url for the JS file where Ti.Android.currentActivity is used so the new window opened will not be tied to the current activity within the current url. The Ti.UI.Window.activity property should be used for accessing the activity associated with the window. A factory mechanism can be used to make handling this per activity easier. For example, call a method such as the one shown below inside the open event listener for a heavy weight window with the argument Ti.UI.Window.activity:
       var registerLifecycleListeners = function(windowActivity) {
           windowActivity.addEventListener('resume', <resume listener>);
           windowActivity.addEventListener('pause', <pause listener>);
           windowActivity.addEventListener('destroy', <destroy listener>);
       }
       
  6. Michael Pettiford 2012-01-30

    Closing issue Tested with Ti Studio build 1.0.8.201201262211 Ti Mob SDK 1.8.1.v20120127173134 OSX Lion Droid 3 OS 2.3.4, Android Emulator 2.2.2 Verified that the expected behavior of pause, resume, and destroy work correctly

JSON Source