Jun 13 | 9:55 AM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Jun 13 | 10:00 AM |
bbarnes | has entered the room |
Mark M. |
hello, bbarnes
|
Mark M. |
how can I help you today?
|
bbarnes |
Hi!
|
bbarnes |
I took your advanced course in NYC last summer
|
bbarnes |
I have a question regarding Android Services
|
bbarnes |
That is, with a started service
|
Jun 13 | 10:05 AM |
Mark M. |
go ahead
|
bbarnes |
If the app is going to communicate with the service on an ongoing basis
|
bbarnes |
Does it seem right to pass data to the service by calling startService over and over with Intents?
|
Mark M. |
seems fine to me
|
bbarnes |
The original call will indeed start the service, and further calls will just pass the intent
|
bbarnes |
Okay, thats good
|
bbarnes |
It seemed like a questionable design, just because of that fact that "START service" would be called potentially many times
|
bbarnes |
and frequently
|
Mark M. |
it's not the best method name
|
Mark M. |
however, as I probably said many times in training, "they didn't ask me"... :-)
|
bbarnes |
What are the best practices for communicating with services?
|
Mark M. |
that's a bit difficult to answer in the abstract
|
Jun 13 | 10:10 AM |
Mark M. |
can you be more specific?
|
bbarnes |
As far as our application of services...our app
will be communicating with and controlling a physical system that
controls lights/shades/etc
|
bbarnes |
The service was intended to facilitate the communication with the system itself
|
bbarnes |
that way if we wanted to reuse that framework for
another app, widget, etc they could all just use the same communication
service
|
bbarnes |
so now we
|
bbarnes |
we're* looking at how to best pass the data back
and forth between the app and the service that will be maintaining the
connection to the system
|
bbarnes |
the connection being a TCP connection
|
Mark M. |
you have multiple options
|
Mark M. |
I would first focus on how you are going to get the data from the service back to the UI or other portions of your app
|
Mark M. |
personally, I would use something along the lines of an event bus: LocalBroadcastManager, Otto, greenrobot's EventBus, etc.
|
Mark M. |
however, that assumes that the data coming down
the TCP connection is a bit "transactional" and can be represented in
discrete objects, versus some sort of stream
|
bbarnes |
The current proposal was that the service would communicate system update back to a DAO layer that the UI would observe
|
Mark M. |
you could do that too
|
Mark M. |
whether that's via a ContentProvider and ContentObservers, or a POJO-based DAO layer with an event bus, or whatever
|
Jun 13 | 10:15 AM |
bbarnes |
yeah it's all transactional in the way that it
would be commands from the app thru the service and out to the system,
and then responses to those commands or independent status updates from
the system back to the app
|
Mark M. |
once you have the service->rest of app
communication mechanism decided, you can determine if you want to use
that same mechanism to control the service, or something else
|
Mark M. |
so, for example, if you're using a flexible
message bus, you could send commands to the service via bus messages,
versus startService()
|
Mark M. |
advantage being greater communications parallelism
|
Mark M. |
disadvantage perhaps being figuring out where to startService() the first time
|
bbarnes |
could you elaborate on the bus messages? that sounds more ideal for our application than the startservice
|
Mark M. |
personally, I've used LocalBroadcastManager and Otto
|
Mark M. |
LocalBroadcastManager is in the Android Support package
|
Mark M. | |
bbarnes |
start service for the first time would be easy on
our end because we have a process for intially connecting to a system
already in the flow of the app
|
Mark M. |
pros: reuses the same BrroadcastReceiver/IntentFilter stuff you may be used to
|
Mark M. |
cons: limits you to the same BroadcastReceiver/IntentFilter stuff
|
bbarnes |
awesome, we definitely didnt consider that before
|
Mark M. |
Otto is from Square
|
Mark M. | |
Mark M. |
third-party Apache-licensed library
|
Mark M. |
pros: much more flexible event model, with
whatever event object you want, and simple annotations for denoting what
methods receive the events
|
Mark M. |
cons: it's a third-party library
|
Mark M. |
I know of, but have not used, greenrobot's EventBus: https://github.com/greenrobot/EventBus
|
Mark M. |
their site lists pros/cons vs. Otto
|
Julien | has entered the room |
bbarnes |
Great, checking out the link
|
Mark M. |
LocalBroadcastManager has some coverage in the
book already, and I'll be adding a chapter profiling all of these in the
next update ort wo
|
Mark M. |
er, or two
|
Mark M. |
let me take a question from Julien, and I'll be back with you in a bit
|
Jun 13 | 10:20 AM |
Mark M. |
julien: hi! do you have a question?
|
Jun 13 | 10:20 AM |
bbarnes |
Sure, thanks
|
Julien |
hi yes
|
Mark M. |
Julien: go ahead
|
Julien |
I have a list with severals items (cover of
books). You can start download of each one (i'm using DownloadManager), i
would like to display the progress of each download (10% for instance)
directly in the row view
|
Julien |
I'm looking for the best architecture
|
Julien |
to query at regular time the DownloadManager
|
Julien |
and update the views
|
Jun 13 | 10:25 AM |
Mark M. |
um
|
Mark M. |
in a UI, postDelayed() is the lightest-weight means of doing loose timing
|
Mark M. |
so you could set up a postDelayed() "loop" to get control every so often
|
Mark M. |
each pass, you would call query() on DownloadManager with a suitable DownloadManager.Request
|
Mark M. |
you would use the resulting Cursor to pick out the COLUMN_TOTAL_SIZE_BYTES and COLUMN_BYTES_DOWNLOADED_SO_FAR
|
Mark M. |
compute your percentage based upon those
|
Mark M. |
at that point, how you update the UI depends a bit on what the UI is
|
Mark M. |
is the "row view" in a ListView? A TableLayout? something else?
|
Julien |
ListView with a BaseAdapter
|
Mark M. |
OK
|
Mark M. |
once you know the new percentage, you would update the data model behind the BaseAdapter, to handle scrolling
|
Mark M. |
plus, you would see if the row for this download
is visible in the ListView, get at the row in the ListView itself using
typical ViewGroup methods, and update it
|
Julien |
ok I see, it's better than calling notifyDataSetChanged each second i guess
|
Mark M. |
ListView has getFirstVisiblePosition() and getLastVisiblePosition() to help you determine if your row is visible
|
Jun 13 | 10:30 AM |
Mark M. |
yeah, notifyDataSetChanged() tends to be a bit invasive
|
Mark M. |
that's simpler, and if your UI will work with it, go that route
|
Mark M. |
my assumption was that notifyDataSetChanged()
would cause too much of an update, and therefore manually fiddling with
each row is more apropos
|
Julien |
yes
|
Mark M. |
what trips up developers doing this is forgetting
that the user *can* scroll, so you still need to do all the
ListView/ListAdapter stuff right for updating the percentage in
getView()
|
Mark M. |
let me switch back to bbarnes, and I'll be back with you shortly
|
Julien |
ok
|
Mark M. |
bbarnes: do you have another question?
|
bbarnes |
Well, I think we got the main question answered
|
bbarnes |
I do just want to run this by you though
|
bbarnes |
The potential design would now look like this: App uses bus to send and receive messages with the connection service
|
bbarnes |
Then the app is in control of updating the backend as we see fit, and the UI can still observe that as planned
|
bbarnes |
So the service would solely be there to maintain the connection with our system via a REST interface
|
bbarnes |
Anything look out of whack?
|
Mark M. |
"maintain the connection" = always-connected socket?
|
bbarnes |
correct
|
Jun 13 | 10:35 AM |
Mark M. |
and that's what your server needs?
|
Mark M. |
I'm confused as to where the REST interface comes into play
|
bbarnes |
As per the current design as far as I know
|
Mark M. |
as that's usually reserved for stateless HTTP
|
Mark M. |
but, other than that, what you have sounds fine
|
bbarnes |
I actually am not very well versed in the REST protocol we are making, so thats the next thing we will investigate
|
bbarnes |
Maybe the connection can come and go
|
Mark M. |
the other thing is to make sure that you have a clear idea when you will call stopService()
|
bbarnes |
But right, either way the service's job will be to keep some connection available
|
bbarnes |
Oh right!!
|
bbarnes |
I almost forgot
|
bbarnes |
The plan for when to call that was in onDestroy of the first activity launched
|
bbarnes |
Are we violating any best practices by doing that?
|
Mark M. |
that's not an ideal plan, as if the user presses HOME, your service stays running
|
Mark M. |
you have two major options, as I see it
|
Mark M. |
1. if you determine that you are doing
REST-over-HTTP and do not need a persistent connection, switch to using
an IntentService and sending commands via startService(), as
IntentService knows to shut down when there is no more work queued up
|
Mark M. |
2. Attempt to use onUserLeaveHint() to figure out
that the app is going into the background, call stopService() then...
and then figure out when startService() will be needed in the future
(e.g., check some boolean before sending any messages)
|
Jun 13 | 10:40 AM |
bbarnes |
Okay - the thought was that if the user presses home, they can come back into the app and have no connection delay
|
bbarnes |
Ive never heard of onUserLeaveHint - sounds pretty useful!
|
Mark M. |
the docs around it are a bit wishy-washy, which is why I haven't written about it
|
Mark M. |
it's a callback method on Activity
|
Mark M. | |
Mark M. |
"Called as part of the activity lifecycle when an
activity is about to go into the background as the result of user
choice. For example, when the user presses the Home key,
onUserLeaveHint() will be called, but when an incoming phone call causes
the in-call Activity to be automatically brought to the foreground,
onUserLeaveHint() will not be called on the activity being interrupted. "
|
bbarnes |
Huh, since api 3
|
Mark M. |
is this an app for ordinary end users, or is this an in-house enterprise app?
|
bbarnes |
Definitely end users
|
Mark M. |
yeah, then, you really don't want to leave the service hang out forever
|
bbarnes |
Wouldnt the process get killed after awhile though?
|
Mark M. |
sure, but processes with a service in them live longer, generally speaking, than processes sans service
|
Mark M. |
so, the user runs your app, then goes into Angry
Birds, then takes a phone call, and returns from the phone call to see
that Angry Birds has to start back up at the beginning, because it got
killed rather than your process
|
Mark M. |
*eventually*, even a service-laden process will be terminated due to low memory conditions
|
Mark M. |
the key is device behavior in the meantime
|
bbarnes |
Alright, thanks
|
Jun 13 | 10:45 AM |
Mark M. |
unless that service is adding value while the app
is in the background, you really want to figure out a point when you can
stop it
|
Mark M. |
let me switch back to Julien
|
bbarnes |
Is there another good way to leave it running for a certain amount of time?
|
Mark M. |
Julien: do you have another question?
|
bbarnes |
Sure
|
Julien |
View paste
|
Mark M. |
no, in the activity with the ListView
|
Julien |
ok
|
Mark M. |
you only need to update the ListView while you have a ListView :-)
|
Julien |
Do you think it's ok having it running during the life of this activity ?
|
Julien |
or should i managed to handle start and stop
|
Julien |
?
|
Mark M. |
so long as your delay period isn't insane, sure
|
Mark M. |
oh, I see
|
Mark M. |
yeah, onStart()/onStop() is probably a good idea
|
Mark M. |
use removeCallbacks() in onStop() to break the "loop"
|
Mark M. | |
Mark M. |
here I use onResume()/onPause(), but onStart()/onStop() would work as well
|
Julien |
ok
|
Jun 13 | 10:50 AM |
Julien |
so in my loop i need to query the DownloadManager and update the visible row with progress
|
Julien |
if any
|
Mark M. |
yes
|
Julien |
And in getView
|
Mark M. |
yes
|
Julien |
why in getView ?
|
Julien |
If i got my loop
|
Julien |
updating
|
Julien |
the row ?
|
Mark M. |
if the user scrolls, you want to show the percentage of the newly-populated rows
|
Mark M. |
that percentage has to come from somewhere
|
Julien |
from the loop ?
|
Mark M. |
you could say that you'll show nothing, until the next pass of the postDelayed() loop
|
Mark M. |
but you want that loop to be slow (every few seconds)
|
Julien |
ok just to have a smooth UI
|
Julien |
i get it
|
Mark M. |
right
|
Julien |
Thanks, no more question for now
|
Mark M. |
bbarnes: you asked "Is there another good way to leave it running for a certain amount of time?"
|
Mark M. |
you are welcome to cook up your own sort of inactivity timer in the service
|
Mark M. |
i.e., if nothing has happened in X seconds, go ahead and tear things down
|
Mark M. |
the key is whether you can determine a reasonable
value of X, then deal with the somewhat non-deterministic state when you
come back to the foreground
|
bbarnes |
OK
|
Mark M. |
should all be doable, but will require a bit more thinking
|
bbarnes |
Are there any metrics/rules of thumb for that?
|
Mark M. |
none that I can cite
|
bbarnes |
...any that you cant? ;)
|
Jun 13 | 10:55 AM |
Mark M. |
sure: pick a value of X that will piss off the fewest users
|
Mark M. |
"-)
|
Mark M. |
er, :-)
|
bbarnes |
Gotcha
|
bbarnes |
Well this was really useful, I imagine we will be back for other office hours
|
bbarnes |
Thanks for your time
|
Mark M. |
you are very welcome
|
Mark M. |
note that the transcript from this chat will be archived at http://commonsware.com/office-hours/ shortly after the chat ends
|
Mark M. |
the next chat is Tuesday, 4pm Eastern
|
Mark M. |
any final questions?
|
bbarnes | |
bbarnes |
I answered that myself, but there is a final question - where should I report that?
|
Mark M. |
this is unique to the HTC One, in your tests?
|
bbarnes |
As far as I know
|
Mark M. |
I think HTC may be monitoring the 'htc' tag, but I'll pass the link along to one of my HTC contacts to make sure they see it
|
bbarnes |
My Droid DNA was alright
|
Jun 13 | 11:00 AM |
bbarnes |
thanks - Ill add that tag
|
Mark M. |
you already have it, I think
|
Mark M. |
yeah, it's there
|
bbarnes |
ah, right
|
Mark M. |
well, that's a wrap for today's chat
|
bbarnes |
great
|
Mark M. |
have a pleasant day, all!
|
bbarnes | has left the room |
Julien | has left the room |
Mark M. | turned off guest access |