4

My doubt is regarding how to run a piece of code within an API in my service, on another thread.

I have an API func in my service. Only a part of this API's code, which is independent (2-3 LOC), I want to move it to separate thread, as these take up siginificant time, and as these lines of code have no impact on UI thread as of now. This is what I did.

ORIGINAL CODE:

func(){
    subA();
    subB();
    subC();
}

MODIFIED CODE:

Thread mThread = null;
func(){
    subA();
    if(mThread == null){
        mThread = new Thread(){
            public void run(){
                subB();
                subC();
            }
        } 
    }
    mThread.start();
}

On running this code, I am getting an exception for "thread already started".

I did read on SO about this, that an already started thread cannot be re-started again. I need to create a new thread again and start that. But I do not want to create a new thread object everytime, as that will lead to performance issues on UI thread. Is there any other way this can be handled.

I discovered a couple of other ways to acheive this in android, like Handler, HandlerThread, AsyncTask, etc. But I am not able to settle my mind on which is the best to use here (I do not want to create new objects everytime (of thread/asynctask/handler/handlerthread), just wan to create thread object once and re-use it everytime).

If anybody has worked on this area before, please help !

superuser
  • 625
  • 3
  • 7
  • 19
  • You will be better off using async tasks or handlers. In the long run you'll run into lots of issues that these constructs have been made for and breeding your own solution is also a lot more work to create. Do the threads only if you want to learn. – allprog Jan 30 '14 at 09:34
  • @allprog Can you have a look at my own answer. Any comments, how I can avoid creating new runnable each time (I am doing that right now, as I need to pass new parameters to subB() & subC() every time. And I think creating a runnable object just once would create a problem.) – superuser Jan 30 '14 at 09:50

3 Answers3

3

On further looking into thread part, I managed to find that using ThreadHandler is simpler than using a thread + a looper (as a ThreadHandler has a looper attached to it by default, and manages it well). So, this is the code I am looking towards for now.

HandlerThread mThread = null; 
Handler mHandler = null; 
func(){ 
   subA(); 
    if(mThread == null){
        mThread = new HandlerThread("mThread"); 
        mThread.start();
        mHandler = new Handler(mThread.getLooper());
    } 
    mHandler.post(new Runnable(){
    public void run(){
        subB(); 
        subC();
    }});
}

But I find one issue with this code, I still need to create a new object of a runnable on each call to func(). Need to find out how I can avoid this.

superuser
  • 625
  • 3
  • 7
  • 19
  • Creating objects is cheap (in most cases). With Android it helps a lot if you start thinking of functions as objects that can be passed around like in Javascript. Try to avoid mutable fields as much as possible, use the final keyword wherever you can. This code is exactly what needs to be done in these cases. If concurrent execution of subX is not an issue, then use AsyncTask instead of the handler. Well done! – allprog Jan 30 '14 at 10:14
  • 1
    A few more notes: 1. the handler ensures sequential execution, Runnables are executed one by one. 2. if the runnable has no mutable state and it always operates on the same input data, then it is probably safe to reuse it. However, as long as it does not create an issue, I'd leave the code as is. The current is the safest solution. – allprog Jan 30 '14 at 10:19
  • "if the runnable has no mutable state and it always operates on the same input data, then it is probably safe to reuse it." - I did that. Now my code runs fine, and without creating any new object each time. yey !! – superuser Feb 04 '14 at 12:04
1

You can only start a thread once, which is why your getting the error.

The simple fix is to use a queue. Have the thread read from the queue in an endless loop, and have other threads put "work" to do into the queue.

Here's an example similar to yours (updated to use blocking interface):

Thread mThread = null;
void func(String dataToWorkWith) {
    final BlockingQueue<String> q = new ArrayBlockingQueue<String>(1000);
    subA();
    q.add("some data if you need it");
    if(mThread == null) {
        mThread = new Thread() {
            public void run() {
                while(true) {
                    try {
                        final String dataToWorkWith = q.take();
                        subB(dataToWorkWith);
                        subC(dataToWorkWith);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

        };
        mThread.start();
    }
}

That's assuming you want subA in the main thread each call and subB and subC in a background thread.

Ted Bigham
  • 4,237
  • 1
  • 26
  • 31
1

Move the mThread.start(); inside the if(mThread == null) block. You're starting the same thread unconditionally on subsequent calls to the method.

laalto
  • 150,114
  • 66
  • 286
  • 303
  • If I do this, subB() and subC() will run only on first call to func(). But I want them to run on every call to func() independently, with new parameters each time. – superuser Jan 30 '14 at 09:15
  • Looks like you could really use an `AsyncTask` then, creating an executing a new instance of it for each call. – laalto Jan 30 '14 at 09:16
  • Maybe I can do that. But I do not want to create a new object of anything everytime, as that would put load on my UI thread. Creating new objects takes up significant time, and we should avoid that during critical UI operations (AFAIK). I want to find some way, like, maybe I can create an instance of AsyncTask for first call to func(), and re-use that object for every subsequent call to func(). But afaik, that is not possible with AsyncTasks ? Not sure about Handler/HandlerThread. – superuser Jan 30 '14 at 09:20