Office Hours — Today, November 21

Yesterday, November 20

Nov 21
7:25 PM
Mark M.
has entered the room
Mark M.
turned on guest access
Steve S.
has entered the room
Steve S.
Hi Mark!
Mark M.
hi, Steve!
how can I help you today?
Steve S.
I have a follow-up question about the Bluetooth service we've discussed previously:
. The service is a started service that needs to be available to several activities in the app, so my idea is to start it in an Application subclass. The service needs some data that is persisted to run Bluetooth, so I was going to include that in the intent used to start the service. My question is what the return value from onStartCommand() should be - I think I should use START_NOT_STICKY but am not sure.
Mark M.
IIRC, this is a kiosk app, right?
Steve S.
yes
Mark M.
and you want this Bluetooth service to basically be running all the time?
Steve S.
yes
7:30 PM
Mark M.
then between that, and the "include that in the intent" bit, you should use START_REDELIVER_INTENT
most likely, your process won't ever get terminated, outside of a crash (in which case, you have bigger problems)
but, if during some maintenance cycle, when the app is in the background, Android terminates your process, it should restart it soonish to get your service running again
and, it will hand you the Intent that you used to start the service earlier, so you have your data
though, IMHO, the service should read its own data, rather than have an Application be responsible for reading it from persistent storage
Steve S.
as things stand, there's an issue with redelivering intents (so i might need to do what you said regarding having the service get its own data).
Here's onStartCommand:
View paste
@Override
public int onStartCommand(Intent intent, int unused1, int unused2) {
    String action = intent.getAction();
    switch (action) {
        case ACTION_BLUETOOTH_START:
            start(intent);
            break;
        case ACTION_BLUETOOTH_WRITE:
            write(intent);
            break;
        default:
            throw new IllegalArgumentException("unknown action: " + action);
    }
    return START_NOT_STICKY;
}
An intent is used for both start and write, and they carry different data
Mark M.
hmmm...
OK, then with this flow, START_REDELIVER_INTENT is not the right answer
Steve S.
so maybe i'll need to change things around
7:35 PM
Mark M.
I'm just not sure what value there is in having the Application push initialization data into the service
ideally, components are self-sufficient to the extent possible
and, in your service, you're in ideal shape to do that I/O on a background thread
Steve S.
right. i've considered both approaches. the main reason for not having the service get the data is that it makes the Bluetooth code less general. but there may be countervailing considerations then
backing up a bit, i'm not very clear on the circumstances surrounding shutting down and restarting the service
Mark M.
um, could you be more specific?
Steve S.
sure
i was thinking that the service would get shut down only if the rest of the app (e.g. the activity last running) would also get shut down. is that correct?
Mark M.
there are four scenarios:
7:40 PM
Mark M.
(at least for your environment, where the user doesn't have access to Settings)
1. you call stopService()
2. you call stopSelf()
3. your service crashes with an unhandled exception
4. your process is terminated while it is in the background
assuming that you don't do the first two, and that you don't have any bugs, the only consideration is the last one
and even that's fairly unlikely in your scenario, as your app won't be in the background much, I imagine
Steve S.
ok
Mark M.
if your service is in the same process as your activities (the default behavior), then all of that goes away when the process does
Steve S.
right
Mark M.
if you have your service in a separate process (via android:process), then that process could be terminated separately
Steve S.
no, everything is in one process
great - very helpful
Mark M.
cool
7:45 PM
Steve S.
so the next thing i'm unclear about is how things get restarted after the process is terminated
i take it it's not the same as starting the app for the first time?
Mark M.
that depends on what "things" are
your service will get restarted automagically if you use START_STICKY or START_REDELIVER_INTENT
it will not if you use START_NOT_STICKY; it will be your job to start that service from elsewhere in your code as appropriate
your UI is gone until some future startActivity() call
(in theory, it could come back due to BACK navigation or the RECENTS list, but I'm assuming those aren't relevant in your kiosk scenario)
Steve S.
so in those scenarios, Application#onCreate wouldn't be called?
Mark M.
that will be called when your process is started for whatever reason (e.g., your service is being restarted due to START_STICKY or START_REDELIVER_INTENT)
7:50 PM
Steve S.
so if i start the service in MyApplication#onCreate, and if the process is terminated, the service would still get restarted (it might just take longer)?
7:55 PM
Steve S.
i mean if use use START_NOT_STICKY
Mark M.
well, that depends on what would cause your process to exist
Steve S.
ok
Mark M.
if you are using START_NOT_STICKY, Android will not start your process as part of restarting your service automatically
so now you need some external agent to start an activity, start a service, or send a broadcast to your app
Steve S.
so that could be the user navigating back to the app?
Mark M.
in principle, yes -- not sure how relevant that is to your kiosk scenario, though
Steve S.
ok
8:00 PM
Steve S.
ok. i think i have a better understanding of the situation
users will be able to access Android options (with a password).
it would be ok if Bluetooth stopped running then, as long as it starts again once the user goes back to the activity
Mark M.
there is the theoretical possibility that they could Force Stop your app from Settings
Steve S.
ok
Mark M.
and, depending on the version of Android you're running, they might have the ability to stop the service from the Settings UI
Steve S.
we're now using 4.4
Mark M.
yeah, that probably still has the manual service control stuff in Settings
by "user", I assume you mean somebody responsible for administering these devices
Steve S.
yes
Mark M.
in that case, if they Force Stop your app or stop your service from Settings, it's their own damn fault
:-)
your app will resume normal behavior as soon as they go back to your UI, though
8:05 PM
Steve S.
so they could do that by starting the app normally?
Mark M.
yes
Steve S.
ok
so reviewing:
i could leave onStartCommand the way i have it now (not that I should) as long as i'm ok with the possibility of pathological cases where the administrator calls Force Stop or the stops the service from Settings?
Mark M.
pretty much
Steve S.
great.
thank you so much for working through all these details with me!
Mark M.
if the admin leaves your UI via the password, brings up Settings, then goes to lunch, your process might be terminated while Settings is in the foreground
Steve S.
ok
Mark M.
but, as soon as the admin navigates back to your app, Android will create a fresh process for you
8:10 PM
Steve S.
cool
i'm wondering if i could show you some more of the service code and get your feedback
Mark M.
um, sure, but do bear in mind that these chat transcripts get archived
be judicious about posting significant quantities of non-open source code
Steve S.
sure - thanks
here's the start() method that gets called in onStartCommand():
(i've tried to take account of feedback i've gotten from you in prior chat sessions)
View paste
private void start(Intent intent) {
    if (mConnectThread != null || mIsStopped)
        return;
    mData = intent.getParcelableExtra(EXTRA_DATA);
    BluetoothAdapter adapt = BluetoothAdapter.getDefaultAdapter();
    if (adapt == null) {
        postStatus(Status.UNSUPPORTED);
        stopSelf();
    } else {
        mConnectThread = new ConnectThread(adapt);
        postStatus(Status.CONNECTING);
        mConnectThread.start();
    }
}
so i'm now getting the adapter on the main thread, and stopSelf() is called here but nowhere else
and here's the run method of ConnectThread:
8:15 PM
Steve S.
View paste (2 more lines)
ConnectThread:
@Override
public void run() {
    for (;;) {
        if (!mmAdapter.isEnabled()) {
            postError(Error.DISABLED);
        } else {
            if (connect())
                return;
        }
        synchronized (LOCK) {
            if (mIsStopped)
                return;
        }
        final long sleepMillis = 200;
...
Mark M.
I'm not a big fan of infinite loops with sleep() delays
Steve S.
so (taking your advice) i don't recreate ConnectThread for each connection attempt, but use a sleep loop
ok. what would be better?
Mark M.
perhaps nothing -- I don't know enough of your app
and, assuming that these kiosks are always connected to power, it's not a big deal
Steve S.
right, the device is always plugged in
anything else?
Mark M.
and this loop exits once you connect, so that's not too bad
I was thinking at first that this would be going the entire time your app was running
Steve S.
no
Mark M.
however, the loop keeps going so long as mmAdapter.isEnabled() is false and mIsStopped is false
not sure how much of an issue that is
do you want to be calling postError() every 200ms in this situation?
8:20 PM
Steve S.
right, maybe that could be less frequent
Mark M.
either extend your sleep time, or only call postError() every Nth pass through the loop, or something
Steve S.
ok
i'm not wedded to that sleep time
Mark M.
that's good, because marrying a sleep time is probably illegal in your jurisdiction
:-)
Steve S.
good point
Mark M.
come for the developer support, stay for the bizarre legal advice!
(note: nothing in these chats constitutes legal advice)
Steve S.
then i won't rule out marrying the sleep time
no more questions today
Mark M.
OK
Steve S.
this has been extremely helpful
Mark M.
glad to be useful!
Steve S.
i really appreciate you're working through the details of shutting down and starting up the app with me
Mark M.
it's a messy area
in a sea of messy areas known as Android app development
8:25 PM
Steve S.
i've looked all over for that information and haven't found anything at that level of detail
thank you, and have a good rest of the evening!
Mark M.
you too!
Steve S.
has left the room
8:30 PM
Mark M.
turned off guest access

Yesterday, November 20

 

Office Hours

People in this transcript

  • Mark Murphy
  • Steve S