Frage

In my little app, I receive a series of queued onEventXXX(), from the system (not controlled by me).

The timing of those onEventXXX() is not guaranteed. The only thing guaranteed is that they are received in the order in which they were put into the queue.

Inside onEventXXX() I have to fire an operation (O) that cannot start while another process (P) is going on.

But I cannot just discard this "firing an operation (O)". I must wait until that process (P) is complete, then fire it.

IOW, I have to queue or at least postpone that "firing an operation" until that process (P) is complete.

My immediate idea for implementing this was that , instead of firing the operation (O), I would:

  1. Start a one-shot timer that would periodically check on process (P)
  2. When timer elapses, if process (P) isn't complete, start the time (itself) again.
  3. When timer elapses, if process (P) is complete, fire operation (O).

But knowing the richness of Android, I am suspecting there is a better way of doing this, already built into the system/API.

If there is a better way to implement the above, what would it be?

War es hilfreich?

Lösung

CountDownLatch would be the best solution.

I don't have detail example, but I think you can use it with ease.

Process is like below

  1. define CountDownLatch varaiable named latch
  2. set latch count to 1
  3. when event O received, make a new ThreadO. in ThreadO, wait until latch's count reaches 0.
  4. when process P completes, countdown latch.

If you code like that, you can handle many situations easily.

  1. if O received first, you can wait until P is complete.
  2. if P received first, you can start O immediately.
  3. you can use timeout feature that CountDownLatch provides.

I use CountDownLatch in asyc unit test. You can see my code here : http://kingori.egloos.com/4554640

Although it's not like your case, but you can grab hint from my code. Also, javadoc of CountDownLatch provides good example.

Andere Tipps

I would do it this way:

  • in onEventX, check if process exited, if yes, do your action immediately (you can also omit this step and just do 2nd step)

  • otherwise, create AsyncTask where you call Process.waitfor() in doInBackground, when it completes, do your action in onPostExecute

With this approach, you call action on UI thread, and as soon as possible.

Do not use timers for this task. Timer is a "polling" method. If your timer would be ticking too fast, you would just waste CPU time. If it's ticking too slow, your action would be called with possible delay.

First, I would suggest that if a user clicked on something he should see some immediate result, in your case, if the button action needs to wait for something add a circle loading animation on the button immediately to signal to the user the action will happen shortly.

Now for the action itself, if there is only one (or few) such possible actions that take place after one (or few) possible background process, I would have the button synchronously raise a flag (maybe via a persistent class member) and have the long process synchronously check for that flag after it finishes it's process and perform the requested action on finishing.

Another possible option if to use wait and notify to have the button code wait for the process to finish, this is usually considered a very high performance solution in Java:

for example (ignore typos, this is on-the-spot):

button.setOnClickListener(new OnClickListener() { 
    public void onClick(View v) {
        v.post(new Runnable() {
            @Override
            public void run() {
                synchronized (lockObject) {
                    try {
                        lockObject.wait();
                        doAction();
                    } catch (InterruptedException e) {}
                }
            }
        }
    }
}

Then have the long process notify when it finishes.

Note that new Runnable() doesn't create a new thread, it simply adds a Runnable to the View's thread queue

As suggested by mice, the button onClick handler should check if the process is not currently running, and if so just perform the action immediately.

This way you don't need to create a new thread (or an AsyncTask which is actually a pool of threads)

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top