17

I am making one android library. And in my library i want to allow a user to perform a particualar task either in background or in main thread.

I want to do something like this .

1st Scenario

MyLibabry.with(context)
     .performSomeTask(<params>)
     .execute();

When user write the above snippet code. the task should be perform in background. So i will return the result of task using any listeners.

And now consider the below snippet code.

2nd Scenario

Result result = MyLibabry.with(context)
     .performSomeTask(<params>)
     .get();

Now, When user attach the get() at the end of the statement. The task should be execute on main thread and block other threads . That's why result is initialized immediately.

So My Question is how can i implement the feature that if user attached the .get(), that thread in performSomeTask() should run on main thread. otherwise in background.

Note : Don't focus on returning result. I will implement it in Java Generics. What i want to know is how to make code reusable so that when user only attach .get() it will run on main thread. Otherwise that same code should run on background. I don't want to write repeated code.

You can refer the existing library.

  • AsyncTask - Android official for performing background task.
  • ION - for download/upload file

So two library have that same feature, and there are many more library which does the same, and i want to do the same.

It will be great if some one provide me a small example for this.

Moinkhan
  • 12,732
  • 5
  • 48
  • 65
  • Let's break it down to a Runnable: You can just call `run` and it will run on the current thread. Or you can set up a thread `new Thread(myRunnable).start()` to have it run in a different thread or you can submit it to some Executor ... So in your case calling `get()` would probably map to running the run method directly and `execute` to post it to some HanlderThread or Executor or the like ... – Fildor Jun 28 '16 at 09:05
  • Added an answer that gives more info into Fildor's Runnable example and shows basic Executor code that is Java and Android best practice for the kind concurrency wrapping you are trying to do. – J-Boss Jul 08 '16 at 01:31

3 Answers3

6

(I hope, you clarify with returning values in the examples above) So this is several type of single solution for your task. Look closely to running task in new thread. You need to start you action in background ThreadPool....

Note! (Also I hope you clarify with using background Threads. For example, using your code above with function .execute() - doesn't start new Thread, but just putting new task for executing from BlockingQueue in static ThreadPool)

So what is next? Using function .get() for executing task in MainThread, you need to post this task in current action loop. And you will help Handler and Looper. And answering you question, you should know, that you have only one way for this task. Starting action in background thread using method .execute() and starting new task to Handler using method .get(). That is all!


If you want know about some examples of realization, there are many kinds of solution. I just post single using Handler and HandlerThread for understating work of this example.

public class MyLibrary extend HandlerThread {

    private static Handler backgroundHandler;
    private static Handler foregroundHandler;
    private static MyLibrary myLibrary

    private MyLibrary () {
        super(MyLibrary.class.getSimpleName());
        start();
    }

    public static MyLibrary getInstance() {
        if (myLibrary == null) {
            synchronized (MyLibrary.class) {
                if (myLibrary == null) {
                    myLibrary = new MyLibrary();
                }
            }
        }
        return myLibrary;
    }

    public static WorkingTask with (Context context) {
         //Just update, if there are null
         if (foregroundHandler == null) {
              foregroundHandler = new Handler(context.getMainLooper);
         }

         if (backgroundHandler == null) {
             backgroundHandler = new Handler(getLooper);
         }
         return new WorkingTask();
    }

    public void getBackgroundHandler () {
        return backgroundHandler;
    }
    public void getForegroundHandler () {
        return foregroundHandler;
    }
 }


  // ..........


public class WorkingTask  {
   private Runnable workingRunnable;

   public performTask (Runnable run) {
       this.workingRunnable = run; 
       return this;
   }

    public void execute () {
       MyLibrary.getInstance().getBackgoundHandler()
          .postRunnable(workingRunnable)
    }

    public void get () {
       MyLibrary.getInstance().getForegroundHanlder()
          .postRunnable(workingRunnable)
    }
}
Onur
  • 5,617
  • 3
  • 26
  • 35
GensaGames
  • 5,538
  • 4
  • 24
  • 53
  • Good answer, but i think that `execute()` must receive a `listener` (or `callback`) to notify an error or success. What you think? – Douglas Nassif Roma Junior Jul 01 '16 at 11:53
  • Sure! But i missed that, just for show example of main question. There are will be solutions with returning value, and status of work. – GensaGames Jul 01 '16 at 11:55
  • And if you know Handler good. You know, that using example above HandlerThread - using single Thread. But you may write you library ThreadPool, so it will perform you background tasks in several threads using simple execute. – GensaGames Jul 01 '16 at 11:57
  • Please forgive any seeming complaint with your code. It is not that your example isn't excellent, but it is retreading work done years ago. Work that is already more refined and included in the Java Spec. – J-Boss Jul 08 '16 at 01:34
  • @J-Boss Sure using Executors for this task- better and more flexible solution. I just wrote as example, with Handlers. Thanks. – GensaGames Jul 08 '16 at 06:45
  • Ok I Appreciate your Effort. But I want to do this using the `Executors` and `FutureTask` class of Java. So it will be great if you can provide the solution using the Java concurrency API. Which is standard for this kind of Functinality. I have review the code of `ION` & `AsyncTask`They are also using the Java Concurrency API for this. – Moinkhan Jul 12 '16 at 07:21
  • @Moinkhan it's no problem making this solution with executors. I just show an example... – GensaGames Jul 12 '16 at 07:55
2

You have to use Future Task class to do task in background.

1st Scenario If the task is in background then, you will return using result interface method.

2nd Scenario If the task is in foreground then, you will return output directly to calling class.

Sample code for execute()

public void execute() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Future future = mExecutorService.submit(mCursorBinderTask);
                Log.d("asd", "onCreate: started ");
                while (true) {
                    if (future.isDone()) {
                        if (mResultListener != null) {
                            try {
                                mResultListener.onResult(future.get());
                            } catch (InterruptedException e) {
                                mResultListener.onResult(null);
                            } catch (ExecutionException e) {
                                mResultListener.onResult(null);
                            }
                        }
                        break;
                    }
                }
            }
        }).start();
    }

