/**
* OpenAtlasForAndroid Project
* The MIT License (MIT) Copyright (OpenAtlasForAndroid) 2015 Bunny Blue,achellies
* <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software
* without restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
* <p>
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
* <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author BunnyBlue
**/
package com.openatlas.android.task;
import android.annotation.TargetApi;
import android.os.AsyncTask;
import android.os.Build.VERSION;
import android.os.Debug;
import android.os.Looper;
import android.os.MessageQueue.IdleHandler;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class Coordinator {
private static final String TAG = "Coord";
private static final Executor mExecutor;
static final Queue<TaggedRunnable> mIdleTasks;
private static final BlockingQueue<Runnable> mPoolWorkQueue;
public static class CoordinatorRejectHandler implements
RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable runnable,
ThreadPoolExecutor threadPoolExecutor) {
Object[] toArray = mPoolWorkQueue.toArray();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append('[');
for (Object obj : toArray) {
if (obj.getClass().isAnonymousClass()) {
stringBuilder.append(getOuterClass(obj));
stringBuilder.append(',').append(' ');
} else {
stringBuilder.append(obj.getClass());
stringBuilder.append(',').append(' ');
}
}
stringBuilder.append(']');
throw new RejectedExecutionException("Task " + runnable.toString()
+ " rejected from " + threadPoolExecutor.toString()
+ " in " + stringBuilder.toString());
}
private Object getOuterClass(Object obj) {
try {
Field declaredField = obj.getClass().getDeclaredField("this$0");
declaredField.setAccessible(true);
obj = declaredField.get(obj);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e2) {
e2.printStackTrace();
} catch (IllegalArgumentException e3) {
e3.printStackTrace();
}
return obj;
}
}
public static abstract class TaggedRunnable implements Runnable {
public final String tag;
public TaggedRunnable(String tag) {
this.tag = tag;
}
@Override
public String toString() {
return getClass().getName() + "@" + this.tag;
}
}
static class PostTask extends AsyncTask<Void, Void, Void> {
private final TaggedRunnable mTaggedRunnable;
@Override
protected Void doInBackground(Void... params) {
return process(params);
}
public PostTask(TaggedRunnable taggedRunnable) {
this.mTaggedRunnable = taggedRunnable;
}
protected Void process(Void... params) {
Coordinator.runWithTiming(this.mTaggedRunnable);
return null;
}
@Override
public String toString() {
return getClass().getSimpleName() + "@" + this.mTaggedRunnable;
}
}
@TargetApi(11)
public static void postTask(TaggedRunnable taggedRunnable) {
PostTask postTask = new PostTask(taggedRunnable);
if (VERSION.SDK_INT < 11) {
postTask.execute();
} else {
postTask.executeOnExecutor(mExecutor);
}
}
public static void postTasks(TaggedRunnable... taggedRunnableArr) {
for (TaggedRunnable taggedRunnable : taggedRunnableArr) {
if (taggedRunnable != null) {
postTask(taggedRunnable);
}
}
}
public static void postIdleTask(TaggedRunnable taggedRunnable) {
mIdleTasks.add(taggedRunnable);
}
public static void runTask(TaggedRunnable taggedRunnable) {
runWithTiming(taggedRunnable);
}
public static void runTasks(TaggedRunnable... taggedRunnableArr) {
for (TaggedRunnable taggedRunnable : taggedRunnableArr) {
if (taggedRunnable != null) {
runWithTiming(taggedRunnable);
}
}
}
public static void scheduleIdleTasks() {
Looper.myQueue().addIdleHandler(new IdleHandler() {
@Override
public boolean queueIdle() {
TaggedRunnable taggedRunnable = Coordinator.mIdleTasks
.poll();
if (taggedRunnable == null) {
return false;
}
Coordinator.postTask(taggedRunnable);
return !Coordinator.mIdleTasks.isEmpty();
}
});
}
private static void runWithTiming(TaggedRunnable taggedRunnable) {
long nanoTime;
long j = 0;
boolean isDebug = true;
if (isDebug) {
nanoTime = System.nanoTime();
j = Debug.threadCpuTimeNanos();
} else {
nanoTime = 0;
}
try {
taggedRunnable.run();
if (isDebug) {
System.out.println("Timing - "
+ Thread.currentThread().getName() + " "
+ taggedRunnable.tag + ": "
+ ((Debug.threadCpuTimeNanos() - j) / 1000000)
+ "ms (cpu) / "
+ ((System.nanoTime() - nanoTime) / 1000000)
+ "ms (real)");
}
} catch (RuntimeException e) {
System.out.println("Exception in " + taggedRunnable.tag);
if (isDebug) {
System.out.println("Timing - "
+ Thread.currentThread().getName() + " "
+ taggedRunnable.tag + " (failed): "
+ ((Debug.threadCpuTimeNanos() - j) / 1000000)
+ "ms (cpu) / "
+ ((System.nanoTime() - nanoTime) / 1000000)
+ "ms (real)");
}
} catch (Throwable th) {
th.printStackTrace();
int i = 1;
if (isDebug) {
System.out.println("Timing - "
+ Thread.currentThread().getName() + " "
+ taggedRunnable.tag + (i != 0 ? " (failed): " : ": ")
+ ((Debug.threadCpuTimeNanos() - j) / 1000000)
+ "ms (cpu) / "
+ ((System.nanoTime() - nanoTime) / 1000000)
+ "ms (real)");
}
}
}
@TargetApi(11)
static Executor getDefaultAsyncTaskExecutor() {
if (VERSION.SDK_INT >= 11) {
return AsyncTask.SERIAL_EXECUTOR;
}
try {
Field declaredField = AsyncTask.class.getDeclaredField("sExecutor");
declaredField.setAccessible(true);
return (Executor) declaredField.get(null);
} catch (Exception e) {
return null;
}
}
static Executor getCurrentExecutor() {
return mExecutor;
}
static {
mIdleTasks = new LinkedList<TaggedRunnable>();
mPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(8, 16,
1, TimeUnit.SECONDS, mPoolWorkQueue, new ThreadFactory() {
private final AtomicInteger a = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "CoordTask #" + this.a.getAndIncrement());
}
},
new CoordinatorRejectHandler());
mExecutor = threadPoolExecutor;
SaturativeExecutor
.installAsDefaultAsyncTaskExecutor(threadPoolExecutor);
}
@TargetApi(11)
private static ThreadPoolExecutor getDefaultThreadPoolExecutor() {
try {
return (ThreadPoolExecutor) AsyncTask.THREAD_POOL_EXECUTOR;
} catch (Throwable th) {
Log.e(TAG,
"Unexpected failure to get default ThreadPoolExecutor of AsyncTask.",
th);
return null;
}
}
}