Oct 5 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Oct 5 | 7:25 PM |
Steve S. | has entered the room |
Steve S. |
Hi, Mark!
|
Mark M. |
hi, Steve!
|
Mark M. |
how can I help you today?
|
Steve S. |
I'm trying to create a timer that displays text showing how long in the past something happened
|
Steve S. |
i have code that compiles and runs, but i want to make sure there are no memory leaks and it can run on the main thread
|
Steve S. |
Here's the code:
|
Steve S. |
View paste
(6 more lines)
|
Mark M. |
this is massive overkill
|
Steve S. |
ok
|
Mark M. |
or, at least, it seems to be
|
Steve S. |
sure. how so?
|
Oct 5 | 7:30 PM |
Mark M. | |
Mark M. |
in my case, I am showing a Toast, but you could update your TextView
|
Mark M. |
you don't need a subclass of Handler, as postDelayed() exists on any View
|
Steve S. |
i see
|
Mark M. |
you don't need WeakReference, as you know when your TextView is going away and can call removeCallbacks() at that point
|
Steve S. |
ok - you know because onStop() is called?
|
Mark M. |
and postDelayed() (on View or Handler) is inexact, as you may be tying up the main application thread when the specific millisecond arises
|
Mark M. |
correct, or onDestroy(), or whatever lifecycle method makes sense in your scenario
|
Steve S. |
it doesn't have to be exact - an approximation is ok
|
Mark M. |
OK, but you could just call System.currentTimeMillis() and calculate the difference between your original time and now, and get an accurate result
|
Steve S. |
ok
|
Steve S. |
this is great. i'll review your example and try to simplify things
|
Mark M. |
there's material on it in the book
|
Steve S. |
great, i'll review that too
|
Steve S. |
on another topic, i had a follow-up question on the service we talked about last time
|
Oct 5 | 7:35 PM |
Steve S. |
i've tried to respond to the issue you raised
|
Steve S. |
here's the new version of the class i showed you last time:
|
Steve S. |
View paste
(6 more lines)
|
Mark M. |
you have a thread that is... starting another copy of the thread?
|
Steve S. |
yes. so that's a question: is that ok?
|
Mark M. |
it feels like it'll be an infinite loop
|
Mark M. |
(ish, considering that it's not technically a loop)
|
Steve S. |
it may be, but that's acceptable for our application
|
Mark M. |
you start the first ConnectThread
|
Steve S. |
i'll show you the code that starts things off:
|
Steve S. |
View paste
|
Mark M. |
that seems like it would still create thousands of threads a second, causing your device to catch on fire
|
Mark M. |
start() is asynchronous, as that's the point of a thread
|
Mark M. |
(rephrase: start() on a Thread is asynchronous)
|
Steve S. |
right
|
Mark M. |
so, here's what I see:
|
Mark M. |
you call the start() method in your second paste
|
Steve S. |
ok
|
Mark M. |
it gets an adapter
|
Mark M. |
it creates a ConnectThread and calls start() on it
|
Oct 5 | 7:40 PM |
Mark M. |
this then exits the LOCK block
|
Steve S. |
right
|
Mark M. |
meanwhile, ConnectThread invokes run()
|
Mark M. |
run() sees that the adapter is not enabled
|
Mark M. |
run calls postError() and creates/starts another ConnectThread
|
Mark M. |
'
|
Mark M. |
ConnectThread #2 invokes run(), sees that the adapter is not enabled, calls postError(), and creates/starts another ConnectThread
|
Mark M. |
ConnectThread #3 invokes run(), sees that the adapter is not enabled, calls postError(), and creates/starts another ConnectThread
|
Mark M. |
ConnectThread #4 invokes run(), sees that the adapter is not enabled, calls postError(), and creates/starts another ConnectThread
|
Mark M. |
ad nauseum
|
Steve S. |
that is correct
|
Mark M. |
um, that's bad
|
Steve S. |
i realize that this probably isn't a good idea in general
|
Mark M. |
the Android/Linux thread scheduler isn't designed for that kind of abuse, and I'm not sure what you're gaining by this approach
|
Steve S. |
what would be a better approach?
|
Mark M. |
some sort of polling loop with a nice fat delay per pass or something
|
Mark M. |
my assumption is that you want to find out as soon as the adapter is enabled
|
Steve S. |
sure
|
Mark M. |
and my further assumption is that the Bluetooth APIs don't offer a callback mechanism for that (as I haven't played with those APIs)
|
Steve S. |
no callback as far as i know
|
Mark M. |
in which case, having *a* thread monitor the adapter isn't necessarily a bad thing
|
Mark M. |
but that's *a* thread, not a continuous sequence of rapidly-spawned threads
|
Steve S. |
so would what i have be better if i introduced a delay?
|
Steve S. |
ok
|
Mark M. |
use a while loop
|
Oct 5 | 7:45 PM |
Steve S. |
ok
|
Mark M. |
while not enabled, sleep for X milliseconds, for as big a value of X as you can reasonably stand
|
Steve S. |
ok - you mean Thread#sleep()?
|
Mark M. |
or SystemClock.sleep(), which saves you dealing with the InterruptedException
|
Steve S. |
great
|
Mark M. |
this sort of polling isn't ideal -- if there were a callback, use it
|
Mark M. |
but, a simple check-and-sleep loop is way better than bludgeoning your CPU with a lead pipe
|
Steve S. |
great. very helpful
|
Steve S. |
i'll take another pass at this then
|
Steve S. |
i have one last question, on CountDownTimer
|
Mark M. |
if it has to do with what isTheFinalCountdown() actually does, I haven't tried it
|
Steve S. |
i don't think i'm up to that version of Android yet, but I'll look for it
|
Mark M. |
I really hope it plays the Europe song, but I doubt it :-)
|
Oct 5 | 7:50 PM |
Mark M. |
it's new to Android 8.0, yet another SDK Easter egg
|
Steve S. |
is it in your booik?
|
Steve S. |
*book
|
Mark M. |
no, but I mentioned it in one of my "random musings" blog posts, pointing out the relevant GEICO commercial
|
Steve S. |
i'll check it out!
|
Mark M. |
but, that's apparently not your question
|
Steve S. |
not yet...what i'm wondering is, to avoid memory leaks, do i need to create a static subclass of CountDownTimer, or is it ok to create an anonymous class?
|
Mark M. |
beats me
|
Mark M. |
I don't think I have ever used it
|
Mark M. |
the JavaDocs shows an anonymous class
|
Mark M. |
so, that's a positive sign that an anonymous class is OK
|
Steve S. |
ok. i did look at the source code, and internally it uses a Handler class with postDelated(), which isn't static
|
Mark M. |
hmmm
|
Mark M. |
I tend to use other things (postDelayed() for UI stuff, SecheduledExecutorService for background stuff)
|
Mark M. |
*presumably* their use of Handler is OK, as probably somebody would have pointed out a leak already
|
Steve S. |
ok
|
Mark M. |
and I'm not seeing any issues about leaks, other than this one: https://issuetracker.google.com/issues/37094374
|
Oct 5 | 7:55 PM |
Mark M. |
and that seems to be tied to something else
|
Mark M. |
so, you're probably OK with an anonymous class, but I can't be sure
|
Steve S. |
should i not be ok with that?
|
Mark M. |
:: shrug ::
|
Steve S. |
ok
|
Mark M. |
like I said, I don't use it, but that's more that I like the alternatives that I cited better
|
Steve S. |
ok
|
Mark M. |
but if there are no issues, and the JavaDocs show an anonymous class, it is likely that an anonymous class is OK
|
Mark M. |
"likely" is not "guaranteed"
|
Steve S. |
sure
|
Steve S. |
i suppose i could test it myself to see if there are any problems
|
Steve S. |
no more questions today. i really appreciate all your help! i'll take another stab at the BluetoothService
|
Steve S. |
have a great rest of the day!
|
Mark M. |
you too!
|
Steve S. | has left the room |
Oct 5 | 8:30 PM |
Mark M. | turned off guest access |