Office Hours — Today, April 12

Thursday, April 7

Apr 12
3:55 PM
Mark M.
has entered the room
Mark M.
turned on guest access
4:15 PM
Michaelidle
has entered the room
Mark M.
hello, Michaelidle!
how can I help you today?
Michaelidle
Hello! I'm new around here, but have seen some of your talks. Good to meet you.
4:20 PM
Mark M.
good to (e-)meet you too!
Michaelidle
I'm a fairly experienced with Java, but still shaky when it comes to threading, especially on Android.
So please excuse the ignorance of my next few questions =)
Mark M.
no worries
Michaelidle
I have an IntentService, and inside of the onHandleIntent() method I open up a websocket, which has multiple callbacks onOpen, onFailure() etc. Am I digging myself a hole? I haven't pushed this into the app store yet, but I feel like it's wrong to have callbacks in an Android component (Service) that doesn't "exist" anymore. I'm not sure if I am making much sense.
Mark M.
you are making complete sense
and yes, off the cuff, this sounds bad
IntentService is a simple solution for synchronous background work
it is not designed to support asynchronous work, though
for that case, use a regular Service, with your own background thread, and calling stopSelf() when you know that you are done with the service
4:25 PM
Michaelidle
Yes, so essentially I do a synchronous background operation, and at the end, my last requirement is to connect to a socket.
I didn't necessarily "want" to connect to the socket in the IntentService, but it was convenient because the IntentService work that is done, ends up returning with an Object that I need to connect to the web socket.
Mark M.
¯\_(ツ)_/¯
either convert the whole shooting match to a regular Service with your own thread, or break the work into two services
Michaelidle
Another "requirement" is to essentially stay connected, until the process dies. If so, the web socket falls back onto GCM, so it's not a big deal.
Mark M.
um, well, the point behind a service is to try to keep the process rolling for a bit longer
Michaelidle
Okay, I may send it into another service entirely.
Yeah, I don't need to keep the process going too much longer. I'd be okay with this happening with the lifecycle of my activities.
So, what's the "bad" part of having callbacks in something like IntentService. onHandleIntent seems to finish, and my callbacks get called. Am I creating a leak?
4:30 PM
Mark M.
well, your use case is, um, odd
the "and my callbacks get called" part will be unreliable
Michaelidle
Unreliable? How so?
Mark M.
once onHandleIntent() returns, if there is no more work to be done, the IntentService calls stopSelf() and destroys itself
if, at this point, there are no more running services in your process, your process importance falls
Michaelidle
Correct.
Mark M.
hence, sometime after onHandleIntent() returns, your process will be terminated
that could be milliseconds, seconds, or minutes later
Michaelidle
My web socket service detects if I have disconnected and will fallback to sending me a push through GCM.
Mark M.
not if your process is terminated, it won't
if your process is terminated, everything is gone
::poof::
Michaelidle
Well, I think the server side of the web socket equation sends a message, sees that it couldn't sent it, and will send a GCM instead.
Mark M.
if the other party disconnects, while your process is still running, *then* you will get a callback
true, but this begs the question of why you're bothering with the WebSocket in the first place
personally, I don't trust code where I expect the process to killed in the middle of my doing some work
Michaelidle
Correct. I understand that I won't get a callback if my process dies. I'm okay with that, as the web socket server will detect that I didn't get the message and fallback onto GCM.
It's not that it's expected, it's just handling the case of the socket not being connected.
Mark M.
if your service is not running, you have to expect your process to terminate at any moment
4:35 PM
Mark M.
including in the middle of whatever I/O you are doing (WebSocket, disk, etc.)
Michaelidle
For example, if I reboot my phone and don't open my app, my server will send me a push notification. When I log back into the application, it will register for the socket, and use that socket for realtime communication.
Yeah, I see your point.
Mark M.
for some number of milliseconds, yes
now, that number of milliseconds may be "thousands" or "tens of thousands", or it may not be
if the work you are doing in response to the message is disposable, I suppose what you're doing might work
Michaelidle
So, onHandleIntent() returns, stopSelf gets called, and the IntentService destroys itself... but it does NOT destroy the callback...? correct?
Mark M.
well, the IntentService itself does not know anything about your WebSocket stuff
you may be at risk of garbage collection, so you'll need some static reference to that WebSocket (direct or indirect)
that might come "for free" with whatever thread is involved in your WebSocket code (yours or inside some library)
but otherwise, your WebSocket stuff should be oblivious to the service being destroyed... up until the process is terminated
Michaelidle
So the IntentService can be destroyed even though there is a listener/callback inside of it? How is that possible? (I know you just said that the IntentService doesn't know about my web socket stuff). But again, what if we just took it to a more generic level and forget that it's a web socket, and just a callback.
4:40 PM
Mark M.
"How is that possible?" -- again, IntentService knows nothing about your code
4:40 PM
Mark M.
bear in mind that "destroyed" refers to a state in Android terminology
it does not mean "garbage collected" or "has its bytes set to all 0's" or something
"destroyed" means "Android's gonna ignore it and let go of any references to it"
eventually, it will get garbage collected
but none of that has anything to do with your code
by the same token, it's not like, just because you registered an OnClickListener on a Button, that an activity cannot be destroyed
Michaelidle
Wouldn't it be impossible to garbage collect the service though if there's a callback registered?
Mark M.
that depends on who is holding what
and that gets back to your earlier point about a memory leak, which I failed to address
if the callback object has a reference to the IntentService, the IntentService can still be destroyed, but it cannot be garbage collected
this would represent a memory leak
your callback object might explicitly have a reference to the IntentService (e.g., you needed a Context and held onto the IntentService, since that's a Context)
or it might have an implicit reference (e.g., your callback is an inner class of the IntentService)
either would be a memory leak
Michaelidle
I think I understand. So in an Activity, just because I register a callback on a button, doesn't mean that I leak the activity because I never call an unregister on the button. But the button is effectively destroyed, so the callback would never be called.
Mark M.
right
and, if nothing else has a reference to that callback, that callback will be garbage collected
in your case, with the WebSocket, if the WebSocket code is calling back into your code, it must be managing some thread on your behalf
4:45 PM
Mark M.
so your callback probably cannot be garbage-collected, so long as all of that is outstanding
Michaelidle
Aha. That's what I'm missing here, the callback is actually an object, right... so if no one is holding onto that callback, it could be GC'd?
Mark M.
(I say "probably" because I do not know much about the WebSocket library in question, let alone its implementation details, beyond educated guesswork)
Michaelidle
I think this is making more sense, and I think if I just wasn't using anonymous inner classes, this would be a little bit clearer.
Mark M.
yeah, anonymous inner classes are handy but make this sort of work icky
so, once your service is destroyed, you are counting on the WebSocket library for keeping itself around (e.g., by the running thread referencing it), and thereby keeping your callback around
that might work
that might not work
it would depend upon the library implementation
you could "force it" by having a static reference to your callback somewhere, which amounts to an intentional memory leak
Michaelidle
Okay, so I think my solution, may be acceptable for right now. As you said before, the message I get is disposable. I may try to work out something better where I connect a socket at activity onStart and close the socket onStop, but traversing through activities seems like I may miss something... or make a ton of connections/disconnections to my server.
4:50 PM
Michaelidle
I really don't like to introduce statics, but maybe making a web socket helper/wrapper class singleton could work
Mark M.
you could collect a heap dump, shortly after your IntentService is destroyed
and see whether something is holding onto your callback
4:55 PM
Michaelidle
Great idea.
Shortly after the IntentService is destroyed or after the IntentService is GC'd?
Mark M.
after it is destroyed should be OK
the act of creating a heap dump basically does a full-process GC
Michaelidle
Really? Didn't know that. Thanks for the heads up. I usually do a force gc via the ADM.
Mark M.
well, the heap dump does not contain any garbage
from the logs, it appears they do this by doing a full-process GC, then generating the dump
it's possible that they just filter out garbage when writing the dump file, but leave the garbage in the process
regardless, for your analysis, after the IntentService is destroyed (and give a few milliseconds), the IntentService should no longer be tied to a GC root, and so it will not appear in the heap dump
5:00 PM
Mark M.
that's a wrap for today's chat
the transcript will be posted to https://commonsware.com/office-hours/ shortly
the next office hours are Thursday at 9am US Eastern
BTW, thanks for subscribing!
and have a pleasant day!
Michaelidle
has left the room
Mark M.
turned off guest access

Thursday, April 7

 

Office Hours

People in this transcript

  • Mark Murphy
  • Michaelidle