Jan 5 | 7:20 PM |
Mark M. | has entered the room |
Jan 5 | 7:25 PM |
Mark M. | turned on guest access |
Rory M. | has entered the room |
Mark M. |
hello, Rory!
|
Mark M. |
how can I help you today?
|
Rory M. |
Hey Mark
|
Rory M. |
Ok - so have much experience using the ActivityRecognition Api?
|
Mark M. |
zero
|
Mark M. |
sorry
|
Rory M. |
:/
|
Rory M. |
Well - maybe i can abstract what i'd like to learn a bit
|
Mark M. |
it's worth a shot
|
Jan 5 | 7:30 PM |
Rory M. |
Actually ...Recognition is quite similar to how Google Play location services works
|
Rory M. |
Basically i wish to trigger the api on device boot up
|
Rory M. |
using an intent service
|
Rory M. |
And a broadcast receiver
|
Rory M. |
This triggers the service...
|
Mark M. |
why an IntentService?
|
Mark M. |
those are not a great choice for a service that needs to stick around a while
|
Rory M. |
Because I don't need to handing threads
|
Mark M. |
yeah, but once onHandleIntent() returns, the service is destroyed
|
Rory M. |
And also - it fires up and then finishes once it serves it's purpose - so less battery drain
|
Rory M. |
yeah
|
Mark M. |
:: shrug ::
|
Mark M. |
OK, let's assume that the work you need to do is beyond just the BroadcastReceiver, and so you're using an IntentService with it
|
Rory M. |
but inside onHandleIntent - i make a call to onConnected() of the recognition api
|
Rory M. |
ok...i'm listening
|
Mark M. |
well, you're starting to touch on the problem
|
Mark M. |
if the activity recognition API is akin to the fused location provider, everything is asynchronous
|
Rory M. |
yeah
|
Mark M. |
you call some method on the API and provide some listener (or, in some cases, a PendingIntent) to get a response
|
Mark M. |
that's where IntentService falls flat
|
Rory M. |
yeah pending intent
|
Rory M. |
So you would suggest using vanilla Service?
|
Mark M. |
yes, because you need to be firmly in control over when the service gets destroyed
|
Jan 5 | 7:35 PM |
Rory M. |
Ok - that is good advice
|
Mark M. |
you may not need your own thread, at least for the initial bootstrapping of the activity recognition service
|
Rory M. |
But since a service runs on min thread ...
|
Rory M. |
main
|
Mark M. |
I assume, from an earlier comment, that in the end, you want to tell the activity recognition service to invoke a PendingIntent when some criteria are met?
|
Rory M. |
It may affect performance of device
|
Rory M. |
choppy ui
|
Rory M. |
no..?
|
Rory M. |
if it's continuous
|
Mark M. |
well, that depends entirely upon what you are doing on that thread
|
Mark M. |
I am not sure what "continuous" means here
|
Rory M. |
Sorry ...very long running
|
Rory M. |
Potentially for the lifetime of the battery charge for instance
|
Mark M. |
again, it depends on what work you are doing
|
Rory M. |
yeah
|
Mark M. |
and since you indicated that you want the service to go away, call stopSelf() when you're done with it
|
Rory M. |
Yeah
|
Mark M. |
let's roll back to a question I asked earlier: I assume, from an earlier comment, that in the end, you want to tell the activity recognition service to invoke a PendingIntent when some criteria are met?
|
Rory M. |
Yeah
|
Rory M. |
But the problem is
|
Rory M. |
I need an application context for the GoogleApiClient.builder
|
Rory M. |
before the service is kicked off
|
Rory M. |
ie...in the BCR
|
Mark M. |
BCR?
|
Rory M. |
But getApplicationContext is not available at this point
|
Rory M. |
Sorry broadcastReceiver
|
Mark M. |
if BCR == BroadcastReceiver, call getApplicationContext() on the Context passed into onReceive
|
Mark M. |
or, move your GoogleApiClient work into the service (since I thought that the point of the service was to be making those calls)
|
Jan 5 | 7:40 PM |
Rory M. |
Yeah I had it in the service - but the googleClient instance wasn't connecting
|
Mark M. |
usually, with a manifest-registered receiver, if we need to involve a service to get the work done, you shove *all* the work into the service
|
Mark M. |
I doubt that will somehow be improved by doing part of the work in the receiver
|
Mark M. |
if anything, I would expect it to make things worse
|
Rory M. |
yeah maybe... this is where I am having issues
|
Rory M. |
View paste
(6 more lines)
|
Mark M. |
but, again, if you were using an IntentService, I can see where the API might have issues, since that IntentService would have been destroyed before any callback was made
|
Rory M. |
Yes! That is what is happening
|
Mark M. |
where is mApiClient coming from?
|
Rory M. |
View paste
|
Mark M. |
and where are you calling that?
|
Rory M. |
View paste
|
Mark M. |
right
|
Mark M. |
welcome to the world of race conditions
|
Rory M. |
haha
|
Rory M. |
yeah
|
Mark M. |
you cannot assume that mApiClient is connected by the time initRecognitionClient() returns
|
Mark M. |
in fact, it almost assuredly is not connected
|
Rory M. |
How can you tell?
|
Mark M. |
you also cannot assume that mApiClient is connected by the time onHandleIntent() gets called
|
Mark M. |
because it's asynchronous, and involves IPC to a Play Services process, and that's going to take a few milliseconds, minimum
|
Rory M. |
That's why I assumed the apiClient should be connected in the BCR
|
Mark M. |
no
|
Jan 5 | 7:45 PM |
Mark M. |
this goes back to my "don't use an IntentService" suggestion
|
Rory M. |
ok...
|
Mark M. |
kicking off the registration in onStartCommand() is fine
|
Mark M. |
so, calling initRecognitionClient() there is fine
|
Rory M. |
ok cool
|
Mark M. |
then, do not do anything else until you get your onConnected() callback
|
Mark M. |
there, execute your calls to get stuff from that API
|
Rory M. |
So...
|
Mark M. |
now, if that work will take some synchronous time, it may be that you fork your own thread to do that work
|
Mark M. |
but the key is that you need to control the timing of when that thread begins (after onConnected() is called)
|
Mark M. |
whereas with IntentService, you are not in control of the timing
|
Mark M. |
when the work is complete, call stopSelf() (and exit the thread, if you forked one)
|
Rory M. |
Ok... Just to step back a second...I should move the logic from within in onHandleIntent() to the onConnected()?
|
Mark M. |
yes
|
Rory M. |
ok cool
|
Rory M. |
Once that is done...
|
Mark M. |
(and I don't know exactly where startActivityRecognition() comes into play)
|
Rory M. |
View paste
|
Mark M. |
that too needs to move into onConnected(), or be called from onConnected()
|
Mark M. |
again, you cannot use mApiClient until onConnected() is called
|
Mark M. |
as before then, it is not yet connected
|
Rory M. |
Ok
|
Jan 5 | 7:50 PM |
Rory M. |
So...in terms of controlling the timing
|
Rory M. |
I don't have much experience of threading...
|
Rory M. |
From a high level ...would you be able to give me some pseudo code to assist wit the timing
|
Mark M. |
first, don't worry about threading just yet
|
Mark M. |
get your functionality working first
|
Mark M. |
use logging to get a handle on how much time is being spent in your onConnected() method, and in any other callbacks that you wind up using as part of this process
|
Rory M. |
ok..
|
Mark M. |
if you are spending sub-millisecond amounts of time at a shot, you're probably find without a background thread
|
Mark M. |
if you determine that you are spending more time than that, *then* worry about threading
|
Mark M. |
as how the threading will work depends a *lot* on your code in onConnected()
|
Jan 5 | 7:55 PM |
Mark M. |
for example, suppose that you need to call some method on the activity recognition API that requires another callback, akin to onConnected(), and you have to wait on doing the rest of your work until that callback gets called
|
Mark M. |
it could be that some of this work needs to be on the background thread but other bits do not
|
Rory M. |
Like onConnectionSuspended or onConnectionFailed?
|
Rory M. |
()
|
Mark M. |
probably not those, as those are peers of onConnected()
|
Mark M. |
but, for example, with the fused location provider, the non-PendingIntent version of the gimme-a-location methods take a listener
|
Mark M. |
but onConnectionSuspended()/onConnectionFailed() will tie into your threading, insofar as you may need to cancel that thread or somethign
|
Mark M. |
er, something
|
Mark M. |
which, again, is why you should focus on getting the functionality right first, then step back and ponder the threading issue
|
Mark M. |
particularly since, as you mentioned, you are not that experienced with threading
|
Rory M. |
Yeah
|
Mark M. |
it may be that the answer winds up being that you get the functionality working, then you put this project on the side for a bit while you spend focus time on Java /Android threading approaches, then come back to the project and apply what you learned
|
Rory M. |
yeah ...most likely Mark
|
the_new_mr | has entered the room |
the_new_mr |
hi Mark
|
Mark M. |
Rory: let me take a question from the_new_mr, and I'll come back to you in a bit
|
Mark M. |
the_new_mr: hi! do you have a question?
|
the_new_mr |
yes.
|
Rory M. |
But if i have the api connecting .. then updates coming from the api service - and then being posted to UI or system notifications - why function would threading serve in this use case..?
|
Jan 5 | 8:00 PM |
the_new_mr |
first, thanks for the book :)
|
Jan 5 | 8:00 PM |
the_new_mr |
and this service :)
|
Rory M. |
Ok...no probs
|
Rory M. |
Yeah... tremendous work Mark !
|
Mark M. |
thanks for the kind words!
|
the_new_mr |
you are welcome and most deserving
|
the_new_mr |
so...
|
the_new_mr |
we have a null pointer exception occurring
|
the_new_mr |
it's an awkward one
|
the_new_mr |
basically related to context
|
the_new_mr |
it's an internal android exception
|
Mark M. |
can you post the stack trace?
|
the_new_mr |
which seems to be occurring because the context is null
|
the_new_mr |
however, the context we're passing is the application context
|
the_new_mr |
sure, one min
|
the_new_mr |
thought i had it ready but i didn't. one minute please
|
Jan 5 | 8:05 PM |
Mark M. |
(Rory: with respect to your last question, I do not know the business logic of your app, or the activity recognition API, so I cannot provide much specific advice on how those will interact with threads)
|
Rory M. |
Yeah ok... you've been fantastic help in any case Mark... many many thanks
|
Rory M. |
These sessions are archived yeah?
|
Mark M. | |
Mark M. |
this chat will show up there shortly after the chat ends
|
Rory M. |
Great - thanks Mark!
|
Mark M. |
the_new_mr: any luck reproducing the crash?
|
the_new_mr |
nearly there...
|
Jan 5 | 8:10 PM |
the_new_mr |
View paste
(6 more lines)
|
Rory M. |
Mark - what is your rate for micro-consulting?
|
Mark M. |
(Rory: $45/15 minutes)
|
Rory M. |
Cool - thanks
|
Mark M. |
the_new_mr: and you're *sure* that the Context passed into getFromPreference() is not null?
|
the_new_mr |
i guess it is
|
the_new_mr |
but we don't know how
|
the_new_mr |
this crash is from prod
|
the_new_mr |
the context we pass here is application context
|
the_new_mr |
so we dont know why its null
|
Mark M. |
where and when are you calling getApplicationContext() to obtain that Context?
|
Mark M. |
for example, are you storing this in a field somewhere that might not be initialized after a configuration change?
|
the_new_mr |
were calling it in the fragent
|
the_new_mr |
sorry, fragment
|
the_new_mr |
getContext().getApplicationContext()
|
the_new_mr |
like that
|
Mark M. |
when? are you passing it into execute() or the AsyncTask's constructor or something?
|
Jan 5 | 8:15 PM |
the_new_mr |
we're calling getContext().getApplicationContext() during the execution of doInBackground()
|
Mark M. |
don't do that
|
the_new_mr |
and passing it to calcAccountBalance
|
the_new_mr |
i just think now
|
the_new_mr |
this context is not correct?
|
Mark M. |
do not do anything in doInBackground() that directly touches the activity
|
the_new_mr |
ok. why?
|
Mark M. |
call getApplicationContext() and pass it into the AsyncTask, before executing it (e.g., constructor argument)
|
Mark M. |
because of configuration changes
|
the_new_mr |
what's the difference?
|
Mark M. |
while a configuration change is going on, while a retained fragment itself will be stable, what activity is hosting it might not be
|
the_new_mr |
ok
|
the_new_mr |
so it cant get application context?
|
the_new_mr |
ok
|
Mark M. |
and if you wind up calling getApplicationContext() on a destroyed activity, I can easily see getting null back (or just simply crashing)
|
the_new_mr |
ok. that makes sense and is very good advice. we will try that and see. thanks for your help
|
Mark M. |
it is safe for your task to touch the fragment and the fragment's fields, but do not touch the activity
|
Mark M. |
you are very welcome
|
Mark M. |
Rory: back to you! do you have another question?
|
the_new_mr |
ok. is this mentioned in the android docs somehwere?
|
the_new_mr |
seems to not be documented
|
the_new_mr |
or maybe we missed it
|
Mark M. |
(probably not, but I'm fairly sure that I cover it in my chapter on threading)
|
Rory M. |
Not right now Mark - It's late where I am (GMT) - so will look tomorrow - I think you have def put me on the right track though
|
Rory M. |
When is you next session Mark?
|
the_new_mr |
ok. guess we missed that. thanks. we'll try this. thanks again
|
Mark M. |
Tuesday, in this same time slot (7:30pm US Eastern)
|
Jan 5 | 8:20 PM |
the_new_mr |
i think you have one early at another time in the calendar. right Mark?
|
Mark M. |
yes, I rotate through three times: 9am, 4pm, and 7:30pm US Eastern
|
Mark M. |
hoping that most people will have at least one of those be convenient
|
the_new_mr |
that's good
|
Rory M. |
Thanks again Mark - v much appreciate. G'night from Ireland!
|
Mark M. |
Erin go Bragh, and all that :-)
|
the_new_mr |
good evening all
|
Rory M. |
haha... excellent
|
Mark M. |
well, I *am* a Murphy, though it's been a few years since my last trip to Dublin
|
Rory M. |
Yeah the surname did occur to me
|
Rory M. |
Native or descent?
|
Mark M. |
I'm American, and I forget exactly how far back it was when my ancestors made the trip over
|
Rory M. |
Must dig out your threading chapter tomorrow ... g'night all
|
Mark M. |
have a pleasant evening!
|
the_new_mr |
good night
|
Jan 5 | 8:25 PM |
the_new_mr |
quick question Mark
|
Jan 5 | 8:25 PM |
Mark M. |
go ahead
|
the_new_mr |
so... the problem is that in the very short time from when the fragment is there to when it runs the async task, the fragment may be detached from the activity
|
the_new_mr |
correct?
|
the_new_mr |
or the activity is no longer there?
|
Mark M. |
I'd be more worried about the old activity being destroyed, yet that being the one that you are getting in your fragment when you call getContext()
|
the_new_mr |
ok
|
Mark M. |
destroyed activities fail in lots of ways
|
the_new_mr |
but why is calling getApplicationContext on that getContext a problem. because isn't it the same application context?
|
Mark M. |
in this case, the destroyed activity might have nulled out its field that points to the application context
|
the_new_mr |
or because activity is gone so it can't get application context anymore?
|
Mark M. |
which is why you are getting null back
|
the_new_mr |
ok. yes
|
Mark M. |
right
|
the_new_mr |
ok. that is fine
|
the_new_mr |
thank you Mark and good evening
|
Jan 5 | 8:30 PM |
Mark M. |
that is a wrap for today's chat
|
Mark M. |
again, the transcript will appear at https://commonsware.com/office-hours/ shortly
|
Mark M. |
and the next chat is Tuesday, at 7:30pm US Eastern
|
Mark M. |
have a pleasant evening, all!
|
Rory M. | has left the room |
the_new_mr | has left the room |
Mark M. | turned off guest access |