Office Hours Transcript: 2021-05-25
Tad joined
Hey Mark
hello, Tad!
how can I help you today?
Hey I’ve got an odd situation with Volley going on. Is that something you can brainstorm with me on?
I have not used Volley in several years
Hmmm…ok. Do you use a different library for what it does?
OkHttp and Retrofit, mostly
Volley is not well-maintained or well-supported, IMHO
ok - well the question also involves making sure I understand what is going on under the covers prior to me making a call to it.
yeah, if you have questions that are not tied into the Volley API, I can probably help
I’m not sure it is a Volley thing, although I’ve walked through enough times in the debugger now where I think it is some sort of race condition in there.
What I use it for is to make HTTPS calls to an API that is the backend of my website.
Validating user credentials, etc. etc.
When the app starts up, the entire flow works flawlessly.
But, for example, on some Samsung phones I notice if the user pops out to the phone settings, then turns off a permission that my app has been previously granted by the user (access to the microphone for example), what Samsung does is actually restart the app.
that is not unique to Samsung
When it does this, the Application class gets onCreate called, then whatever activity was previously active gets its onCreate called and so forth.
if the user revokes a dangerous permission, it should kill your process on all Android devices with runtime permissions (6.0 and higher)
Standard stuff I guess
right
OK so when I go through the same initialization sequence on that startup, Volley freezes.
I’ve traced it far enough where I know it is making the web call, I know it is getting data back from the web server.
Then nothing.
No error, no nothing.
Just goes into a black hole.
If I step through it in the debugger, this does not happen.
Hence me thinking it is a race condition of some sort.
I cannot explain how your revoked-permission scenario is different from any other cold start
Me either
by "Then nothing", do you mean that Volley does not call your listener with the results?
Except it doesn’t go through my "SplashActivity", which then would launch a main activity vs. going directly to "main" (or whatever other activity had previously been active)
Correct Volley does not
It also doesn’t call the error listener
It just…stops
And the app hangs, as if it is a deadlock on the UI thread
which it isn’t
with respect to SplashActivity
, that’s probably because your task is still outstanding, and so Android wants to return to the last activity the user had been in
Yes I understand why it isn’t called on the restart
I was just mentioning that because it isn’t exactly the same code path
if you revoke the permission, then swipe the task off the overview screen, do you get the same stuck-Volley behavior?
Yes - my way to cause this behavior is to have the main screen of my app up.
Then hit the "multiple windows" button on the device (this is Samsung Android 8)
Swipe over to Settings
Change the permission
Then pop back to the app
It actually starts working until it hits that request of Volley to access my website
OK, no offense, though, but that’s not what I suggested trying
Doesn’t matter which web API I call - it’s all the same behavior
after the "Change the permission" step, swipe your app’s task off the overview screen, then launch the app normally from the launcher
Right - I thought you were asking how I get it to fail
now, I’m trying to see how important SplashActivity
really is
ok one sec
with the task killed, your launcher icon should run you through your splash screen
I think at the moment that I change the permission, it restarts the app before I can do anything
the overview screen (where you are swiping between apps) should still have your app, and you should still be able to swipe and remove it
that removes your task, and it will cause Android to run your app normally from the launcher icon, rather than attempt to go directly to your main activity
What you are calling the "overview screen" is the view I get when I tap the icon on this device which shows me recently run apps?
(It’s an older Samsung S7)
yes
"overview screen" is Google’s term for it
ok one second - I need to rebuild this app (another annoying issue I’m having with AS 4.2.1 is that it thinks it can’t delete the build directory for some reason, all of a sudden)…
Have you run into that with anyone else?
I assume it must be some sort of environment problem on my system, but I’ll be darned if I can tell.
the build directory problem? no
Started happening when I upgraded from 4.1.3
Yeah I’m having to do full rebuilds now a bunch.
OK just so I am clear
I start the app, get it to the main screen where it normally gets ok.
Then I use the overview screen to go to settings, and change the permission?
yes
(or get to Settings however you want)
Then I go back to the overview screen, and swipe left on my app?
um, assuming left will swipe it off the screen, then yes
(the overview screen varies a lot)
Swipe off the screen - ok. I guess I don’t normally do that
ok going to settings
Swiped it off
now launch the app normally, and see if it gets hung up in the same spot
It did not
Started normally
OK, that’s kinda what I expected
my guess is that your problem is more tied to the process termination with the task still outstanding, rather than it being tied strictly to the permission revocation
you could test that via adb shell am kill ...
, where ...
is your application ID
onDestroy() gets called in these cases?
not necessarily
Hmmmm. ok
but your process goes "poof", so there will be nothing in memory, as you will have no more memory
I do have some "shut down" code where I stop the Volley Queue, clear out any pending requests, etc.
yeah, that shouldn’t be the problem here, since the process is gone
ok
anyway, so things work if your process is started normally and runs through your splash screen, but things break if your process is started and bypasses the splash screen
Right
The weird thing is that I’ve verified that in both cases, the https call gets made and valid data comes back.
It is what is happening with that return value after that point that is different
so, either the splash screen path is doing something important, or showing the splash screen is changing the conditions enough to avoid your presumed race condition, akin to how running in the debugger is changing the conditions
Right.
For all other activities, I have a base class that extends AppCompatActivity, and in that class in onCreate and onResume I’m verifying that the Volley queue is initialized prior to anything else getting done.
For all other activities other than splashactivity I mean
SplashActivity
is not triggering a Volley request itself, I take it?
So I can see that on re-entry to the app, those methods are getting called, and Volley is getting initialized.
And of course, like I said it actually makes the call and receives data.
So it isn’t the queue or queue mechanincs
Something else
Back to what you are doing - I haven’t used okHttp directly. Does it offer the high level functionality of Volley or would I have to roll my own?
what specifically are you referring to with respect to Volley?
here is an example:
public static void performNonBlockingHttpGet(String url,
HashMap<String, Object> params,
final Response.Listener<String> listener,
final Response.ErrorListener errorListener,
int timeoutInSeconds) throws Exception {
String newUrl;
Timber.d("Lifecycle: performNonBlockingHttpGet with timeout: %d url: %s, Volley queue shows %s entries.", timeoutInSeconds, url,getRequestQueueCount());
Response.Listener<String> internalListener = response -> {
decrementRequestQueueCount();
try {
listener.onResponse(response);
Timber.d("Lifecycle: performNonBlockingHttpGet for %s ends normally.", url);
} catch (Exception e) {
Timber.e("Lifecycle: performNnBlockingHttpGet for %s ends in exception: %s", url, e.getMessage());
throw e;
}
};
Response.ErrorListener internalErrorListener = error -> {
decrementRequestQueueCount();
try {
errorListener.onErrorResponse(error);
Timber.d("Lifecycle: performNonBlockingHttpGet for %s ends with an error:%s.",url, error.getMessage());
} catch (Exception e) {
Timber.e("Lifecycle: performNonBlockingHttpGet for %s ends in exception: %s", url, e.getMessage());
throw e;
}
};
if (params != null) {
newUrl = createGetRequestUrl(url, params);
} else {
newUrl = createGetRequestUrl(url, null);
}
StringRequest request =
new StringRequest(Request.Method.GET,
newUrl,
internalListener,
internalErrorListener) {
@Override
public Priority getPriority() {
return Priority.NORMAL;
}
};
request.setRetryPolicy(new DefaultRetryPolicy(timeoutInSeconds * 1000,
1,
DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
addToRequestQueue(request, true);
I set two listeners, and make the Url call
Processing the result in my listeners
OkHttp uses one listener rather than two, and its setup work is simpler
And its supported :)
yes, they publish updates every month or so, as opposed to once in a blue moon for Volley
and, there are a lot more people out there who know how to help with OkHttp questions
How do I get around the fact that other libraries I am using (PubNub being one) also has their own dependency on OkHttp which may not be the version I would be using?
i.e. it is within their shipping library
Gradle will choose the newer of the requested versions
that occasionally causes problems, if the API changes
Yeah, I was using okHttp originally for some other reason, and when I upgraded my PubNub stopped working correctly and those guys said "don’t do that"
So I dropped it
FWIW, I’m using OkHttp with PubNub successfully on my main client’s project
Which version?
of each?
I’m using PubNub 5.0.0
we’re back on 4.35.0 of PubNub and 4.9.0 of OkHttp
That’s a pretty recent version of OkHttp right?
yes
Interesting
I think the OkHttp that they use internally is fairly old
that’s certainly possible
Have you been pretty happy with PubNub? I’ve noticed a fair number of intermittent disconnects.
it’s been reasonable, but I wouldn’t run a nuclear power plant on it
It generally will always reconnect, but I’ve had to really tighten down my recovery code.
Probably a good thing anyway
Is your client using it world-wide or just in one region?
mostly US, with trace amounts elsewhere
Are you using the heartbeat mechanism?
I haven’t looked at the PubNub bits in quite some time, so I forget, sorry
I started, then stopped using it. For me decided it was ok to recover on re-connect rather than proactively detect disconnect.
Plus it costs extra $
I’ll look into OkHttp again, thanks.
Anything big on the horizon that you think is going to be a game-changer?
Android-wise?
not sure how far out of a horizon you’re considering
Jetpack Compose will be a game-changer, insofar as Google is going to push the heck out of it
Yep, that’s a good call.
that is supposed to ship 1.0.0 stable in July
Are they still supporting Flutter in a big way?
that’s actually about the only game-changer I can think of off the top of my head
Flutter? oh, yeah
lots of material on that at the recent Google I|O conference
So continuing to gain steam in your opinion…
I’m uncertain about the "gain" part
they have a fair bit of steam
I’m uncertain if it has hit a ceiling of adoption or not
That’s the real question
o me
to me
I’ve read a few articles in the last 6 months or so of the "So I decided to give Flutter a try and compare it to Xamarin" (or <name your other favorite>)
it certainly has its adherents
I would not be surprised if 5% of Android app development is done in Flutter today
I would be surprised if the figure were substantially higher than that
OK - thanks again for your insights, gonna jet.
have a pleasant evening!