May 1 | 3:50 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
May 1 | 3:55 PM |
Mathias | has entered the room |
Mathias |
Hi Mark
|
Mark M. |
hello, Mathias!
|
Mark M. |
how can I help you today?
|
Mathias |
Hope all is well
|
Mathias |
we talked last week about geofences and notifications, and that i should create a service to handle the server calls, not sure if you remember
|
Mark M. |
vaguely
|
Mathias |
hehe
|
Mark M. |
I remember geofences being a chat topic
|
Mathias |
great. well, i have some issues, have prepared a question:
|
Mathias |
View paste
|
Mark M. |
whoa
|
Mark M. |
either do not do an async callback inside your IntentService, or do not use IntentService
|
May 1 | 4:00 PM |
Mark M. |
IntentService already gives you a background thread
|
Mark M. |
you don't need another one
|
Mark M. |
and, once onHandleIntent() returns, the IntentService is destroyed
|
Mark M. |
which means you cannot use it to show a Notification
|
Mark M. |
(frankly, I would expect a crash here, though I haven't tried this scenario specifically)
|
Patrick B. | has entered the room |
Patrick B. |
Hello all
|
Mark M. |
(BTW, hello, Patrick -- I will be with you shortly!)
|
Patrick B. |
(ok :) )
|
Mark M. |
Mathias: depending on what you are using for your server call, you may be able to switch to a synchronous API
|
Mark M. |
(e.g., execute() vs. enqueue() with OkHttp3)
|
Mathias |
But you, and threads on stackoverflow say that if you need something to run in the background, like asyncs, you should have the service do the job
|
Mark M. |
correct
|
Mark M. |
Service itself has no background threads, forcing you to implement that yourself
|
Mark M. |
IntentService has a background thread, with specific rules for its use
|
Mathias |
yeah, and i do that by doing an async call?
|
Mark M. |
and you are welcome to do that... if you switch from IntentService to Service
|
Mark M. |
move your async call to onStartCommand()
|
Mark M. |
and call stopSelf() when the async call is completed
|
Mark M. |
this gives you the effect of an IntentService (only around when it is doing work), without the dueling background threads
|
Mark M. |
personally, I tend to use IntentService, but with synchronous work in onHandleIntent()
|
Mark M. |
but, either way works
|
Mark M. |
let me take a question from Patrick, and I will return to you in a bit
|
Mark M. |
Patrick: your turn! do you have a question?
|
May 1 | 4:05 PM |
Mathias |
ok
|
Patrick B. |
View paste
|
Mark M. |
thanks!
|
Patrick B. |
View paste
|
Mark M. |
"Was the cwac-endless Project deprecated?" -- yes
|
Mark M. |
"and the repository for building is gone" -- I think it is marked as private now
|
Mark M. |
"I haven't found any announcement in your newsletters that it was going away" -- the README had a "discontinued" notice for quite some time
|
Mark M. |
"Is there a replacement or suggested migration?" -- I have no idea
|
Mark M. |
I was not using it, and my implementation sucked, which is why I discontinued it
|
Mark M. |
I moved a bunch of the discontinued projects to private repos last year
|
Mark M. |
I imagine that there are some endless options out on the Android Arsenal
|
Mark M. | |
Mark M. |
ack
|
Mark M. | |
Patrick B. |
View paste
|
Mark M. |
if you need a copy of the source, for your own repo, drop me an email at mmurphy }at{ commonsware.com, and I'll send it to you
|
Patrick B. | |
Mark M. |
yes, let me switch back to Mathias, and I'll return in a bit for your second question
|
May 1 | 4:10 PM |
Mark M. |
Mathias: your turn! do you have another question?
|
Mark M. |
Mathias?
|
Mark M. |
Mathias, let me know when you're back in the chat
|
Mark M. |
in the meantime...
|
Mathias |
hey
|
Mark M. |
Patrick: on to your next question!
|
Mathias |
do patrick!
|
Mathias |
:)
|
Mathias |
im reading
|
Mark M. |
OK
|
Patrick B. |
Ok great :)
|
Patrick B. |
View paste
|
Mark M. |
"As far as I know, there is AsyncTask, Loaders, and the "bring your own implementation" approaches" -- oh, there are many more than that :-)
|
Patrick B. |
I am not surprised to hear that ;)
|
Mark M. |
when you retrieve data, are you saving it to disk, or is it only going to the UI, being discarded as the user navigates?
|
May 1 | 4:15 PM |
Patrick B. |
It varies. For some parts (messaging) I store it in SQLite. but mostly it is discarded and reloaded on demand.
|
Mark M. |
for the stuff that you are persisting, I'd lean towards an IntentService, coupled with an event bus to let the UI layer know about the new data
|
Mark M. |
for the stuff that is more or less disposable, the "cool kids" are using Rx, which is why I added a chapter on it
|
Mark M. |
personally, I'm happy with bare threads and an event bus
|
Mark M. |
there is no single "best practice" in this area
|
Mark M. |
so, by and large, if it is working for you, and you are comfortable with it, it's all good
|
Patrick B. |
Ok great. I'll definitely check out Rx as well.
|
Mathias |
+1 for eventbus, makes code much more readable than broadcasting everywhere
|
Patrick B. |
I wasn't sure especially with the "war on background processing" as you put it
|
Mark M. |
nothing of what you're doing here is background processing, though
|
Mark M. |
by "background processing", I don't mean threads
|
Mark M. |
I mean doing work when you're not in the foreground
|
Patrick B. |
Ah ok, my mistake
|
May 1 | 4:20 PM |
Patrick B. |
(end of my second question :) )
|
Mark M. |
Mathias: do you have another question?
|
Mathias |
sure
|
Mathias |
in my intentservice right now, i fire up a locationmanager, fetch my location via callbacks, when i have one i make an async server call, then create a notification. all the callbacks are methods in the intentservice itself.
|
Mathias |
all works fine, except for the final notification-creation
|
Mark M. |
all works fine temporarily
|
Mark M. |
your process might be terminated before that work gets done
|
Mark M. |
because your work is extending beyond the timeframe of when your service is running
|
Mathias |
reading up now, i notice that intentservice works basically like broadcastreceiver, so my understanding is now that i basically have gained nothing by calling the intentservice from my broadcastreceiver, since they both die at the end of the method?
|
Mark M. |
no
|
Mathias |
ok?
|
Mark M. |
first, onReceive() is called on the main application thread; onHandleIntent() is called on a background thread
|
Mark M. |
hence, a BroadcastReceiver, alone, is not suitable for doing anything significant (i.e., over a millisecond or so)
|
Mathias |
not sure that can be true, my broadcastreceiver is a geofence callback, called when the app is killed
|
Mathias |
(called by the system)
|
Mark M. |
so?
|
Mark M. |
every Android SDK app process has a main application thread
|
Mathias |
so how can it be "on the application th" ah! ok
|
Mark M. |
that is why we call it the "main application thread" nowadays, and not the "UI thread"
|
Mathias |
gotcha
|
Mark M. |
so, we team up a manifest-registered receiver with some sort of service, where the service handles the long-running work
|
Mark M. |
your job is to ensure that the service is running until the work is completed
|
Mark M. |
the two patterns for this are:
|
Mathias |
ok... so my intentservice can live way longer, but only until the method exits, or at least no guarantees
|
Mark M. |
1. use IntentService, but without asynchronous work
|
May 1 | 4:25 PM |
Mark M. |
2. use Service, with asynchronous work, and calling stopSelf() yourself when you are done
|
Mark M. |
in your case, asynchronous work is unavoidable, assuming that you're not just relying upon getLastKnownLocation()
|
Mathias |
precisely
|
Mathias |
what i was going to say... :)
|
Mark M. |
so, use Service, and call stopSelf() when you are dong with all your asynchronous work
|
Mathias |
so i have to do 2.
|
Mark M. |
I used to have a CWAC-LocPoll library that demonstrated this
|
Mark M. |
like CWAC-Endless, it sucked
|
Mathias |
hehehe ok
|
Mark M. |
and so it was discontinued, etc.
|
Mark M. |
I don't think that I have an example of this pattern in the book
|
Mathias |
well, i will have to code it. this seems like a classical case of "better not forget to close up shop afterwards"...
|
Mark M. |
most of my services either are IntentServices or are manual shutdown by the user (e.g., via some Notification)
|
Mathias |
stopSelf seems scary
|
Mark M. |
why? it's the service equivalent of finish() on an activity
|
Mathias |
yes, but you usually don't call finish yourself
|
Mark M. |
it says "I'm done here -- see ya!"
|
Mark M. |
sure you do
|
Mark M. |
most activities started with startActivityForResult() call finish()
|
Mark M. |
for example
|
Mathias |
yeah, true
|
Mark M. |
most splash screens call finish() (for those folk who insist upon splash screens)
|
Mark M. |
it's not typical for "traditional" activities, but it certainly gets used
|
Mark M. |
and, under the covers, IntentService is calling stopSelf(), after onHandleIntent() returns, if no more commands had been queued up
|
Mathias |
yes, but i don't have to remember to make sure i clean up
|
May 1 | 4:30 PM |
Mark M. |
:: shrug ::
|
Mathias |
hehe yep
|
Mathias |
well thanks, i guess i'll get on it
|
Mark M. |
OK
|
Mark M. |
Patrick: back to you for question #3!
|
Patrick B. |
Fantastic, I have some overlap with Mathias' question. I haven't quite caught all so it might be a bit repetitive
|
Patrick B. |
View paste
|
Patrick B. |
Now, I'm not quite sure this is the way to go, since you wrote above " use IntentService, but without asynchronous work"
|
Patrick B. |
Would this be a good implementation or am I way off?
|
Mark M. |
by that, I mean that IntentService should not make asynchronous calls itself
|
Mark M. |
IntentService already has a background thread, and so synchronous calls are the way to go
|
Mark M. |
if you are working with some API that has no synchronous option (e.g., requesting location updates), a Service may be fine, but IntentService is not
|
Patrick B. |
Ah I see, so an IntentService should not request LocationCallbacks for example. Gotcha.
|
Mark M. |
right, because that API is intrinsically asynchronous
|
Mark M. |
(much to the chagrin of many a new developer)
|
Mark M. |
using an IntentService for posting the information to the server is a reasonable approach
|
May 1 | 4:35 PM |
Mark M. |
particularly if this stuff needs to be done quasi-real-time
|
Patrick B. |
Great. And combined with the Eventbus input the feedback to the UI sounds nice too.
|
Mark M. |
right
|
Mark M. |
if you get into a situation where you cannot perform the work now (e.g., no network connectivity), JobScheduler is a fine solution, if your minSdkVersion is 21 or higher
|
Mark M. |
in fact, over time, we are likely to move more work to JobScheduler and away from IntentService, given some Android O changes to service behavior
|
Patrick B. |
ok. currently still at 16 (will have to check that)
|
Mark M. |
yeah, you'll need to figure out how you want to handle a long-term lack of connectivity, then
|
Mark M. |
push come to shove, you would use AlarmManager to retry every so often, until the work can get completed
|
Patrick B. |
I'd have subscribed to network events for that and simply rescheduled the IntentService...
|
Patrick B. |
(put a new Intent once network was up)
|
Mark M. |
that will work pre-7.0 AFAIK
|
Mark M. |
it definitely will not work on Android O
|
Mark M. |
and I forget if the events that you are interested in are eligible as broadcasts in the 7.x versions
|
Mark M. |
the challenge comes when you have this work to be done, but the user navigates away from the app
|
Mark M. |
then, you need something outside of your process to be able to start up a process for you, as your original process may have been terminated
|
Mark M. |
JobScheduler handles this, plus the connectivity checks
|
Mark M. |
and so represents a nice "all in one" solution
|
May 1 | 4:40 PM |
Mark M. |
on older devices, JobScheduler is not an option, so you can try watching for connectivity-change broadcasts
|
Patrick B. |
Ok, that is going to make my life complicated. Especially since the penetration of new Android versions is so slow.
|
Mark M. |
yup
|
Patrick B. |
View paste
|
Mark M. |
no, I haven't attended I|O in person in a few years
|
Mark M. |
Mathias: back to you! do you have another question?
|
Patrick B. |
Ok, in that case: Thank you again for your answers!
|
Mark M. |
you're very welcome!
|
Patrick B. |
I'll be signing off. Have a nice afternoon!
|
Mark M. |
you too!
|
Patrick B. | has left the room |
May 1 | 4:55 PM |
Mathias |
Hey mark. let me first quickly say thanks for the great explanations, hadn't gotten all about intentservice dying as soon after method exit
|
Mathias |
i might have something more, going to check my notes
|
Mathias |
Right! one quick one about eventbus register/deregister
|
Mark M. |
go ahead
|
Mathias |
my app is tabbed, with a activity with fragments as tabs
|
May 1 | 5:00 PM |
Mathias |
the fragment onstop/onstart are called when showing and not showing, but since they are brought back when user clicks tab, i want them to recieve my data-update events even when not showing.
|
Mathias |
so i register/unregister in oncreateview/ondestroyview. I have not found much about this, almost all examples are onstart/onstop or pause/resume. But do you see a problem with this approach?
|
Mark M. |
that's probably OK
|
Mathias |
my thoughts here is so that i don't have to redraw all components in onresume every time if you see what i mean
|
Mathias |
ok great
|
Mark M. |
OTOH, you are potentially wasting CPU cycles for UI updates that the user will never see
|
Mark M. |
you need to decide which is better
|
Mathias |
true
|
Mark M. |
and that's a wrap for today's chat
|
Mathias |
have a good one
|
Mark M. |
the transcript will be posted to https://commonsware.com/office-hours/ in a bit
|
Mark M. |
the next chat is Wednesday at 7:30pm US Eastern
|
Mark M. |
have a pleasant day!
|
Mathias | has left the room |
Mark M. | turned off guest access |