Feb 26 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Feb 26 | 7:25 PM |
Guido | has entered the room |
Mark M. |
hello, Guido
|
Mark M. |
how can I help you today?
|
Guido |
Hi Mark
|
Susheel | has entered the room |
Susheel |
Hi Mark
|
Guido |
I've got a question about fragments going through configuration change.... I prepared it earlier today so I'm going to paste it here
|
Mark M. |
hello, Susheel!
|
Susheel |
Hi Guido
|
Guido |
Hi Susheel
|
Susheel |
I'll go after Guido
|
Guido |
View paste
|
Mark M. |
"setRetainInstance seems not to keep the selected items/action mode when in CHOICE_MODE_MULTIPLE_MODAL" -- your views get rebuilt; setRetainInstance() has nothing to do with this on its own
|
Guido |
oh I see, I didn't get this, so the class is retained but the view gets rebuilt?
|
Mark M. |
correct
|
Mark M. |
onCreateView() gets called again
|
Mark M. |
as you need fresh widgets to go with the fresh activity
|
Guido |
yes, that's right
|
Feb 26 | 7:30 PM |
Mark M. |
all setRetainInstance() does for you is say that non-widget data members could be held onto across the configuration change
|
Mark M. |
for widget state like this, option #2 (onSaveInstanceState()) is the best answer, as that not only survives configuration change, but handles the recent-tasks list and such as well
|
Mark M. |
for your model data, for something of this complexity, I'd lean towards #3 or a singleton data manager
|
Mark M. |
the latter would be if the same model data is needed by multiple components (multiple activities, or activities plus services, etc.0
|
Mark M. |
er, etc.)
|
Svetlin S. | has entered the room |
Guido |
OK and would I need to persist also the adapters or the cursors only?
|
Svetlin S. |
Hello everyone.
|
Mark M. |
hello, Svetlin -- I will be with you after Susheel!
|
Svetlin S. |
Thanks.
|
Mark M. |
Guido: I use "persist" specifically to refer to file-based storage
|
Mark M. |
in which case, your data is already persisted
|
Guido |
yep sorry... that's right
|
Mark M. |
if you mean "retained", in terms of the model fragment, then I'd retain the cursors (the model data)
|
Guido |
yep, that's what I meant :-)
|
Guido |
thanks!
|
Mark M. |
Susheel: your turn! do you have a question?
|
Susheel |
View paste
|
Feb 26 | 7:35 PM |
Mark M. |
um, well
|
Mark M. |
that's probably something for you to take up with a manufacturer or something
|
Mark M. |
first, flash drives like that aren't part of the Android SDK, at least pre-4.4
|
Susheel |
right
|
Susheel |
but the same thing happens to my externally mounted sdcard
|
Mark M. |
which, again, is not part of the Android SDK
|
Susheel |
oh really
|
Susheel |
ok
|
Mark M. |
removable media, pre-4.4, was outside the scope of the Android SDK
|
Susheel |
gotcha
|
Mark M. |
the sole exception is a device that uses removable media for external storage
|
Mark M. |
and there haven't been many of those since Android 3.0
|
Mark M. |
so, by and large, the rules of the game for removable media are up to the manufacturer
|
Susheel |
Perfect, thanks :)
|
Susheel |
I have another question but I will wait for my turn.
|
Mark M. |
sorry I didn't have a better answer for you
|
Susheel |
That's cool, thanks
|
Mark M. |
Svetlin: your turn! do you have a question?
|
Svetlin S. |
Yes.
|
Svetlin S. |
I'm working on a handset which will run only one application.
|
Svetlin S. |
The handset connects to external devices using services.
|
Svetlin S. |
I implemented a Watchdog service started on boot to "oversee" them.
|
Svetlin S. |
Using AlarmManager.
|
Svetlin S. |
Now I need to consistently check the status of those service (list, (re)start) etc.
|
Svetlin S. |
Inside the Watchdog, periodically.
|
Feb 26 | 7:40 PM |
Svetlin S. |
I've come up with the idea of having a Pojo, ServiceManager which will provide the needed callbacks (registration of services, invoking operations on them etc.) to the Watchdog.
|
Svetlin S. |
So far OK?
|
Mark M. |
I'm baffled as to why all of this isn't being done in custom firmware, but, beyond that, I'm with you
|
Svetlin S. |
Let me copy in Pastebin.
|
Svetlin S. |
BTW the plan is eventually the app to run on a normal phone/tablet handset.
|
Svetlin S. | |
Mark M. |
wait
|
Mark M. |
"the app"?
|
Mark M. |
singular?
|
Mark M. |
all of these services are in one app?
|
Svetlin S. |
The device will run just this app.
|
Mark M. |
then I'm really lost
|
Svetlin S. |
Yes, some are local, some remote (in another process). E.g. Bluetooth, RemoteSync etc.
|
Mark M. |
ok
|
Feb 26 | 7:45 PM |
Svetlin S. |
It is a normal approach for a medical device running a custom Android version.
|
Feb 26 | 7:45 PM |
Svetlin S. |
Using ActivityManager in the ServiceManager does not seem to work out well, because the Watchdog has to run and take action even if the device is asleep.
|
Mark M. |
I don't see how ActivityManager helps or harms in this are
|
Mark M. |
er, in this area
|
Mark M. |
if you want to check on the status of services periodically, and you want to do so even if the device is asleep, you'll use a _WAKEUP alarm
|
Svetlin S. |
I can't get my services using it, including inside a ServiceTestCase.
|
Mark M. |
I haven't used ActivityManager much, and I have never used getRunningServices(), so I have no idea what the expected behavior should be
|
Mark M. |
other than I'd be stunned if waking the device out of sleep mode somehow affected that behavior
|
Mark M. |
if you have these so tightly coupled, you might consider using the binding pattern
|
Mark M. |
your "watchdog" simply binds to each of the services and does some AIDL equivalent of a "ping" or status check or whatever
|
Svetlin S. |
There is the problem of the context, so that ActivityManager works called from an Activity, not so much from IntentService.
|
Svetlin S. |
I hear you.
|
Svetlin S. |
That's why I decided to put a static inside the controlled Service so that it reports its state.
|
Mark M. |
in your case, you'd leave off BIND_AUTO_CREATE, so you would know if the service was not already created, so you could use startService() to get it running more durably
|
Feb 26 | 7:50 PM |
Mark M. |
but that is not useful across process boundaries
|
Svetlin S. |
Let me pastebin what I tried.
|
Mark M. |
actually, let me take questions from the others
|
Mark M. |
and I'll be back with you in a bit
|
Mark M. |
as this has gone on a little long
|
Svetlin S. |
Sure!
|
Mark M. |
Guido: your turn! do you have another question?
|
Guido |
yes please
|
Guido |
View paste
(5 more lines)
|
Guido |
I realise it can be due to multiple factors but I wonder if you have an idea where to start from...
|
Guido |
or just knowledge of some "popular" mistakes...
|
Mark M. |
I don't think the back stack is designed to handle situations where you replace what was supposed to removed on a BACK press
|
Mark M. |
in fact, that feels like a UX or code smell
|
Mark M. |
oh, well, wait
|
Mark M. |
never mind
|
Mark M. |
I have a better picture for what you're doing
|
Mark M. |
but I don't think the framework-supplied back stack will work well here
|
Guido |
ok
|
Mark M. |
it's designed for simpler scenarios, like EU4You
|
Mark M. |
there, we only have two fragments, and either both are showing, or just the master
|
Mark M. |
an example of your scenario would be master/detail, with N possible detail fragments
|
Mark M. |
such as a shopping cart, with different fragments for different types of products (books versus movies versus boxes of laundry detergent)
|
Feb 26 | 7:55 PM |
Mark M. |
and there I would expect to have to handle the BACK stuff myself in onBackPressed(), though simply "knowing" whether the detail is showing and how to remove it
|
Guido |
ok, that clarifies my problem then
|
Mark M. |
in terms of the action mode stuff, are you referring to your own action mode, or a system-supplied one (e.g., EditText)?
|
Guido |
in one fragment type is system supplied in a ListView and in the other one is custom becasue I have a RecyclerView
|
Mark M. |
ListView doesn't have an action mode
|
Mark M. |
EditText does, and WebView kinda does
|
Mark M. |
but very few other widgets have one built in
|
Mark M. |
(actually, TextView does if you make it selectable)
|
Mark M. |
for your own action mode, I can see where onBackPressed() would need to know about it and finish() the action mode
|
Guido |
oh I see what you mean
|
Mark M. |
so it'd be: if action mode, then finish() it; else if detail showing, remove() it; else super.onBackPressed()
|
Mark M. |
where the latter will do your finish() for the activity for you, along with anything else that normal BACK processing might need
|
Mark M. |
doing finish() yourself is *probably* OK, but I just chain to super.onBackPressed(), so I know I'm not forgetting anything :-)
|
Guido |
yep, sounds great
|
Mark M. |
now, I may be off on your detail fragment handling, as I don't have a complete picture of what you're doing
|
Feb 26 | 8:00 PM |
Mark M. |
but let me take questions from the others, and I'll be back with you in a bit
|
Mark M. |
Susheel: your turn! do you have another question?
|
Susheel |
Going back to my previous question, when I replug the flash drive I'm able to view the contents of the flashdrive using a filemanager app. Does that mean that I should also be able to write a file to it? Or is that not sufficent info and should I still contact the manufacturer about it?
|
Guido |
alright great
|
Mark M. |
"Does that mean that I should also be able to write a file to it?" -- off the cuff, I would expect so
|
Mark M. |
particularly if this file manager is a third-party one
|
Mark M. |
not a pre-installed one
|
Mark M. |
a third-party file manager should have no more capabilities than does your app
|
Mark M. |
(modulo the device being rooted and that file manager leveraging that where your app does not)
|
Susheel |
Ok. So do you think for some reason when I unplug the device the file path is being broken or something like that? Is that possible?
|
Susheel |
because when I restart it works well
|
Susheel |
until I unplug
|
Mark M. |
aw, heck, on this Ubuntu notebook that I'm using, I sometimes have to reboot as the system freezes when I unmount the external hard drive I use for backups
|
Mark M. |
*anything* can go haywire when dealing with removable storage as far as I am concerned :-)
|
Mark M. |
then again, my last name is Murphy, and so I'm used to problems like this...
|
Susheel |
Oh I see that makes sense...haha
|
Mark M. |
so, while an Android device should be stable and well-behaved when unmounting and re-mounting removable media, that can have bugs just like anything else
|
Susheel |
ok
|
Mark M. |
and probably more than its fair share, given that device manufacturers can kinda do what they want
|
Susheel |
Right
|
Mark M. |
and some device manufacturers can get awfully creative when given that amount of flexibility
|
Susheel |
No kidding
|
Mark M. |
so, if you're not seeing stable results, that's definitely something you'll have to take up with the manufacturer
|
Susheel |
Got it. Thank you Mark!
|
Feb 26 | 8:05 PM |
Mark M. |
(or reseller or whoever handles that support for the hardware in question)
|
Mark M. |
Svetlin: your turn!
|
Svetlin S. |
Thanks, let me continue from where I stopped.
|
Svetlin S. |
I went over the binding pattern, and it looks it will do the job.
|
Svetlin S. |
My problem is, other people with less experience are writing some of the services.
|
Mark M. |
sounds like they need a good book :-D
|
Svetlin S. |
That's why I'm trying to get away with the least possible changes to their code.
|
Svetlin S. |
Please take a look at the static in http://pastebin.com/AFHT47Ab
|
Svetlin S. |
In AuthenticatorService.
|
Svetlin S. |
The idea is to have "state" variables describing the state a service is in.
|
Svetlin S. |
I know you are against "constantly" running services, but in this case it is a matter of life/wellbeing that if a service stops, it is restarted ASAP.
|
Mark M. |
for dedicated hardware, that's fine
|
Mark M. |
it gets to be a bigger problem on off-the-shelf stuff used by ordinary people for other things
|
Mark M. |
but, again, this is only an issue for remote processes
|
Svetlin S. |
How so?
|
Mark M. |
anything inside the same process as the watchdog won't stop unless you stop it or Android terminates that whole process
|
Feb 26 | 8:10 PM |
Svetlin S. |
I thought that's the case only if the service runs in "foreground"?
|
Mark M. |
no
|
Mark M. |
now, there's the case where the user goes into Settings and manually stops an individual service, but I am assuming that's not a worry for dedicated hardware
|
Svetlin S. |
Hm. Anyway, opinion on the static?
|
Mark M. |
you mean the singleton-ish instance of AuthenticatorService?
|
Svetlin S. |
Yes.
|
Mark M. |
IMHO it is not doing anything for you
|
Susheel | has left the room |
Svetlin S. |
I'm aware stopService is async, so onDestroy will not get called immediately, anything else?
|
Mark M. |
but, if you want it, you're not leaking anything
|
Svetlin S. |
Also, the service may mis-behave and get stopped because the hardware it is attached to mis-behaves.
|
Mark M. |
but there your singleton won't help you
|
Mark M. |
say for example you crash with an unhandled exception
|
Mark M. |
assuming that it doesn't take down the whole process, Android might get rid of the individual service
|
Mark M. |
but it's not going to call onDestroy()
|
Mark M. |
because it assumes that your crashed service has lost its proverbial marbles
|
Mark M. |
and it's just going to let go of it
|
Mark M. |
and so your singleton will still point to it
|
Svetlin S. |
Correct. OK then, the way to go is to have an interface basically implementing the binding pattern for all services?
|
Feb 26 | 8:15 PM |
Svetlin S. |
Then the ServiceManager will undertake this actions on behalf of the Watchdog?
|
Mark M. |
well, the first thing is to have robust exception handling in all your code in the service :-)
|
Mark M. |
the nice thing about the binding pattern approach is that your watchdog/ServiceManager neither knows nor cares what is or is not in your process
|
Mark M. |
it just knows that it tries to bind and uses that to get state information
|
Mark M. |
and if the attempt to bind fails, then the service collapsed for some reason, and so we need to startService() it again
|
Mark M. |
that behavior is identical whether the service in question is in your process or in a :remote process
|
Mark M. |
you could even create a ManagedService base class that handles all of the common stuff for the binding, and have the other developers inherit from it instead of Service
|
Svetlin S. |
Yes. Thanks, very useful indeed. I also have a question about Service unit testing.
|
Mark M. |
let me swing back to Guido first
|
Mark M. |
Guido: your turn! do you have another question?
|
Svetlin S. |
I think I'll do exactly that. Any sample code to share ;-)
|
Svetlin S. |
Oops, sorry.
|
Mark M. |
(Svetlin: not really, as I don't do a lot with binding -- yours is one of the few use cases where I find binding to be valuable)
|
Guido |
no Mark, unless you have anything else to add, I think I'm good with your suggestions
|
Mark M. |
OK
|
Mark M. |
Svetlin: back to you
|
Svetlin S. |
Are there any problems with this sort of testing?
|
Feb 26 | 8:20 PM |
Guido |
Thanks Mark, have a good night
|
Svetlin S. |
View paste
(20 more lines)
|
Mark M. |
Guido: see you later!
|
Guido | has left the room |
Mark M. |
Svetlin: well, startService() is asynchronous
|
Mark M. |
actually, perhaps not in ServiceTestCase
|
Svetlin S. |
Really?! I'm very confused then, I thought it calls either onCreate or onStartCommand which are on the UI thread?
|
Mark M. |
yes, but unit testing code is not on the UI thread
|
Mark M. |
that's why you have to do the whole wait-for-idle-sync stuff in UI testing
|
Mark M. |
personally, I haven't used ServiceTestCase
|
Svetlin S. |
ServiceTestCase provides both a mock application and a mock context, correct?
|
Mark M. |
if I understand the docs correctly, yes
|
Mark M. |
I usually test what's inside the service separately, using something like AndroidTestCase if I need a Context for that
|
Svetlin S. |
So from the point of view of ServiceTestCase isn't it that the behavior is the same "as if" it is running on a mock UI thread?
|
Mark M. |
beats me
|
Mark M. |
I can tell you that the activity TestCase classes don't work that way
|
Svetlin S. |
The instrumented or the plain ones?
|
Mark M. |
certainly ActivityInstrumentationTestCase2 has its test methods running on another thread
|
Mark M. |
and, from what I can tell, the equivalents you run using AndroidJUnitRunner for JUnit4 support do the same
|
Feb 26 | 8:25 PM |
Mark M. |
my testing life is dominated by standard JUnit and AndroidTestCase, with sporadic uses of ActivityInstrumentationTestCase
|
Mark M. |
(er, ActivityInstrumentationTestCase2)
|
Mark M. |
I don't mess with the other Android test case base classses
|
Mark M. |
that's not to say they're bad
|
Svetlin S. |
I'm using JUnit3 for now. I wish there was somewhere a complete example of how to use ServiceTestCase?
|
Mark M. |
I just don't use them
|
Mark M. |
there's probably some floating around the CTS somewhere
|
Svetlin S. |
CTS is what (sorry)?
|
Mark M. |
Compatibility Test Suite
|
Svetlin S. |
OK, I'll take a look.
|
Mark M. |
it is what device manufacturers have to pass in order to qualify for the Google proprietary apps
|
Mark M. |
a search on "extends ServiceTestCase" on Google turns up surprisingly few hits, though some may be useful to you
|
Svetlin S. |
Got it. Thank you so much for your time, Mark, appreciated.
|
Mark M. |
based on the hit count, it would appear I'm not the only one who skips ServiceTestCase...
|
Svetlin S. |
Yeah, seems like it.
|
Svetlin S. |
Thanks again. We actually met at the Google I/O a couple of years ago, BTW.
|
Svetlin S. |
Thanks for the Warescription, appreciated.
|
Mark M. |
you are very welcome
|
Mark M. |
though I've skipped the past three I/O's
|
Mark M. |
time flies when you're having fun and/or writing Android code :-)
|
Feb 26 | 8:30 PM |
Svetlin S. |
Me too, that was back in 2011, I think, when it was worth going...
|
Svetlin S. |
Have a good one, bye.
|
Mark M. |
that's a wrap for today's chat
|
Mark M. |
the transcript will be posted to http://commonsware.com/office-hours/ shortly
|
Mark M. |
the next chat is Tuesday at 4pm US Eastern
|
Mark M. |
have a pleasant day!
|
Svetlin S. | has left the room |
Mark M. | turned off guest access |