Office Hours — Today, January 5

Yesterday, January 4

Jan 5
7:20 PM
Mark M.
has entered the room
7:25 PM
Mark M.
turned on guest access
Rory M.
has entered the room
Mark M.
hello, Rory!
how can I help you today?
Rory M.
Hey Mark
Ok - so have much experience using the ActivityRecognition Api?
Mark M.
zero
sorry
Rory M.
:/
Well - maybe i can abstract what i'd like to learn a bit
Mark M.
it's worth a shot
7:30 PM
Rory M.
Actually ...Recognition is quite similar to how Google Play location services works
Basically i wish to trigger the api on device boot up
using an intent service
And a broadcast receiver
This triggers the service...
Mark M.
why an IntentService?
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
yeah
Mark M.
:: shrug ::
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
ok...i'm listening
Mark M.
well, you're starting to touch on the problem
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
that's where IntentService falls flat
Rory M.
yeah pending intent
So you would suggest using vanilla Service?
Mark M.
yes, because you need to be firmly in control over when the service gets destroyed
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 ...
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
choppy ui
no..?
if it's continuous
Mark M.
well, that depends entirely upon what you are doing on that thread
I am not sure what "continuous" means here
Rory M.
Sorry ...very long running
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
But the problem is
I need an application context for the GoogleApiClient.builder
before the service is kicked off
ie...in the BCR
Mark M.
BCR?
Rory M.
But getApplicationContext is not available at this point
Sorry broadcastReceiver
Mark M.
if BCR == BroadcastReceiver, call getApplicationContext() on the Context passed into onReceive
or, move your GoogleApiClient work into the service (since I thought that the point of the service was to be making those calls)
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
I doubt that will somehow be improved by doing part of the work in the receiver
if anything, I would expect it to make things worse
Rory M.
yeah maybe... this is where I am having issues
View paste (6 more lines)
@Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "onHandleIntent: called");
        //validate that the received Intent contains activity recognition data
        if (mApiClient.isConnected()) {
            Log.d(TAG, "onHandleIntent: mApiClient is connected");

            if (ActivityRecognitionResult.hasResult(intent)) {
                Log.d(TAG, "onHandleIntent: ActivityRecognitionResult.hasResult");

                // extract the ActivityRecognitionResult from the Intent to see what activities your user might be performing
                ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(intent);
                Log.d(TAG, "onHandleIntent: result = " + result.toString());

                // retrieve a list of the possible activities by calling getProbableActivities() on the ActivityRecognitionResult object.
...
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
private void initRecognitionClient() {
        mApiClient = new GoogleApiClient.Builder(this)
                .addApi(ActivityRecognition.API)
                .addConnectionCallbacks((GoogleApiClient.ConnectionCallbacks) getApplicationContext())
                .addOnConnectionFailedListener((GoogleApiClient.OnConnectionFailedListener) getApplicationContext())
                .build();

        mApiClient.connect();
        Log.d(TAG, "initRecognitionClient: called - mApiClient.builder and connect() called");
    }
Mark M.
and where are you calling that?
Rory M.
View paste
  @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        initRecognitionClient();
        Log.d(TAG, "onStartCommand: called");
        startActivityRecognition();
        Log.d(TAG, "onStartCommand: startActivityRecognition() called");
        return START_STICKY;
    }
Mark M.
right
welcome to the world of race conditions
Rory M.
haha
yeah
Mark M.
you cannot assume that mApiClient is connected by the time initRecognitionClient() returns
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
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
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
so, calling initRecognitionClient() there is fine
Rory M.
ok cool
Mark M.
then, do not do anything else until you get your onConnected() callback
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
but the key is that you need to control the timing of when that thread begins (after onConnected() is called)
whereas with IntentService, you are not in control of the timing
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
Once that is done...
Mark M.
(and I don't know exactly where startActivityRecognition() comes into play)
Rory M.
View paste
public  void startActivityRecognition(){
        Intent intent = new Intent(getApplicationContext(), ActivityRecognitionService.class);
        pendingIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        ActivityRecognition.ActivityRecognitionApi.requestActivityUpdates(mApiClient, 2000, pendingIntent);
    }
Mark M.
that too needs to move into onConnected(), or be called from onConnected()
again, you cannot use mApiClient until onConnected() is called
as before then, it is not yet connected
Rory M.
Ok
7:50 PM
Rory M.
So...in terms of controlling the timing
I don't have much experience of threading...
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
get your functionality working first
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
if you determine that you are spending more time than that, *then* worry about threading
as how the threading will work depends a *lot* on your code in onConnected()
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
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?
()
Mark M.
probably not those, as those are peers of onConnected()
but, for example, with the fused location provider, the non-PendingIntent version of the gimme-a-location methods take a listener
but onConnectionSuspended()/onConnectionFailed() will tie into your threading, insofar as you may need to cancel that thread or somethign
er, something
which, again, is why you should focus on getting the functionality right first, then step back and ponder the threading issue
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
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..?
8:00 PM
the_new_mr
first, thanks for the book :)
8:00 PM
the_new_mr
and this service :)
Rory M.
Ok...no probs
Yeah... tremendous work Mark !
Mark M.
thanks for the kind words!
the_new_mr
you are welcome and most deserving
so...
we have a null pointer exception occurring
it's an awkward one
basically related to context
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
however, the context we're passing is the application context
sure, one min
thought i had it ready but i didn't. one minute please
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
These sessions are archived yeah?
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...
8:10 PM
the_new_mr
View paste (6 more lines)
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:300)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
at android.preference.PreferenceManager.getDefaultSharedPreferencesName(PreferenceManager.java:374)
at android.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:369)
at com.example.utility.PreferenceUtility.java.lang.Object getFromPreference(android.content.Context,java.lang.String,java.lang.Object)(PreferenceUtility.java:76)
at com.example.ui.fragments.manager.HomeDataHelper.void getBalanceFromPrefer
...
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
but we don't know how
this crash is from prod
the context we pass here is application context
so we dont know why its null
Mark M.
where and when are you calling getApplicationContext() to obtain that Context?
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
sorry, fragment
getContext().getApplicationContext()
like that
Mark M.
when? are you passing it into execute() or the AsyncTask's constructor or something?
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
i just think now
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)
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
so it cant get application context?
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
you are very welcome
Rory: back to you! do you have another question?
the_new_mr
ok. is this mentioned in the android docs somehwere?
seems to not be documented
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
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)
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
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
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
8:25 PM
the_new_mr
quick question Mark
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
correct?
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
thank you Mark and good evening
8:30 PM
Mark M.
that is a wrap for today's chat
again, the transcript will appear at https://commonsware.com/office-hours/ shortly
and the next chat is Tuesday, at 7:30pm US Eastern
have a pleasant evening, all!
Rory M.
has left the room
the_new_mr
has left the room
Mark M.
turned off guest access

Yesterday, January 4

 

Office Hours

People in this transcript

  • Mark Murphy
  • Rory McGuigan
  • the_new_mr