10

When the user launches my Android App, I fire up 2 threads to do some processing in the background. thread_1 does some calculations on the client, and thread_2 fetches some data from the server. That all works fine. None of the threads modify the UI. I have two follow up questions.

new Thread(new Runnable(){
    @Override
    public void run(){
        MyClass.someStaticVariable = doSomeCalculations();
    }
}).start();
  1. What is the best practice to retrieve the data from the run() method of a thread? I currently have a static variable, and I assign the relavent calculated data/ fetched data to it. Or is it recommended to use the Handler class to get data out of threads? I imagined one only uses the handler if they wish to update the UI.

    while(true)
    {
        if (!thread1.isAlive() && !thread2.isAlive())  
        {
            startActivity(intent)
        }
    }
    
  2. I need to wait until both threads are finished before I can pass the data from both threads via an Intent. How can I achieve that? I can do it using the code shown above, but that just seems wrong.

CaptJak
  • 3,592
  • 1
  • 29
  • 50
Raunak
  • 6,427
  • 9
  • 40
  • 52
  • 1
    For 1 - use a shared object that you pass through the constructor of the `Runnable`. For 2 - use Thread.join(). – Mihai Sep 22 '11 at 17:18
  • I don't think you can't pass a variable/shared object to the constructor of runnable! – Raunak Sep 22 '11 at 17:28
  • 2) As suggested, use Thread.join() to wait for it to finish; 1) You can pass "final" references or you can call enclosing instance via MyClass.this.doCalc(); – Michael M. Sep 22 '11 at 17:51
  • if he used the Thread.join() , the apps will be blocked until the thread finish his work – Houcine Sep 22 '11 at 17:57
  • Yeah, my bad. I forgot that, in Java, anonymous classes can't have parametered constructors. You can always make the Runnable a nested class, though. – Mihai Sep 22 '11 at 18:08

4 Answers4

12

You could use a Future. It will block on get until the data is available: http://developer.android.com/reference/java/util/concurrent/Future.html

An alternative is to pass a CountDownLatch into the threads and call countDown() when exiting the run method: http://developer.android.com/reference/java/util/concurrent/CountDownLatch.html

final CountDownLatch latch = new CountDownLatch(2);
new Thread(new Runnable(){
  @Override
  public void run(){
    // Do something
    latch.countDown()
  }
}).start();
new Thread(new Runnable(){
  @Override
  public void run(){
    // Do something
    latch.countDown()
  }
}).start();

latch.await()
startActivity(intent)
Joshua
  • 26,234
  • 22
  • 77
  • 106
  • This looks clean way to wait until both threads are finished in Android.. though CountDownLatch doesn't seem to have a way to obtain the calculated data... Future does that.. thanks for your input. – Raunak Sep 22 '11 at 17:42
2

Using callable / Future / ExecutorService would be the cleanest way of doing this in a reqular java app (should be same for android as well)

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<Integer> firstThreadResult = executor.submit(new Callable<Integer>() {
   Integer call() {
   }
});

Future<Integer> secondThreadResult = executor.submit(new Callable<Integer>() {
   Integer call() {
   }
});

executor.shutdown();
executor.awaitTermination(Integer.MAX_VALUE,TimeUnit.SECONDS); // or specify smaller timeout
// after this you can extract the two results
firstThreadResult.get();
secondThreadResult.get();

More detailed example.

gkamal
  • 20,777
  • 4
  • 60
  • 57
  • Yes, I agree. Thats how you would do it in standard java app. I'm thinking if that would be the most efficient way to go on Android. – Raunak Sep 22 '11 at 17:39
  • Efficiency wise it should be same as your approach - launching two threads and waiting for them to finish using join and shared data. Callable & Futures makes the sharing of data cleaner without using static / instance variables. – gkamal Sep 22 '11 at 17:42
1
  1. You can use a shared object (via static field pointing to it or any other way), but you must be aware of two things. First there are synchronization issues with objects accessed by two threads. Use immutable objects to aleviate that. Second, how to notify the other thread that new shared data is available - this depends on what your other thread is doing.

  2. Set common flag that both threads check or set when they finish. This way thread can check if other flag finished before it.

Peter Knego
  • 79,991
  • 11
  • 123
  • 154
  • If you read the question, I specifically say that none of the threads update the UI, so I don't think I need to use AsyncTask. Your second solution seems to be like what I already have.. check if the threads are still alive, if not, move on and fire up that intent. – Raunak Sep 22 '11 at 17:26
  • AsyncTask uses Handlers internally to do a callback on the ui-thread to update the ui. – Kevin Sep 22 '11 at 17:29
  • what do you think of using your proper listener , is a kind of a listener who listen on your thread , when he finished , you will execute what you want ,!!! – Houcine Sep 22 '11 at 17:43
  • no , i mean you can create your proper listener to listen on a thread , kind of : myThread = new MyThread(...); myThread.setOnFinishListener(this); myThread.start(); // and then override the method onFinish( your params ..) ; and do what you want in it , ( launch new Activity , launch another thread , etc ) – Houcine Sep 22 '11 at 17:56
  • Ok, but then the listener would be executed in the other thread (not the one you defined it in). – Peter Knego Sep 22 '11 at 17:58
  • for example , i have two threads , a and b , and i launch the a thread and listen until his finished , and then , i will modify my UI, do some stuff ,and launch a second thread , whitout blocking the apps ,( The user can do anything else in that moment , that's what i want to say . and by using Listener on your threads , it will be easy to controll that . and sorry for my english – Houcine Sep 22 '11 at 18:04
  • You can update UI only in UI thread. UI thread is not constantly running your code. Actually it runs your code mainly to invoke listeners. So, you can not hog the UI thread to wait for another thread. Just use AsyncTask for all this. – Peter Knego Sep 22 '11 at 18:08
  • it depands on the context, you can ovveride the method runOnUIThread easily to tell it that this code will be executed on the UIThread :). Second : i'm agree with u about using AsynTask when you have a lot of updates of UI after launching some operations that take more time – Houcine Sep 22 '11 at 18:12
  • `Activity.runOnUiThread()` is `final` so you can't overide it. You are supposed to call it and provide a `Runnable`. – Peter Knego Sep 22 '11 at 18:18
  • yeah that's what i mean , something like this : this.runIOnUIThread(new Runnable(){... – Houcine Sep 22 '11 at 18:20
0

For query 1:

What is the best practice to retrieve the data from the run() method of a thread?

In addition to Future, you can use Callback mechanism from your thread run() method. In this run() method, pass the value to caller object or set the relevant values in an object.

Implementing callbacks in Java with Runnable

For query 2:

I need to wait until both threads are finished before I can pass the data from both threads via an Intent

You can achieve it in multiple ways in addition basic join() API.

1.ExecutorService invokeAll() API

Executes the given tasks, returning a list of Futures holding their status and results when all complete.

2.CountDownLatch

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

3.ForkJoinPool or newWorkStealingPool() in Executors

Refer to this related SE question:

wait until all threads finish their work in java

Community
  • 1
  • 1
Ravindra babu
  • 37,698
  • 11
  • 250
  • 211