I
I
ioooioi2014-01-31 17:29:57
Android
ioooioi, 2014-01-31 17:29:57

Long running asynchronous operations in Android?

It is necessary to implement a "login form" in the application. A simplified model looks like this:
1. the user presses a button
2. a "long" operation is launched
3. after the operation, the user is transferred to some kind of activity
How to implement this correctly?
Known nuances of the Android platform:
1. Operations not directly related to updating the UI must be performed outside the UI thread.
2. The user can leave the activity at any time, including "immediately after the start of the operation."
3. The user can return to the activity at any time, including "before the operation is completed" and "after the operation is completed"
4. If the user left the activity, there are no guarantees
(To simplify the task, let's assume that after clicking the button, we immediately show a ProgressDialog that cannot be closed, so the Back scenario can be skipped)
Requirements:
If the user clicked the button, then without any "assume" and "if" the operation should work and only the result is available, the user should be transferred to another activity. This behavior should not depend on what the user did immediately after pressing the button: received an incoming call, turned the device, pressed Home, deciding to read Twitter, etc.
1. There is a known simple wrong solution like this:

...
new AsyncTask() {
  void doWork() {
    boolean authenticated = webService.authenticate(email, password);
    if(authenticated) {
      Intent intent = new Intent(AuthActivity.this, HomeActivity.class);
      startActivity(intent);
      finish();
    }
  }
}
...

It only works if the user is not spinning the device, no one is calling him, and he does not decide to go to Twitter while we are authenticating here.
2. There is a complex wrong decision like this:
class WebServiceFacade extends IntentService {
  ... // оно пришлёт нам Broadcast
}
...
// дёргаем сервис
Intent authenticateIntent = new Intent(...)
startService(authenticateIntent);
...
// ждём ответ от сервиса
BroadcastReceiver receiver = .... {
  Intent intent = new Intent(AuthActivity.this, HomeActivity.class);
  startActivity(intent);
  finish(); 
}

This thing will definitely not work if the user is not looking at our activity by the time the operation is completed. Because we will simply unsubscribe from these notifications.
3. Difficult right decision
No Android specifics - we write a self-made service, for example, on ExecutorService in such a way that, firstly, it always exists, and secondly, in addition to sending notifications through a callback, it provides access to the results of operations that have completed, but which no one "listened" to at the time the operations were completed . In this case, when launching an activity, we first check the finished results and draw conclusions based on them, if there are no results, then we subscribe and just hang - we wait for the result, if the user decides to leave, we unsubscribe (well, and further in a circle). This guarantees a safe kill and restart of the activity, while everything is transparent to the user. Something like this:
(at)Singleton
class MyService {
  ...
}
...
(at)Inject
private MyService service;
...
void onResume() {
  if(service.hasAuthResult()) {
    Intent intent = new Intent(AuthActivity.this, HomeActivity.class);
    startActivity(intent);
    finish(); 
  } else {
    service.setListener(this);
  }
}
...
void onPause() {
  service.setListener(null);
}
...
void MyServiceListener.onAuthResult(bool isAuthenticated) {
    Intent intent = new Intent(AuthActivity.this, HomeActivity.class);
    startActivity(intent);
    finish();  
}
...

I implemented solution number 3, it works great, but I don't like the amount of code and the very fact of having such a bike. Maybe I'm missing something and with the same correctness, everything can be done easier?

Answer the question

In order to leave comments, you need to log in

3 answer(s)
M
Mintormo, 2014-01-31
@Mintormo

You have described all this in such detail that there is nothing to object to. You have such requirements that it is easier, apparently, not to do. You probably know that Android is full of solutions of this kind: complex and sweeping. For example when working with XML. So I think you should leave it like that.

F
FoxInSox, 2014-01-31
@FoxInSox

You are too categorical about AsyncTask. AsyncTask doesn't have to run another activity, it should have one simple task. In your case: send a request > receive a response > store in the database the data that the user is authorized or not > notify those interested that the request was successful / not successfully completed. Then there are only two scenarios:
1. If the user is still in activity authorization, then the next activity is launched
2. If the user is somewhere else, then when returning to the application, the onResume method will be called in which the database must be checked for authorization.
PS The same story, by the way, with IntentService. He should just execute the request and save the result in the database, he should not broadcast the event ala "authorization was successful, launch the specific activity"
PS2 ProgressDialog without the possibility of canceling it in this case bad manners, because. the user could just accidentally press the button, or he is in a bad network zone and he will have to watch for 20-30 seconds until the request is completed.

K
Konstantin, 2014-02-01
@Norraxx

I would do this with AsyncTask. The reason is simple, because you don't want the user to notice that some additional service is running there ...
And the dialogs - all the blocking dialogs in android - are insanity.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question