package com.door43.tools.reporting; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * This class manages multiple threads from a static singleton so you can easily keep track of * threads accross activities. */ public class TaskManager { private static Map<String, Integer> mTaskKeys = new HashMap<>(); private static Map<Integer, ManagedTask> mTaskMap = new HashMap<>(); private static Map<String, List<Integer>> mGroupTasksMap = new HashMap<>(); private static Map<Integer, List<String>> mTaskGroupsMap = new HashMap<>(); private static int mCurrentTaskIndex = 0; private static final int KEEP_ALIVE_TIME = 1; private static final TimeUnit KEEP_ALIVE_TIME_UNIT; private static int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors(); private final BlockingQueue<Runnable> mWorkQueue; private final ThreadPoolExecutor mThreadPool; private static TaskManager sInstance = null; static { KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; sInstance = new TaskManager(); } private TaskManager() { mWorkQueue = new LinkedBlockingQueue<>(); mThreadPool = new ThreadPoolExecutor( NUMBER_OF_CORES, // Initial pool size NUMBER_OF_CORES * 4, // Max pool size KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, mWorkQueue); } /** * Returns the TaskManager object * @return */ public static TaskManager getsInstance() { return sInstance; } /** * Adds a task to be executed * @param task the task to be executed * @return the id of the task process */ static public int addTask(ManagedTask task) { mCurrentTaskIndex ++; mTaskMap.put(mCurrentTaskIndex, task); task.setTaskId(mCurrentTaskIndex); queueTask(task); return mCurrentTaskIndex; } /** * Adds a task to be executed. * If the key is already in use the task will not be added. * @param task the task to be executed * @param key a key to retrieve the task at a later time * @return true if the key was added */ static public boolean addTask(ManagedTask task, String key) { if(!mTaskKeys.containsKey(key)) { int index = addTask(task); task.setTaskId(key); mTaskKeys.put(key, index); return true; } else { return false; } } /** * Group a task with others. * The task must be added to the task manager before calling this method * @param task * @param group * @return */ static public boolean groupTask(ManagedTask task, String group) { if(task != null && task.getTaskId() != null) { if (task.getTaskId() instanceof String) { if (mTaskKeys.containsKey(task.getTaskId())) { setGroup(mTaskKeys.get(task.getTaskId()), group); } } else if (task.getTaskId() instanceof Integer) { if (mTaskMap.containsKey(task.getTaskId())) { setGroup((int) task.getTaskId(), group); } } } return false; } static private void setGroup(int id, String group) { // group to task access if(mGroupTasksMap.containsKey(group)) { if(!mGroupTasksMap.get(group).contains(id)) { mGroupTasksMap.get(group).add(id); } } else { List<Integer> l = new ArrayList<>(); l.add(id); mGroupTasksMap.put(group, l); } // task to group access if(mTaskGroupsMap.containsKey(id)) { if(!mTaskGroupsMap.get(id).contains(group)) { mTaskGroupsMap.get(id).add(group); } } else { List<String> l = new ArrayList<>(); l.add(group); mTaskGroupsMap.put(id, l); } } /** * Checks if a task has finished * @param id * @return */ static public boolean isTaskFinished(int id) { return mTaskMap.get(id).isFinished(); } static public ManagedTask getTask(Object id) { if(id instanceof String) { return getTask((String) id); } else if(id instanceof Integer) { return getTask((int)id); } else { return null; } } /** * Returns the task by it's id * @param id * @return */ static private ManagedTask getTask(int id) { if(mTaskMap.containsKey(id)) { return mTaskMap.get(id); } else { return null; } } /** * Returns the task by it's key * @param key * @return */ static private ManagedTask getTask(String key) { if(mTaskKeys.containsKey(key)) { return getTask(mTaskKeys.get(key)); } else { return null; } } /** * Removes a completed task from the manager. * If the task has not finished it will not be removed * @param id the id of the task to be removed. Can be either a string or an int */ public static void clearTask(Object id) { if(id != null) { synchronized (sInstance) { if (id instanceof String) { clearTask((String) id); } else if (id instanceof Integer) { clearTask((int) id); } } } } /** * Removes a completed task from the manager. * If the task has not finished it will not be removed. * @param task the task to be removed */ public static void clearTask(ManagedTask task) { if(task != null) { clearTask(task.getTaskId()); } } /** * Returns a group of tasks * @param group * @return */ public static List<ManagedTask> getGroupedTasks(String group) { List<ManagedTask> tasks = new ArrayList<>(); List<Integer> ids = mGroupTasksMap.get(group); if(ids != null) { for(Integer id:ids) { ManagedTask t = getTask(id); if(t != null) { tasks.add(t); } } } return tasks; } /** * Removes a task from the manager * @param id */ private static boolean clearTask(Integer id) { if(id != null) { if (mTaskMap.containsKey(id) && (mTaskMap.get(id).isFinished())) { mTaskMap.remove(id); // clear group mapping List<String> groups = mTaskGroupsMap.get(id); if (groups != null) { for (String group : groups) { mGroupTasksMap.get(group).remove(id); if (mGroupTasksMap.get(group).size() == 0) { mGroupTasksMap.remove(group); } } mTaskGroupsMap.remove(id); } return true; } } return false; } /** * Removes a task from the manager * @param key */ private static void clearTask(String key) { if(mTaskKeys.containsKey(key)) { if(clearTask(mTaskKeys.get(key))) { mTaskKeys.remove(key); } } } /** * Cancels and removes all tasks */ public static void cancelAll() { ManagedTask[] runnableArray = new ManagedTask[sInstance.mWorkQueue.size()]; // Populates the array with the Runnables in the queue sInstance.mWorkQueue.toArray(runnableArray); // Stores the array length in order to iterate over the array int len = runnableArray.length; /* * Iterates over the array of Runnables and interrupts each one's Thread. */ synchronized (sInstance) { for (int runnableIndex = 0; runnableIndex < len; runnableIndex++) { Thread thread = runnableArray[runnableIndex].getThread(); if (thread != null) { thread.interrupt(); } } } } /** * Adds a task to the thread pool queue * @param task */ private static void queueTask(ManagedTask task) { sInstance.mThreadPool.execute(task); } /** * Cancels a task and removes it from the queue. * The task will however, remain in the list until cleared * @param task */ public static void cancelTask(ManagedTask task) { if(task != null) { synchronized (sInstance) { Thread thread = task.getThread(); if(thread != null) { thread.interrupt(); } task.stop(); } sInstance.mThreadPool.remove(task); } } /** * Cancels and clears all the tasks in a group * @param group */ public static void killGroup(String group) { synchronized (sInstance) { List<ManagedTask> tasks = getGroupedTasks(group); for (ManagedTask t : tasks) { cancelTask(t); clearTask(t); } } } }