Sample code for get()

public ArrayList<T> get() throws ExecutionException, InterruptedException {
        return mCursorBinderTask.call();
    }
  • Ok I founf that you are using `FutureTask` & `Executor` but you code is look like snippet. Can you provide some more code ..? – Moinkhan Jul 12 '16 at 07:23
0

Don't mean to burst any bubbles here, but aren't we just recreating java.util.concurrent.Executors, That is already android best practice to use for flexible concurrent tasks (i.e. thread & handler management)

From: android.os.AsyncTask

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

From: java.util.concurrent.Executors

Factory and utility methods for Executor, ExecutorService, ScheduledExecutorService, ThreadFactory, and Callable classes defined in this package. This class supports the following kinds of methods:

  • Methods that create and return an ExecutorService set up with commonly useful configuration settings.
  • Methods that create and return a ScheduledExecutorService set up with commonly useful configuration settings.
  • Methods that create and return a "wrapped" ExecutorService, that disables reconfiguration by making implementation-specific methods inaccessible.
  • Methods that create and return a ThreadFactory that sets newly created threads to a known state.
  • Methods that create and return a Callable out of other closure-like forms, so they can be used in execution methods requiring Callable.

For Example (and what I believe Fildor was trying to get at):

 //An Executor and a set of RunnableTasks to be executed 
 Executor executor = anExecutor();
 executor.execute(new RunnableTask1());
 executor.execute(new RunnableTask2());

The most simple means of direct execution in the caller's thread:

 class DirectExecutor implements Executor {
   public void execute(Runnable r) {
     r.run();
   }
 }

This not an Asynchronous call, but can be easily executed as one, and not just the background thread, here is a version that spawns a new thread per task:

 class ThreadPerTaskExecutor implements Executor {
   public void execute(Runnable r) {
     new Thread(r).start();
   }
 }

And you can impose any kind of limit such as serial execution:

 class SerialExecutor implements Executor {
   final Queue<Runnable> tasks = new ArrayDeque<>();
   final Executor executor;
   Runnable active;

   SerialExecutor(Executor executor) {
     this.executor = executor;
   }

   public synchronized void execute(final Runnable r) {
     tasks.add(new Runnable() {
       public void run() {
         try {
           r.run();
         } finally {
           scheduleNext();
         }
       }
     });
     if (active == null) {
       scheduleNext();
     }
   }

   protected synchronized void scheduleNext() {
     if ((active = tasks.poll()) != null) {
       executor.execute(active);
     }
   }
 }

These are all text book examples, but it's easy to see that since it's already in there we don't need recreate the wheel, especially when it already supports the kind of wrapping that the MoinKahn is trying to achieve and is already best practice.

Thought I would add alink to Oracle's Executor Interface page, for more information on correct concurrency handling, and wrapping Executor Services.

Edit: Additionally here is a really good FutureTask example using ThreadPoolExecutor for in an in-depth answer from Gray on just that.

Community
  • 1
  • 1
J-Boss
  • 927
  • 4
  • 14