Background
I am using AccountManager with a custom account type. In the beginning the App starts the user LogInActivity and a token recieved by the server is stored within the account. The tokens are used by some Worker organized by WorkManager to sync data in the background. Each worker is requesting the token with AccountManager#getAuthTokenByFeatures(). When the user is changing the password on the website connected with the server, the token is expired and the AccountManager is starting (due to password change) the related LogInActivtiy.
Issue If during the user input of the new login data other (parallel running) Worker are requesting an AuthToken, the LogInActivity is started multiple times.
Solution Approach
Setting android:launchmode="singleInstance"of LogInActivity -> only one Activity is started, but second, third, ... calling Worker results in deadlock due to no return of AccountManagerFuture<Bundle>.
Creating Workaround: AccountAthenticator checks if an Instance of LogInActivity is allready running in Foreground and starts all further Activities invisible in the background. If LogIn was successfull all in the background running LogInActivities recieving necessary Information via LocalBroadcastManager -> LocalBroadcastManager is deprecated, power consuming and unnecessary overhead
Is the use of AccountManager in combination with WorkManager correct in this case?
Might there any configuration issue with AccountManager causing this behavior?
Any other solution approaches?
Help is really appreciated. Thank you!
Related
In the wonderful article by Chet Haase I read this advice which I find quite important:
never make a network request in your Application object. That object
may be created when one of the app’s Services or BroadcastReceivers is
started; hitting the network will turn code that does a local update
at a specific frequency into a regular DDoS.
The application I work on currently follows this (bad) practise: it performs a user login in Application.onCreate() - on a background thread of course, but still. This is a requirement: user needs to be logged in before any activity would do any other tasks, which usually depend on logged in user. I currently do this using RxJava in a way that any activity task observables are flatMapped onto an userlogin event and it works quite nice.
So if I should take that login task out of Application, where should it go? At first I thought it would be nice to use ActivityLifecycleCallbacks and watch for the first activity to be created. But this callback (onActivityCreated) will be called after creation, which is too late for me.
So I think that this should be done by creating some BaseActivity class and putting login and other initialization calls in it's first onCreate(). But I don't feel this is too good, because I'm mixing some app-wide logic in an activity class, it's smelly...
Anything I could have missed?
SplashActivity
An activity that starts the application. It checks for resources availability and if needed, obtains them. It also checks whether there is an active user session, and if there isn't performs a log in, if there are remembered credentials, or redirects the user to the Login/Register screen
BaseActivity
An activity that is specific for your app and that holds initialization and lifecycle callback code that is applicable for all your activities in the application.
I'm using AccountManager in a fairly large Android app and am using Retrofit and Otto to handle REST Requests. When a user's token no longer works a 401 error gets returned to my REST Handler.
What I would like is for the REST handler to be able to show the login activity, but since it's not an activity it can't start the getAuthToken process. My next thought is to send a message back to the calling activity using Otto to let it know that the auth token no longer works, then have that activity start the auth process, which seems like it would work, but I have several activities and it doesn't seem like the right away to go about it, handling something that is application wide on an activity level.
How do other people handle this? Do you just put the re-authentication code everywhere that a user might need to re-authenticate?
Im currently using GAE from Android. I have a search operation which performs what might be a long running query on the datastore (called from AsyncTask). The user has an option to logout from the app while this process is still running. Without an option to cancel this request the actual logout has to wait until the process finishes and the control is back to the client (which, as i said, can take a few seconds).
Is there a way to cancel a call to a GAE endpoint while the server is running and return the control to the client ? Thanks.
p.s.
Obviously, cancelling the AsyncTask only is not enough.
Personally, I put the server comm in a service so that requests can (usually) finish regardless of what the user does. In your case, could you let the user log out, but also let the request complete?
Otherwise, I don't know how to cleanly interrupt an endpoint request. Unfortunately, the actual blocking operation is buried in the endpoints (Google API Client library) code and no cancel operation is exposed that I'm aware of.
When you write that cancelling the task - AsyncTask.cancel(boolean mayInterruptIfRunning) - is not enough, is that because you've tried it and it doesn't interrupt a blocked operation.
No, Google Endpoints use servlets and servlets have no (standard) way of telling a running request to cancel it's work. You will need to build something proprietary.
For example: in your long-running process on server you in it's main loop check if user is still logged in (via a flag in the datastore or memcache). If this flag tells you that user is logged out, then you cancel the processing and return. Also a login/logout procedure must appropriately set this flag.
I'm reading article "Managing the Activity Lifecycle" (http://developer.android.com/training/basics/activity-lifecycle/index.html) and I'm little confused. Maybe first I'll try to say what my application do. So, this is some kind of http client. User login to the server and client store authorization (session id). After login, TimerTask is executing, which for every 10 seconds get some small json from server and by the way server know that authorization key is still alive (normally it is valid for ~30 minutes). In this json could be some events which should be shown to the user (I'm using notification manager for this) or questions for which user should answer (I'm showing custom dialog with "Yes", "No", "Don't know" and then POST answer to the server).
This works fine when my application is in foreground, but I don't really know what should I do if the application is stopped/paused.
My doubts:
I want that TimerTask should works even if user click home button or another application appear. There are two reasons: One - user need to be notified about events, two - I need to keep alive session id. But this article say that when activity is in foreground, it should release resources. What this mean? What are restrictions? Must I stop my timer?
Documentation say that system can kill application when it is no longer needed. What does it mean no longer needed? When user don't use it or when application code do nothing for a while? Can my TimerTask keep alive application?
Storing authorization key. I need remember session id in situations where activity is recreated by system like orientation change, etc. I'm using for this SharedPreferences object. Problem is that using this object, key is saved in database and I can't recognize when my application is closed permanently (which mean "logout") or just recreated because orientation is changed. This occur situations when user run application again after couple hours and my activity restore dead session id (my application look like "logged in" because authorization variable is not empty and I use this state as a flag). I need some temporary version of SharedPreferences object. What about bundle object passed in onSaveInstanceState? Is it temporary?
Regards
It looks like you are on the right track. I think that you should continue reading documentation. Some suggestions:
Activity life cycle
Context.bindService()
ServiceConnection
I'm making a simple REST client in Android: I created a service that is responsible for the REST calls, but it needs the username and password to operate.
The username and password is stored in a regular account registry, so what I want to do is to retrieve the usernama and auth token in the service.
The problem is that getAuthToken wants to have the Activity as the parameter, but while I'm in the service I don't have any kind of Activity to pass. Is there a known way to deal with this issue? Or I'm doing is all wrong and I shouldn't even try to access this data from within a service?
Not necessarily: I've used http://developer.android.com/reference/android/accounts/AccountManager.html#getAuthToken%28android.accounts.Account,%20java.lang.String,%20boolean,%20android.accounts.AccountManagerCallback%3Candroid.os.Bundle%3E,%20android.os.Handler%29 before from within a Service.
EDIT: The world changed. It is now recommended to use this. You just have to have your app register an IntentReceiver for the LOGIN_ACCOUNTS_CHANGED_ACTION and then you will receive that intent whenever the user enters a password. You very explicitly don't wait for the result: you either get it immediately if they're already logged in or you get told you need to wait for the intent.