[TIMOB-16066] Android: Add "foreground" service support
GitHub Issue | n/a |
---|---|
Type | New Feature |
Priority | High |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2018-06-06T01:31:04.000+0000 |
Affected Version/s | n/a |
Fix Version/s | Release 7.3.0 |
Components | Android |
Labels | android, background, demo_app, exalture, foreground, services |
Reporter | Dirlei Dionísio |
Assignee | Joshua Quick |
Created | 2013-12-21T20:36:49.000+0000 |
Updated | 2018-06-19T21:57:02.000+0000 |
Description
Some tasks must be done in a background services but they are essential to an app (like playing audio in a music player, radio player or audiobook player). The problem is that when we use standard background services, Android can kill the service, resulting in a bad user experience.
From the Android documentation:
"A started service can use the startForeground(int, Notification) API to put the service in a foreground state, where the system considers it to be something the user is actively aware of and thus not a candidate for killing when low on memory." (1)
"By default services are background, meaning that if the system needs to kill them to reclaim more memory (such as to display a large page in a web browser), they can be killed without too much harm. You can set this flag if killing your service would be disruptive to the user, such as if your service is performing background music playback, so the user would notice if their music stopped playing." (2)
It will be very appreciated if we can use foreground services in Titanium without the need of writing a native Android module.
Thank you for your valuable suggestion. Titanium SDK currently does not support foreground services so moving this feature request to engineering for further evaluation and prioritization.
Some draft of mine: https://github.com/enzy/titanium_mobile/commit/a979da5d5887612d9fc0d297882f7518eb566c5b Currently I don't know how to pass notification (NotificationProxy) to startForeground method.
Matěj: I don't think that will work. The services use a java-template that is included in the project by the _build.js script. I created a custom template and added the startForeground() call and successfully managed to start a foreground service. However, as soon as the app is killed (but the service keeps running) the javascript code stops running. After having looked at the source code deeper I came to the conclusion that the Kroll class is static, which means only one copy can be run, and that copy is killed along with the app process itself. So the problem seems to be much much deeper than just calling startForeground() etc. But I am not a java programmer, so I may have misunderstood how the underlying code works.
Would like to see an architecture proposal first on how to solve this.
There is an open source GPS tracker that handles this well on Android. Of course its written in Java, however I wonder if this may assist in working out how Appcelerator could be enhanced to support this functionality. http://bk.gnarf.org/creativity/bigbrothergps/
Hi, something new? I need it for gps tracker in android oreo in background
Android 8.0 and above automatically throttles location data collection while the app is backgrounded to about 3 times per hour. This is regardless if your app targets Android 8 or not. The only solution is to use a foreground service. https://developer.android.com/about/versions/oreo/background-location-limits.html Android 8.0 (aka: API Level 26) has a new
Context.startforegroundservice()
method, but I don't know if we need to call it overContext.startService()
since you're still required to callService.startForeground()
afterwards within 5 seconds (this is in regards to location data throttling). This requires research and will determine how to implement this in Titanium.Rainer Schleevoigt is doing some work on this which may help... https://github.com/AppWerft/Ti.LocationUpdatesService
Hey guys! Any news? I'm struggling to get location updates like on android 7 right know I'm blocked
The current plan is to add this feature to Titanium 7.3.0, but we don't have an ETA for when that will be released yet. You may want to try the module mentioned by Greg above. I haven't tried it, but I can see in its code that it calls
Service.startForeground()
which should do the job.As far as I can tell the AppWerft code is not currently working. It's close... but not quite...
I appreciate your answer Joshua, but for me background process is the key for some of my apps, new clients with android 8 can't use it. SInce 31 people start watching this, I think it's time to set priority high.
Bumping priority. Moving to 7.4.0 to be worked on as first priority.
Thanks [~jquick], we'll move it back to 7.3.0.
PR (master): https://github.com/appcelerator/titanium_mobile/pull/10076
*Note to everyone:* If you start a "Service", but never stop it upon app exit, then the service will keep the prior JavaScript runtime alive as per the design choice made by [TIMOB-9831]. The negative consequence of this is that when you attempt to restart the app (tap on the app in the app list), then you'll be stuck on the splash screen and the "app.js" won't get loaded until the service has been stopped. Only one JavaScript runtime can exist at a time and we can't spawn a new one until the service has released the last one. (I don't necessarily agree with this design, but changing it now would be a "breaking-change" for those who depend on it.) That said, there are 2 solutions to address this concern. *Solution 1:* Stop the service upon app exit. You can do this by listening for the "close" event from the root window as follows.
*Solution 2:* Prevent the root window from being closed by listening for its "androidback" event and use an intent to go to the home-screen (simulates a Home button press). This effectively makes your Android app work more like iOS.
I have cherry-picked the changes from [#10076](https://github.com/appcelerator/titanium_mobile/pull/10076) and [#9945](https://github.com/appcelerator/titanium_mobile/pull/9945) into 7_1_X, so developers can use it with their stable GA until 7.3.0 gets released. Important notes before downloading the custom SDK: - It is "7.1.2", a 7.1.1.GA + the changes of this ticket - The changes have not passed the QE-validation so far - The custom SDK is no official build, so please use with caution! Download: https://www.dropbox.com/s/k9n73ow64hq9f51/mobilesdk-7.1.2-osx.zip?dl=1
Hans you made my day!
Hi, Thanks a lot for the fix and for the cherry pick! I tested the cherry picked sdk with 4 phones : * Samsung Galaxy J5 / Android 7.1.1 * BQ Aquaris M5 / android 7. * Google pixel 2 / Android 8 * Sony Xperia F8331 / Android 8.0.0 With the same code It's working for many hours in the the Samsung, BQ and Pixel, but for the Sony I got this error:
My code:
In my tiapp.xml I have.
Should I have created a Notification Channel ?
[~pako.artal@htg-express.com], it sounds like the
launchIntent
property is returningnull
. We'll look into it.And ideally, yes, you should create your own notification channel on Android 8.0 and above. That way you can control the name and importance level given to it, as seen by the end-user. But this is not the cause of the error that you are seeing.
[~pako.artal@htg-express.com], After some investigation, it turns out the native Java [Android.getActivity()](https://developer.android.com/reference/android/app/Activity#getIntent()) method can in fact return
null
in rare cases. Looks like you're running into one of them. This is the method that Titanium'sTi.App.Android.launchIntent
andTi.Android.Activity.intent
properties acquire the launch intent from. https://stackoverflow.com/questions/37856407/can-activity-getintent-ever-return-null So, a simple solution to this problem is to set up the notification to not do anything when tapped on. You can do this by setting up the pending-intent with an empty intent like the below. Note that the foreground notification won't disappear when tapped on, making this a viable solution.Or you can do something like the below. Check if
launchIntent
isnull
, and if it is, use an empty intent instead.Thanks for your work on this! Is it possible to update the notification text after it has been set? I was trying to change the text every time a location is updated with something like 'Last location update 15:12' but it doesn't seem to do it consistently.
{quote}Is it possible to update the notification text after it has been set?{quote} [~engwei], yes, you can update the notification's text by calling the
foregroundNotify()
method again with the same notification ID you used before. This means that you cannot simply just update the notification object's text. The updated notification object has to be re-posted to the status bar via a call toforegroundNotify()
. This is how it works at the native level. Also note that the same is true for notifications posted viaNotificationManager.notify()
. If you want to update a notification posted viaNotificationManager
, then you must call itsnotify()
method again with the same notification ID you used before. We made sure to make the behavior consistent for foreground services. I hope this helps!Thanks Joshua that works perfectly. I was trying to use setLatestEventInfo() and that seemed to only work periodically. Your method works perfectly. Thanks
I received this crash today which I think looks like it's related to foreground service: java.lang.RuntimeException: Unable to resume activity {com.ingelby.grititranger/org.appcelerator.titanium.TiActivity}: java.lang.NumberFormatException: s == null at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3788) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3828) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1746) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6938) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374) Caused by: java.lang.NumberFormatException: s == null at java.lang.Integer.parseInt(Integer.java:570) at java.lang.Integer.parseInt(Integer.java:643) at com.appcelerator.aps.APSAnalyticsModel.addEvent(APSAnalyticsModel.java:78) at com.appcelerator.aps.APSAnalyticsHelper.sendAnalyticsEvent(APSAnalyticsHelper.java:518) at com.appcelerator.aps.APSAnalyticsHelper.postAnalyticsEvent(APSAnalyticsHelper.java:506) at com.appcelerator.aps.APSAnalytics.sendAppForegroundEvent(APSAnalytics.java:133) at org.appcelerator.titanium.TiBaseActivity.onResume(TiBaseActivity.java:1323) at org.appcelerator.titanium.TiActivity.onResume(TiActivity.java:65) at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1361) at android.app.Activity.performResume(Activity.java:7344) at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3763)
Verified the fix in SDK 7.3.0.v20180618182516. Closing.