Skip to content

Commit

Permalink
Add bolts tasks to this library (#1018)
Browse files Browse the repository at this point in the history
Co-authored-by: Roger Hu <[email protected]>
  • Loading branch information
rogerhu and Roger Hu authored Apr 21, 2020
1 parent 2d92432 commit a1db62a
Show file tree
Hide file tree
Showing 18 changed files with 3,474 additions and 2 deletions.
60 changes: 60 additions & 0 deletions bolts-tasks/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the/
// LICENSE file in the root directory of this source tree.

apply plugin: 'java'

configurations {
provided
}

sourceSets {
main {
compileClasspath += configurations.provided
}
}

dependencies {
provided 'com.google.android:android:4.1.1.4'
testImplementation 'junit:junit:4.12'
}


javadoc.options.addStringOption('Xdoclint:none', '-quiet')

task sourcesJar(type: Jar) {
classifier = 'sources'
from sourceSets.main.allJava
}

task javadocJar (type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}

artifacts {
archives sourcesJar
archives javadocJar
}

//endregion

//region Code Coverage

apply plugin: 'jacoco'

jacoco {
toolVersion = '0.7.1.201405082137'
}

jacocoTestReport {
group = "Reporting"
description = "Generate Jacoco coverage reports after running tests."
reports {
xml.enabled true
html.enabled true
}
}

//endregion
129 changes: 129 additions & 0 deletions bolts-tasks/src/main/java/bolts/AggregateException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package bolts;

import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
* Aggregates multiple {@code Throwable}s that may be thrown in the process of a task's execution.
*
* @see Task#whenAll(java.util.Collection)
*/
public class AggregateException extends Exception {
private static final long serialVersionUID = 1L;

private static final String DEFAULT_MESSAGE = "There were multiple errors.";

private List<Throwable> innerThrowables;

/**
* Constructs a new {@code AggregateException} with the current stack trace, the specified detail
* message and with references to the inner throwables that are the cause of this exception.
*
* @param detailMessage The detail message for this exception.
* @param innerThrowables The exceptions that are the cause of the current exception.
*/
public AggregateException(String detailMessage, Throwable[] innerThrowables) {
this(detailMessage, Arrays.asList(innerThrowables));
}


/**
* Constructs a new {@code AggregateException} with the current stack trace, the specified detail
* message and with references to the inner throwables that are the cause of this exception.
*
* @param detailMessage The detail message for this exception.
* @param innerThrowables The exceptions that are the cause of the current exception.
*/
public AggregateException(String detailMessage, List<? extends Throwable> innerThrowables) {
super(detailMessage,
innerThrowables != null && innerThrowables.size() > 0 ? innerThrowables.get(0) : null);
this.innerThrowables = Collections.unmodifiableList(innerThrowables);
}

/**
* Constructs a new {@code AggregateException} with the current stack trace and with references to
* the inner throwables that are the cause of this exception.
*
* @param innerThrowables The exceptions that are the cause of the current exception.
*/
public AggregateException(List<? extends Throwable> innerThrowables) {
this(DEFAULT_MESSAGE, innerThrowables);
}

/**
* Returns a read-only {@link List} of the {@link Throwable} instances that caused the current
* exception.
*/
public List<Throwable> getInnerThrowables() {
return innerThrowables;
}

@Override
public void printStackTrace(PrintStream err) {
super.printStackTrace(err);

int currentIndex = -1;
for (Throwable throwable : innerThrowables) {
err.append("\n");
err.append(" Inner throwable #");
err.append(Integer.toString(++currentIndex));
err.append(": ");
throwable.printStackTrace(err);
err.append("\n");
}
}

@Override
public void printStackTrace(PrintWriter err) {
super.printStackTrace(err);

int currentIndex = -1;
for (Throwable throwable : innerThrowables) {
err.append("\n");
err.append(" Inner throwable #");
err.append(Integer.toString(++currentIndex));
err.append(": ");
throwable.printStackTrace(err);
err.append("\n");
}
}

/**
* @deprecated Please use {@link #getInnerThrowables()} instead.
*/
@Deprecated
public List<Exception> getErrors() {
List<Exception> errors = new ArrayList<Exception>();
if (innerThrowables == null) {
return errors;
}

for (Throwable cause : innerThrowables) {
if (cause instanceof Exception) {
errors.add((Exception) cause);
} else {
errors.add(new Exception(cause));
}
}
return errors;
}

/**
* @deprecated Please use {@link #getInnerThrowables()} instead.
*/
@Deprecated
public Throwable[] getCauses() {
return innerThrowables.toArray(new Throwable[innerThrowables.size()]);
}

}
138 changes: 138 additions & 0 deletions bolts-tasks/src/main/java/bolts/AndroidExecutors.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package bolts;

import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* This was created because the helper methods in {@link java.util.concurrent.Executors} do not work
* as people would normally expect.
* <p>
* Normally, you would think that a cached thread pool would create new threads when necessary,
* queue them when the pool is full, and kill threads when they've been inactive for a certain
* period of time. This is not how {@link java.util.concurrent.Executors#newCachedThreadPool()}
* works.
* <p>
* Instead, {@link java.util.concurrent.Executors#newCachedThreadPool()} executes all tasks on
* a new or cached thread immediately because corePoolSize is 0, SynchronousQueue is a queue with
* size 0 and maxPoolSize is Integer.MAX_VALUE. This is dangerous because it can create an unchecked
* amount of threads.
*/
/* package */ final class AndroidExecutors {

private static final AndroidExecutors INSTANCE = new AndroidExecutors();

private final Executor uiThread;

private AndroidExecutors() {
uiThread = new UIThreadExecutor();
}

/**
* Nexus 5: Quad-Core
* Moto X: Dual-Core
* <p>
* AsyncTask:
* CORE_POOL_SIZE = CPU_COUNT + 1
* MAX_POOL_SIZE = CPU_COUNT * 2 + 1
* <p>
* https://github.com/android/platform_frameworks_base/commit/719c44e03b97e850a46136ba336d729f5fbd1f47
*/
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/* package */ static final int CORE_POOL_SIZE = CPU_COUNT + 1;
/* package */ static final int MAX_POOL_SIZE = CPU_COUNT * 2 + 1;
/* package */ static final long KEEP_ALIVE_TIME = 1L;

/**
* Creates a proper Cached Thread Pool. Tasks will reuse cached threads if available
* or create new threads until the core pool is full. tasks will then be queued. If an
* task cannot be queued, a new thread will be created unless this would exceed max pool
* size, then the task will be rejected. Threads will time out after 1 second.
* <p>
* Core thread timeout is only available on android-9+.
*
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>());

allowCoreThreadTimeout(executor, true);

return executor;
}

/**
* Creates a proper Cached Thread Pool. Tasks will reuse cached threads if available
* or create new threads until the core pool is full. tasks will then be queued. If an
* task cannot be queued, a new thread will be created unless this would exceed max pool
* size, then the task will be rejected. Threads will time out after 1 second.
* <p>
* Core thread timeout is only available on android-9+.
*
* @param threadFactory the factory to use when creating new threads
* @return the newly created thread pool
*/
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);

allowCoreThreadTimeout(executor, true);

return executor;
}

/**
* Compatibility helper function for
* {@link java.util.concurrent.ThreadPoolExecutor#allowCoreThreadTimeOut(boolean)}
* <p>
* Only available on android-9+.
*
* @param executor the {@link java.util.concurrent.ThreadPoolExecutor}
* @param value true if should time out, else false
*/
@SuppressLint("NewApi")
public static void allowCoreThreadTimeout(ThreadPoolExecutor executor, boolean value) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
executor.allowCoreThreadTimeOut(value);
}
}

/**
* An {@link java.util.concurrent.Executor} that executes tasks on the UI thread.
*/
public static Executor uiThread() {
return INSTANCE.uiThread;
}

/**
* An {@link java.util.concurrent.Executor} that runs tasks on the UI thread.
*/
private static class UIThreadExecutor implements Executor {
@Override
public void execute(Runnable command) {
new Handler(Looper.getMainLooper()).post(command);
}
}
}
Loading

0 comments on commit a1db62a

Please sign in to comment.