[TIMOB-15253] Android: singleTask launchMode with HeavyWeight window hangs after 1st launch
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | High |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2017-03-29T18:46:12.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 6.0.3 |
Components | Android |
Labels | android, heavyweight, launchmode, merge-6.0.3, singletask |
Reporter | Fokke Zandbergen |
Assignee | Gary Mathews |
Created | 2013-08-26T08:57:50.000+0000 |
Updated | 2019-01-11T03:37:20.000+0000 |
Description
When launching your app with it's main activity set to Create a new project:
Build (only) to generate manifest:
Open the generated
Add
Build the app to the device:
Open the updated
Lookup the app on the device and launch it again. You'll see a black screen with a titlebar (
Now replace the app's
android:launchMode="singleTask"
and it's main window a HeavyWeight one, it will hang on the second and every following launch.
This bug blocks me from allowing users to open my app by using an URL scheme on a webpage or in a email message, send to them when they lost their password, received an invite to play a game etc.
Why use launchMode?
By default, when launching your app via another app, e.g. using an URL scheme in the browser, your apps main activity will be stacked onto the current app's task. If your app was already running in the background, you will have 2 instances of the app running. Android has a [launchMode](http://developer.android.com/guide/topics/manifest/activity-element.html#lmode) attribute that allows you to instruct the OS to always open the activity as the root of a new task, usingsingleTask
as value.
This works fine, but not when the app's main window is HeavyWeight, for example a Ti.UI.TabGroup
or a Ti.UI.Window
with modal:false
.
To reproduce
Create a new project: titanium create -p android -n test
Build (only) to generate manifest: titanium build -b -p android
Open the generated build/android/AndroidManifest.xml
and copy the first <activity>
tree to the tiapp.xml
under ti:app/android/manifest/application
. You will need to create the empty <manifest>
and <application>
elements yourself.
Add android:launchMode="singleTask"
to the <activity>
element and save tiapp.xml
.
Build the app to the device: titanium build -p android -T device
Open the updated AndroidManifest.xml
to confirm the only effective change is the newly added attribute.
Seeing the app properly launched on the device, move it to the background by clicking the device's home button.
Lookup the app on the device and launch it again. You'll see a black screen with a titlebar (test
) only.
Now replace the app's app.js
with a lightweight window instead of the default heavyweight tabgroup:
Ti.UI.createWindow({backgroundColor:'white'}).open();
For a use case see my blog at: http://fokkezb.nl/2013/08/26/url-schemes-for-ios-and-android-1/
Update: Even if the main application window is light, opening any heavy window will cause the app to hang on a black screen the next time you launch it from the app list. Please make this an urgent issue for 3.2 guys!
Update: I think this is somehow related to the
ti.android.bug2373.finishfalseroot
(TIMOB-9285). I was working on a workaround that was binding the URL scheme to launch the app (which is the main use-case I needsingleTask
for) to a new activity/JS file like this:In the
incoming.js
I tried to launch the actual app by doing:This actually worked, but showed the
An application restart is required
message. So I added theti.android.bug2373.finishfalseroot
property to mytiapp.xml
. And guess what? I got the exact same behavior described in the test case of this ticket: a black screen of death. Hope this is of some help?[~ingo], any change we can have this fixed in 3.1.3?
Hi, Could you please provide test case. Thanks,
The description already includes the steps to reproduce, under *Why use lauchMode* I explain why this should be fixed and at http://fokkezb.nl/2013/08/26/url-schemes-for-ios-and-android-1/ I have a use case. What is it what you need more?
Ugly workaround is to launch the app using the [intent-based URI](https://developers.google.com/chrome/mobile/docs/intents) with the [FLAG_ACTIVITY_NEW_TASK](http://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK) flag set:
Same issue for us. Really annoying :(
Unfortunately, we can't support the singleTask mode with the current Titanium architecture. When you background the app, and go back to it, the screen is black since the tab group activity gets destroyed. The singletask mode seems to destroy all other activity in the task, besides the activity that is marked as single task. This is a problem since we don't re-run the app.js code in this case (and we shouldn't since this is done as a part of the v8 runtime initialization). Fokke actually pointed out a workaround which I have tested and seems to work well. Inside the tiapp.xml of the 'singletask' app, you need to place the finishfalseroot flag.
In the other app that you want to launch the 'singletask' app, You need to do the following:
The key here is to use the FLAG_ACTIVITY_NEW_TASK flag when launching the app with the finishfalseroot set to true. We are actively working to make this a non-issue in the Ti.Next architecture.
I hate seeing this postponed to TiNext, but I understand why it has too. To call the intent from an URL, you would have to attach a scheme to the activity and then use the
#Intent
anchor to add the same flag like this:For more details check my blog: http://fokkezb.nl/2013/09/20/url-schemes-for-ios-and-android-2/
Is there a workaround for this that works in 4.0.x?
Would very much like to see this fixed!
I think it would be good if we'd test and fix the underlying results developers want to achieve: * Be able to launch the app from an email/browser using a URL or other data scheme without creating a second instance off the app in the current activity stack if the app is already open in another.
Behavior is expected for "singleTask". So the Titanium flow is something like this:
When you assign the Root Activity as "singleTask" launch mode, your first launch would do: Root activity -> first window/tabgroup (it works as expected) However, when you hit the home button, all activities after the root will be destroyed, so when the app is relaunched, only the root activity is shown. This result in the black window, cause the splash screen is now gone. This used to work with Lightweight windows is because LW is actually a view on top of the Root Activity.
I did some research and I don't think "singleTask" is a viable solution to this problem. This is because all activities but the one assigned with "singleTask" are destroyed when the app is resume. Even if we can make it work, the app would have to restart every time you background/foreground, which is not what we want. We would need to investigate further for other solutions.
It's a long a time ago but I think we started using singleTask because: {quote} By default, when launching your app via another app, e.g. using an URL scheme in the browser, your apps main activity will be stacked onto the current app's task. If your app was already running in the background, you will have 2 instances of the app running. {quote} So in the end the ticket is not about
singleTask
but about the question: _How to to open a Titanium via a custom URL *AND* prevent multiple instances *AND* without requiring launch flags with the URL_I am currently struggling with the same (branch.io request that I have my launcher as single task), and I added an OnResume function onto the main (launcher) activity, in which I re-read my index.... so far so good, that seems to work. Anyone sees any problem with this approach? This comes very early in my alloy.js :
[~annakozy@direcciona.me] thanks for sharing that workaround! [~cng] if this works then it seems like when the app resumes in singleTask launchMode the existing activities (windows) are killed and [~annakozy@direcciona.me]'s code handles this by re-opening the index controller. Do you see any downsides? If not, then this might be documented as the official way to implement singleTask launchMode and we can support URL schemes on Android, jay!
Yes, there is a problem, not so much with the workaround as with single task itself. It indeed destroys all the activities above the one marked as single task, so maintaining the application state (other open windows) becomes a problem, including background audio. Update: I did more testing. No, it does not actually destroy anything when put into background, even audio keeps playing, it's FOREGROUNDING that creates problems. If you foreground it from the list of activities (on my phone it appears by pressing and holding home button), nothing happens. However if you restart the activity from the icon again, then the child activities are destroyed/restarted
I created TIMOB-20490 to talk about this from the user POV.
I have read more about it, and to me it looks like a generic Android problem, not even really Appc-specific.... applies to any app where some activities are single task and others are not. I am using my workaround which is kinda working, but I am not really sure where to move from there.
I tried the workaround mentioned by [~annakozy@direcciona.me] and it works... but it makes my app unstable and crashes sporadically when resumed. Without the workaround, resuming the app from a URL scheme freezes on the Splash screen. I followed [~fokkezb] article on implementing URL schemes. Any further suggestions?
I believe this bug is preventing full integration with Branch.io.
What exactly is needed for Branch.io?
In the first place, if you dont use singleTask, and you already have your application open, you will have two or more instances of your application open when you launch it from Branch link. As a user, you can see all those instances if you click that button to show the list of running apps on Android It is confusing for the user and I also dont know if it might be confusing for Branch statistics in some way
Just for the record. I still keep re-reading index controller. I did have to do some usual magic to make sure index.js knows it is being reopened (I send it a parameter in arguments like for any other controller), so that it does not add some listeners for the second time. It's been rather stable except for some not frequent cases when it STILL gets stuck at launcher. But this looks like the app fails to register onResume function with the launcher activity (Ti.Android.currentActivity must be something else at the beginning of alloy.js in this case - it often happens when I launch the app from Ti.Studio, but it works ok in normal workflow). Separating launcher in a separate variable could help with this (so I can always add onResume for launcher) But other than that and the typical bugs of duplicated Ti.App.addEventListener, I am fine
Its been since mid-january that we've been rather integrated with Branch and using single task Its not perfect (in testing environment, I can recreate some less-than-perfect behaviors), but this does not seem to cause many user complaints
Point is, I do not experience "sporadic crashes" documented earlier by someone when I am re-reading index controller, or any instability that cant be solved by usual measures of sorting things into "must only do on cold start" and "can do always" (the difference between those cases is easy to spot by passing a parameter into index, when you reopen it)
As a matter of fact, if we teach appcelerator developers to treat index.js like any other window.... most of the problem with singleTask will go away. The problem is perhaps due to the programming style when people assume that index.js is only being read once and put a lot of unsafe stuff there (having listeners added and not removed, ignoring the usual cleanup etc.). Everything that cant be safely repeated should probably be ideally in alloy.js
master: https://github.com/appcelerator/titanium_mobile/pull/8747
Did a check with the PR & found an issue. Commented about it in the PR. Will continue to test after the issue has been looked at.
bump, i would appreciate this to be fixed soon since its an ongoing issue since 2013. I am too running into issues because I'm implementing push notifications and url schemes
PR merged. Waiting for build to close the ticket.
If this fix is going in 6.0.3, when will 6.0.3 be released? We just ran into this issue for one of AppC's big clients - we are the dev shop. Should I open an enterprise support ticket or is this going out really soon?
6_0_X: https://github.com/appcelerator/titanium_mobile/pull/8879
Verified the fix with SDK 6.1.0.v20170309181051. Waiting for 6.0.3 built to close the issue.
Verified the fix with SDK 6.0.3.v20170310122139. Closing the ticket.
Hi everyone, I recognize that this ticket is closed, but I am testing the functionality on 6.0.3.v20170322115949 and I still do not think this is working correctly. Let me provide a quick example. My app's main activity looks like this (from tiapp.xml):
I still see it hanging as soon as the android:launchMode="singleTask" flag is added, running on 6.0.3.GA. It works on the first launch only. If the app was already running and you try opening a link, it hangs.
We are seeing the same issue with it only working on first launch using 6.0.3.GA. Please re-open this ticket.
Also I don't know if this makes a difference, but we are using Hyperloop as well.
Reopening to investigate field reports.
Updated documentation and intent filter guide as part of https://github.com/appcelerator/titanium_mobile/pull/8897
I saw the documentation update in the most recent PR stating: "Defining launch modes using
android:launchMode
is not supported by Titanium Android. However,singleTask
behaviour can be accomplished when using [intent filters](http://docs.appcelerator.com/platform/latest/#!/guide/Android_Intent_Filters)." If we are responding to a custom scheme link initiated from the user's browser - how can we specify a true 'singleTask' launch of our application? I did not find any corresponding information for this in the linked-to documentation on intent-filter. It works for us if our app is closed, but when our app is already open we get stuck at the splash screen. Note we have set this property as well, with no change in result: {noformat}We have exactly the same situation as @jonesdhtx We currently have our schemes disabled for our Android users and enabled for our iOS users... not good at all.
I just saw that this issue was marked as resolved....what is the resolution for handling url schemes in Android? Are you opening another ticket for this? Do we need to open a new ticket?
[~jonesdhtx] What Titanium SDK version are you using? Could you provide example code to reproduce the issue?
Using 6.0.3.GA Here is some example code for showing the app is not getting launched as a new task for 2nd/subsequent invocations. Referring to the code below: 1) In initial state, app not running - In the phone browser click on the link in the test html page 2) App logs our custom message: "Launched as New Task" and opens successfully. 3) Now leaving app open, return to the test html page, and click on the same link again 4) App logs our custom message: "Launched as Resumed/Recycled Task" and the app ux shows only the splash screen Tiapp.xml is setup as: {noformat}
resolved? kudos to [~jonesdhtx]
Can we get this ticket re-opened now that we have the test case from David? Our client has an enterprise support ticket that is tracking this jira issue, but if you decide to not reopen we will need to reference a new ticket.
request re-open. I cant seem to retrieve intent data now when resuming my app from background
Please try https://github.com/appcelerator/titanium_mobile/pull/8897 (TIMOB-24316) for SDK Release 6.0.4.
i guess this has to be built from source? Anyone able to provide a .zip?
Looks like @Gary Mathews has put together a newer PR for 6.0.4 that includes #8897 as well as a couple other related PR's: PR# 8910 (https://github.com/appcelerator/titanium_mobile/pull/8910) which includes cherry-pick of #8894 #8897 #8908 addressing TIMOB-24497, TIMOB-24316, TIMOB-24527 How can we get an sdk build w/ these changes incorporated (prior to 6.0.4 release) to verify these fixes addressed our problem?
Everyone, please check this build below which is built for PR https://github.com/appcelerator/titanium_mobile/pull/8910 & let us know if this resolves your issue. Link: https://www.dropbox.com/s/lbc9tvzl8z7e34w/6.0.4.zip?dl=1 *NOTE* : Do not use this for any of your production apps as this build is not GA & might have bugs/issues.
The #8910 PR build (https://www.dropbox.com/s/lbc9tvzl8z7e34w/6.0.4.zip?dl=1) is an improvement but the issue is not fully resolved.
Before #8910 PR, I was getting this:
1) From initial state with app not running - In the phone browser click on a link in a test html page eg: Launch App 2) Our app opens successfully and handles the link accordingly 3) Now leaving app open, return to the test html page, and click on the same link again 4) Our app ux hangs and shows only the splash screenNow with #8910 applied, I am getting this:
1) From initial state with app not running - In the phone browser click on a link in a test html page eg: Launch App 2) Our app opens successfully and handles the link accordingly 3) Now leaving app open, return to the test html page, and click on the same link again 4) Our app now opens successfully and handles the link accordinglyALSO: I noticed that it will fail in the same way if the app is open before the initial link click:
1) From initial state with app not running - start the app from the app launcher 2) With the app launched and running normally, leave it open and then in the phone browser click on a link in a test html page eg: Launch App 3) Our app ux now hangs and shows only the splash screenThanks for the feedback David, we are looking into it. But please use the
Preview
button on the left to check your comment before submitting. All watchers (62+) just received 11 mails regarding your edited comment. We are trying to keep the flow as clean as possible, thx.I'm having issues with the test build of 6.0.4.GA too. I've created a post at https://github.com/appcelerator/titanium_mobile/pull/8910 (i dont know if this is the preferred location to post that)
Can we get this ticket re-opened so the axway support team doesn't update our enterprise support tickets as resolved?
[~jonesdhtx] I can't reproduce your problem, try this build: [6.0.4_20170406_ddc37c0.zip](https://www.dropbox.com/s/404tr0d1kv54o9w/6.0.4_20170406_ddc37c0.zip?dl=1)
@Gary Mathews - The 6.0.4_20170406_ddc37c0.zip build did not help the failing scenarios I encountered, and it also re-introduced the earlier problem I was having as well. Since you are not able to reproduce the issue on your side - I will try to find some time to put together a full minimal project demonstrating the issues that I can zip up and attach to this ticket.
@Gary Mathews - Here's a link to a sample project zip that demonstrates the problem: https://www.dropbox.com/s/5o4v1bayqlsr83t/SampleProject.zip?dl=0 However, after playing around with it some more, I'm thinking the reason you weren't seeing the problem before might have to do with the use of back button vs home button (or task switcher). Here's a copy of the sample project readme w/ more details:
SampleApp README
This sample app demonstrates that under the scenario outlined below, the app is not getting launched as a new task on Android. Once the sample app is built and deployed to device, you can access the following web page to launch it: [https://obscure-beach-58781.herokuapp.com] The above page simply contains the following 2 links for launching the app:One more note: As mentioned by another poster, there is another issue that is occuring when the app is closed and opened for first time. When doing so we see a "double" launch effect where splash screen and initial screen seems to get displayed twice quickly upon launch.
Failure Mode
If you use the "Task Switcher" to switch between apps you see that the app is not started as a new task. Alternatively, if you press the home button to leave the app and reopen the browser from launch screen it will fail in the same way. * App closed * In browser, launch sample app w/ link to order 11112222: 1: *_ App started as New Task _* 2: Handling: sampleapp://order/11112222 * Use the "Task Switcher" to go back to browser * Launch w/ link to order 11112222 again: 1: *_ App started as Resumed/Recycled Task _* 2: Handling: sampleapp://order/11112222 * Use the "Task Switcher" to go back to browser * Launch w/ link to order 33334444: 1: *_ App started as Resumed/Recycled Task _* 2: Handling: sampleapp://order/33334444Success Mode
However if you use the back button to switch between apps it is behaving as expected: * App closed * In browser, launch sample app w/ link to order 11112222: 1: *_ App started as New Task _* 2: Handling: sampleapp://order/11112222 * Use the "Back Button" to go back to to browser * Launch w/ link to order 11112222 again: 1: *_ App started as New Task _* 2: Handling: sampleapp://order/11112222 * Use the "Back Button" to go back to to browser * Launch w/ link to order 33334444: 1: *_ App started as New Task _* 2: Handling: sampleapp://order/33334444Notes:
The key may be in the differences in back vs home button handling on android. There are multiple sources of info on this, here's a quick description grabbed from [http://stackoverflow.com/questions/34081835/difference-between-back-press-and-home-button-in-android] {quote}After you start an activity, if HOME key is pressed, then the current activity is stopped and its task goes into the background. The system retains the state of the activity - i.e. onSaveInstanceState will be called. If the user later resumes the task by selecting the launcher icon that began the task again, the task comes to the foreground and resumes the activity at the top of the stack. However, if BACK key is pressed, the current activity is popped from the stack and destroyed. The assmuption is the activity is done and will not be used again. So the system does not retain the activity's state - i.e. onSaveInstanceState will not be called. {quote}[~djonesax] That is expected and usually preferred behaviour, [FLAG_ACTIVITY_NEW_TASK](https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_NEW_TASK) states "_When using this flag, if a task is already running for the activity you are now starting, then a new activity will not be started; instead, the current task will simply be brought to the front of the screen with the state it was last in._".
However - the current task is not simply being brought to front of the screen. We are seeing that the our app is being "pseudo-restarted" when we get the second invocation. In other words: we see that our Alloy.Globals object is still intact via an explicit property we set when first launched , but yet a new instance of our application controller/view is being created and shown.
Were you able to recreate the issue? Are you planning to address the issue on this ticket or should we create a new one?
This is definitively not resolved for me. I'm on 6.1.1.GA and I added
<property name="intent-filter-new-task" type="bool">true</property>
inside my tiapp.xml: - launch the app - push it to the background using the home button - click on a link that can be handled by the app - the app tries to reopen itself but it crashes with the stacktrace reported belowCleaning up older fixed issues. If this issue should not have been closed as fixed, please reopen.
I am experiencing this same issue with 7.4.1GA. If my app is not started and I launch it with a url scheme it launches. If the app is already running and I launch it with a url scheme it freezes at the splash screen. This issue seems to still be broken.
This "singleTask" behavior is not a bug and is by Google's design. When using launch mode "singleTask", the Android OS will automatically close all child activity windows when resuming your app. The splash screen is the root activity. So, the app is not hung. The child windows have been forcefully closed by the Android OS and Titanium does correctly fire their associated "close" events. As of Titanium 8.0.0, we're providing another option which doesn't involve "singleTask" (which I recommend that you don't use). In 8.0.0, we've completely refactored our intent handling so that an existing app instance will be resumed and the "newintent" event will be correctly fired when a new intent has been received. This way an Android app behaves a bit more like iOS, which is what everyone is after. Please see [TIMOB-26075] for more details.