Giter VIP home page Giter VIP logo

bolts-android's Introduction

Bolts

Build Status Coverage Status Maven Central Maven Central License

Bolts is a collection of low-level libraries designed to make developing mobile apps easier. Bolts was designed by Parse and Facebook for our own internal use, and we have decided to open source these libraries to make them available to others. Using these libraries does not require using any Parse services. Nor do they require having a Parse or Facebook developer account.

Bolts includes:

  • "Tasks", which make organization of complex asynchronous code more manageable. A task is kind of like a JavaScript Promise, but available for iOS and Android.
  • An implementation of the App Links protocol, helping you link to content in other apps and handle incoming deep-links.

For more information, see the Bolts Android API Reference.

Download

Download the latest JAR or define in Gradle:

dependencies {
  compile 'com.parse.bolts:bolts-tasks:1.4.0'
  compile 'com.parse.bolts:bolts-applinks:1.4.0'
}

Snapshots of the development version are available in Sonatype's snapshots repository.

Tasks

To build a truly responsive Android application, you must keep long-running operations off of the UI thread, and be careful to avoid blocking anything the UI thread might be waiting on. This means you will need to execute various operations in the background. To make this easier, we've added a class called Task. A Task represents an asynchronous operation. Typically, a Task is returned from an asynchronous function and gives the ability to continue processing the result of the task. When a Task is returned from a function, it's already begun doing its job. A Task is not tied to a particular threading model: it represents the work being done, not where it is executing. Tasks have many advantages over other methods of asynchronous programming, such as callbacks and AsyncTask.

  • They consume fewer system resources, since they don't occupy a thread while waiting on other Tasks.
  • Performing several Tasks in a row will not create nested "pyramid" code as you would get when using only callbacks.
  • Tasks are fully composable, allowing you to perform branching, parallelism, and complex error handling, without the spaghetti code of having many named callbacks.
  • You can arrange task-based code in the order that it executes, rather than having to split your logic across scattered callback functions.

For the examples in this doc, assume there are async versions of some common Parse methods, called saveAsync and findAsync which return a Task. In a later section, we'll show how to define these functions yourself.

The continueWith Method

Every Task has a method named continueWith which takes a Continuation. A continuation is an interface that you implement which has one method, named then. The then method is called when the Task is complete. You can then inspect the Task to check if it was successful and to get its result.

saveAsync(obj).continueWith(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    if (task.isCancelled()) {
      // the save was cancelled.
    } else if (task.isFaulted()) {
      // the save failed.
      Exception error = task.getError();
    } else {
      // the object was saved successfully.
      ParseObject object = task.getResult();
    }
    return null;
  }
});

Tasks are strongly-typed using Java Generics, so getting the syntax right can be a little tricky at first. Let's look closer at the types involved with an example.

/**
 Gets a String asynchronously.
 */
public Task<String> getStringAsync() {
  // Let's suppose getIntAsync() returns a Task<Integer>.
  return getIntAsync().continueWith(
    // This Continuation is a function which takes an Integer as input,
    // and provides a String as output. It must take an Integer because
    // that's what was returned from the previous Task.
    new Continuation<Integer, String>() {
      // The Task getIntAsync() returned is passed to "then" for convenience.
      public String then(Task<Integer> task) throws Exception {
        Integer number = task.getResult();
        return String.format("%d", Locale.US, number);
      }
    }
  );
}

In many cases, you only want to do more work if the previous Task was successful, and propagate any errors or cancellations to be dealt with later. To do this, use the onSuccess method instead of continueWith.

saveAsync(obj).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // the object was saved successfully.
    return null;
  }
});

Chaining Tasks Together

Tasks are a little bit magical, in that they let you chain them without nesting. If you use continueWithTask instead of continueWith, then you can return a new task. The Task returned by continueWithTask will not be considered complete until the new Task returned from within continueWithTask is. This lets you perform multiple actions without incurring the pyramid code you would get with callbacks. Likewise, onSuccessTask is a version of onSuccess that returns a new task. So, use continueWith/onSuccess to do more synchronous work, or continueWithTask/onSuccessTask to do more asynchronous work.

final ParseQuery<ParseObject> query = ParseQuery.getQuery("Student");
query.orderByDescending("gpa");
findAsync(query).onSuccessTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> students = task.getResult();
    students.get(0).put("valedictorian", true);
    return saveAsync(students.get(0));
  }
}).onSuccessTask(new Continuation<ParseObject, Task<List<ParseObject>>>() {
  public Task<List<ParseObject>> then(Task<ParseObject> task) throws Exception {
    ParseObject valedictorian = task.getResult();
    return findAsync(query);
  }
}).onSuccessTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> students = task.getResult();
    students.get(1).put("salutatorian", true);
    return saveAsync(students.get(1));
  }
}).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // Everything is done!
    return null;
  }
});

Error Handling

By carefully choosing whether to call continueWith or onSuccess, you can control how errors are propagated in your application. Using continueWith lets you handle errors by transforming them or dealing with them. You can think of failed Tasks kind of like throwing an exception. In fact, if you throw an exception inside a continuation, the resulting Task will be faulted with that exception.

final ParseQuery<ParseObject> query = ParseQuery.getQuery("Student");
query.orderByDescending("gpa");
findAsync(query).onSuccessTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    List<ParseObject> students = task.getResult();
    students.get(0).put("valedictorian", true);
    // Force this callback to fail.
    throw new RuntimeException("There was an error.");
  }
}).onSuccessTask(new Continuation<ParseObject, Task<List<ParseObject>>>() {
  public Task<List<ParseObject>> then(Task<ParseObject> task) throws Exception {
    // Now this continuation will be skipped.
    ParseObject valedictorian = task.getResult();
    return findAsync(query);
  }
}).continueWithTask(new Continuation<List<ParseObject>, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<List<ParseObject>> task) throws Exception {
    if (task.isFaulted()) {
      // This error handler WILL be called.
      // The exception will be "There was an error."
      // Let's handle the error by returning a new value.
      // The task will be completed with null as its value.
      return null;
    }

    // This will also be skipped.
    List<ParseObject> students = task.getResult();
    students.get(1).put("salutatorian", true);
    return saveAsync(students.get(1));
  }
}).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // Everything is done! This gets called.
    // The task's result is null.
    return null;
  }
});

It's often convenient to have a long chain of success callbacks with only one error handler at the end.

Creating Tasks

When you're getting started, you can just use the Tasks returned from methods like findAsync or saveAsync. However, for more advanced scenarios, you may want to make your own Tasks. To do that, you create a TaskCompletionSource. This object will let you create a new Task and control whether it gets marked as completed or cancelled. After you create a Task, you'll need to call setResult, setError, or setCancelled to trigger its continuations.

public Task<String> succeedAsync() {
  TaskCompletionSource<String> successful = new TaskCompletionSource<>();
  successful.setResult("The good result.");
  return successful.getTask();
}

public Task<String> failAsync() {
  TaskCompletionSource<String> failed = new TaskCompletionSource<>();
  failed.setError(new RuntimeException("An error message."));
  return failed.getTask();
}

If you know the result of a Task at the time it is created, there are some convenience methods you can use.

Task<String> successful = Task.forResult("The good result.");

Task<String> failed = Task.forError(new RuntimeException("An error message."));

Creating Async Methods

With these tools, it's easy to make your own asynchronous functions that return Tasks. For example, you can define fetchAsync easily.

public Task<ParseObject> fetchAsync(ParseObject obj) {
  final TaskCompletionSource<ParseObject> tcs = new TaskCompletionSource<>();
  obj.fetchInBackground(new GetCallback() {
    public void done(ParseObject object, ParseException e) {
     if (e == null) {
       tcs.setResult(object);
     } else {
       tcs.setError(e);
     }
   }
  });
  return tcs.getTask();
}

It's similarly easy to create saveAsync, findAsync or deleteAsync. We've also provided some convenience functions to help you create Tasks from straight blocks of code. callInBackground runs a Task on our background thread pool, while call tries to execute its block immediately.

Task.callInBackground(new Callable<Void>() {
  public Void call() {
    // Do a bunch of stuff.
  }
}).continueWith(...);

Tasks in Series

Tasks are convenient when you want to do a series of asynchronous operations in a row, each one waiting for the previous to finish. For example, imagine you want to delete all of the comments on your blog.

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comments");
query.whereEqualTo("post", 123);

findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Task<Void>>() {
  public Task<Void> then(Task<List<ParseObject>> results) throws Exception {
    // Create a trivial completed task as a base case.
    Task<Void> task = Task.forResult(null);
    for (final ParseObject result : results) {
      // For each item, extend the task with a function to delete the item.
      task = task.continueWithTask(new Continuation<Void, Task<Void>>() {
        public Task<Void> then(Task<Void> ignored) throws Exception {
          // Return a task that will be marked as completed when the delete is finished.
          return deleteAsync(result);
        }
      });
    }
    return task;
  }
}).continueWith(new Continuation<Void, Void>() {
  public Void then(Task<Void> ignored) throws Exception {
    // Every comment was deleted.
    return null;
  }
});

Tasks in Parallel

You can also perform several Tasks in parallel, using the whenAll method. You can start multiple operations at once and use Task.whenAll to create a new Task that will be marked as completed when all of its input Tasks are finished. The new Task will be successful only if all of the passed-in Tasks succeed. Performing operations in parallel will be faster than doing them serially, but may consume more system resources and bandwidth.

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comments");
query.whereEqualTo("post", 123);

findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Task<Void>>() {
  public Task<Void> then(Task<List<ParseObject>> results) throws Exception {
    // Collect one task for each delete into an array.
    ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
    for (ParseObject result : results) {
      // Start this delete immediately and add its task to the list.
      tasks.add(deleteAsync(result));
    }
    // Return a new task that will be marked as completed when all of the deletes are
    // finished.
    return Task.whenAll(tasks);
  }
}).onSuccess(new Continuation<Void, Void>() {
  public Void then(Task<Void> ignored) throws Exception {
    // Every comment was deleted.
    return null;
  }
});

Task Executors

All of the continueWith and onSuccess methods can take an instance of java.util.concurrent.Executor as an optional second argument. This allows you to control how the continuation is executed. Task.call() invokes Callables on the current thread and Task.callInBackground will use its own thread pool, but you can provide your own executor to schedule work onto a different thread. For example, if you want to do work on a specific thread pool:

static final Executor NETWORK_EXECUTOR = Executors.newCachedThreadPool();
static final Executor DISK_EXECUTOR = Executors.newCachedThreadPool();
final Request request = ...
Task.call(new Callable<HttpResponse>() {
  @Override
  public HttpResponse call() throws Exception {
    // Work is specified to be done on NETWORK_EXECUTOR
    return client.execute(request);
  }
}, NETWORK_EXECUTOR).continueWithTask(new Continuation<HttpResponse, Task<byte[]>>() {
  @Override
  public Task<byte[]> then(Task<HttpResponse> task) throws Exception {
    // Since no executor is specified, it's continued on NETWORK_EXECUTOR
    return processResponseAsync(response);
  }
}).continueWithTask(new Continuation<byte[], Task<Void>>() {
  @Override
  public Task<Void> then(Task<byte[]> task) throws Exception {
    // We don't want to clog NETWORK_EXECUTOR with disk I/O, so we specify to use DISK_EXECUTOR
    return writeToDiskAsync(task.getResult());
  }
}, DISK_EXECUTOR);

For common cases, such as dispatching on the main thread, we have provided default implementations of Executor. These include Task.UI_THREAD_EXECUTOR and Task.BACKGROUND_EXECUTOR. For example:

fetchAsync(object).continueWith(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> object) throws Exception {
    TextView textView = (TextView)findViewById(R.id.name);
    textView.setText(object.get("name"));
    return null;
  }
}, Task.UI_THREAD_EXECUTOR);

Capturing Variables

One difficulty in breaking up code across multiple callbacks is that they have different variable scopes. Java allows functions to "capture" variables from outer scopes, but only if they are marked as final, making them immutable. This is inconvenient. That's why we've added another convenience class called Capture, which lets you share a mutable variable with your callbacks. Just call get and set on the variable to change its value.

// Capture a variable to be modified in the Task callbacks.
final Capture<Integer> successfulSaveCount = new Capture<Integer>(0);

saveAsync(obj1).onSuccessTask(new Continuation<ParseObject, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<ParseObject> obj1) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return saveAsync(obj2);
  }
}).onSuccessTask(new Continuation<ParseObject, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<ParseObject> obj2) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return saveAsync(obj3);
  }
}).onSuccessTask(new Continuation<ParseObject, Task<ParseObject>>() {
  public Task<ParseObject> then(Task<ParseObject> obj3) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return saveAsync(obj4);
  }
}).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> obj4) throws Exception {
    successfulSaveCount.set(successfulSaveCount.get() + 1);
    return null;
  }
}).continueWith(new Continuation<Void, Integer>() {
  public Integer then(Task<Void> ignored) throws Exception {
    // successfulSaveCount now contains the number of saves that succeeded.
    return successfulSaveCount.get();
  }
});

Cancelling Tasks

To cancel a Task create a CancellationTokenSource and pass the corresponding token to any methods that create a Task you want to cancel, then call cancel() on the source. This will cancel any ongoing Tasks that the token was supplied to.

CancellationTokenSource cts = new CancellationTokenSource();

Task<Integer> stringTask = getIntAsync(cts.getToken());

cts.cancel();

To cancel an asynchronous call using a token you must first modify the method to accept a CancellationToken and use the isCancellationRequested() method to determine when to halt the operation.

/**
 Gets an Integer asynchronously.
 */
public Task<Integer> getIntAsync(final CancellationToken ct) {
  // Create a new Task
  final TaskCompletionSource<Integer> tcs = new TaskCompletionSource<>();

  new Thread() {
    @Override
    public void run() {
      // Check if cancelled at start
      if (ct.isCancellationRequested()) {
        tcs.setCancelled();
        return;
      }

      int result = 0;
      while (result < 100) {
        // Poll isCancellationRequested in a loop
        if (ct.isCancellationRequested()) {
          tcs.setCancelled();
          return;
        }
        result++;
      }
      tcs.setResult(result);
    }
  }.start();

  return tcs.getTask();
}

App Links

App Links provide a cross-platform mechanism that allows a developer to define and publish a deep-linking scheme for their content, allowing other apps to link directly to an experience optimized for the device they are running on. Whether you are building an app that receives incoming links or one that may link out to other apps' content, Bolts provides tools to simplify implementation of the App Links protocol.

Handling an App Link

The most common case will be making your app receive App Links. In-linking will allow your users to quickly access the richest, most native-feeling presentation of linked content on their devices. Bolts makes it easy to handle an inbound App Link by providing utilities for processing an incoming Intent.

For example, you can use the AppLinks utility class to parse an incoming Intent in your Activity:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // An intent filter in your AndroidManifest.xml has probably already filtered by path
  // to some extent.

  // Use the target URL from the App Link to locate content.
  Uri targetUrl = AppLinks.getTargetUrlFromInboundIntent(getIntent());
  if (targetUrl != null) {
    // This is activity is started by app link intent.

    // targetUrl is the URL shared externally. In most cases, you embed your content identifier
    // in this data.

    // If you need to access data that you are passing from the meta tag from your website or from opening app
    // you can get them from AppLinkData.
    Bundle applinkData = AppLinks.getAppLinkData(getIntent());
    String id = applinkData.getString("id");

    // You can also get referrer data from AppLinkData
    Bundle referrerAppData = applinkData.getBundle("referer_app_link");

    // Apps can easily check the Extras from the App Link as well.
    Bundle extras = AppLinks.getAppLinkExtras(getIntent());
    String fbAccessToken = extras.getString("fb_access_token");
  } else {
    // Not an applink, your existing code goes here.
  }
}

Navigating to a URL

Following an App Link allows your app to provide the best user experience (as defined by the receiving app) when a user navigates to a link. Bolts makes this process simple, automating the steps required to follow a link:

  1. Resolve the App Link by getting the App Link metadata from the HTML at the URL specified
  2. Step through App Link targets relevant to the device being used, checking whether the app that can handle the target is present on the device
  3. If an app is present, build an Intent with the appropriate al_applink_data specified and navigate to that Intent
  4. Otherwise, open the browser with the original URL specified

In the simplest case, it takes just one line of code to navigate to a URL that may have an App Link:

AppLinkNavigation.navigateInBackground(getContext(), url);

Adding App and Navigation Data

Under most circumstances, the data that will need to be passed along to an app during a navigation will be contained in the URL itself, so that whether or not the app is actually installed on the device, users are taken to the correct content. Occasionally, however, apps will want to pass along data that is relevant for an app-to-app navigation, or will want to augment the App Link protocol with information that might be used by the app to adjust how the app should behave (e.g. showing a link back to the referring app).

If you want to take advantage of these features, you can break apart the navigation process. First, you must have an App Link to which you wish to navigate:

new WebViewAppLinkResolver(getContext()).getAppLinkFromUrlInBackground(url).continueWith(
    new Continuation<AppLink, AppLinkNavigation.NavigationType>() {
      @Override
      public AppLinkNavigation.NavigationType then(Task<AppLink> task) {
        AppLink link = task.getResult();
        return null;
      }
    });

Then, you can build an App Link request with any additional data you would like and navigate:

Bundle extras = new Bundle();
extras.putString("fb_access_token", "t0kEn");
Bundle appLinkData = new Bundle();
appLinkData.putString("id", "12345");
AppLinkNavigation navigation = new AppLinkNavigation(link, extras, appLinkData);
return navigation.navigate();

Resolving App Link Metadata

Bolts allows for custom App Link resolution, which may be used as a performance optimization (e.g. caching the metadata) or as a mechanism to allow developers to use a centralized index for obtaining App Link metadata. A custom App Link resolver just needs to be able to take a URL and return an AppLink containing the ordered list of AppLink.Targets that are applicable for this device. Bolts provides one of these out of the box that performs this resolution on the device using a hidden WebView.

You can use any resolver that implements the AppLinkResolver interface by using one of the overloads on AppLinkNavigation:

AppLinkNavigation.navigateInBackground(url, resolver);

Alternatively, a you can swap out the default resolver to be used by the built-in APIs:

AppLinkNavigation.setDefaultResolver(resolver);
AppLinkNavigation.navigateInBackground(url);

Analytics

Bolts introduces Measurement Event. App Links broadcast two Measurement Events to the application, which can be caught and integrated with existing analytics components in your application. (Android Support Library v4 is required in your runtime to enable Analytics.)

  • al_nav_out — Raised when your app sends out an App Links URL.
  • al_nav_in — Raised when your app opens an incoming App Links URL or intent.

Listen for App Links Measurement Events

There are other analytics tools that are integrated with Bolts' App Links events, but you can also listen for these events yourself:

LocalBroadcastManager manager = LocalBroadcastManager.getInstance(context);
manager.registerReceiver(
    new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        String eventName = intent.getStringExtra(MeasurementEvent.MEASUREMENT_EVENT_NAME_KEY);
        if (eventName.equals(MeasurementEvent.APP_LINK_NAVIGATE_IN_EVENT_NAME)) {
          Bundle eventArgs = intent.getBundleExtra(MeasurementEvent.MEASUREMENT_EVENT_ARGS_KEY);
          String targetURL = eventArgs.getString("targetURL");
          String referrerName = eventArgs.getString("refererAppName");
          // Integrate to your logging/analytics component.
        }
      }
    },
    new IntentFilter(MeasurementEvent.MEASUREMENT_EVENT_NOTIFICATION_NAME)
);

App Links Event Fields

App Links Measurement Events sends additional information from App Links Intents in flattened string key value pairs. Here are some of the useful fields for the two events.

  • al_nav_in

    • inputURL: the URL that opens the app.
    • inputURLScheme: the scheme of inputURL.
    • refererURL: the URL that the referrer app added into al_applink_data: referer_app_link.
    • refererAppName: the app name that the referrer app added to al_applink_data: referer_app_link.
    • sourceApplication: the bundle of referrer application.
    • targetURL: the target_url field in al_applink_data.
    • version: App Links API version.
  • al_nav_out

    • outputURL: the URL used to open the other app (or browser). If there is an eligible app to open, this will be the custom scheme url/intent in al_applink_data.
    • outputURLScheme: the scheme of outputURL.
    • sourceURL: the URL of the page hosting App Links meta tags.
    • sourceURLHost: the hostname of sourceURL.
    • success: “1” to indicate success in opening the App Link in another app or browser; “0” to indicate failure to open the App Link.
    • type: “app” for open in app, “web” for open in browser; “fail” when the success field is “0”.
    • version: App Links API version.

License

Bolts-Android is MIT licensed, as found in the LICENSE file.

bolts-android's People

Contributors

belemaire avatar bklimt avatar bradley-curran avatar erikandre avatar ersin-ertan avatar gfx avatar grantland avatar izzette avatar lambdapioneer avatar mkochenough avatar moflo avatar nlutsenko avatar samirshrestha avatar stefanrusek avatar vcube-yunarta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bolts-android's Issues

Task.BACKGROUND_EXECUTOR is badly defined and prone to lockups

This follows parse-community/Parse-SDK-Android#646 where we found a possible bug in the executor, and would also propose some changes.

Bug?

It is told in comments that

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.

This is strictly true but practically not with the blocking queue that is being used (LinkedBlockingQueue with infinite capacity). If I got this correctly, with that queue tasks can always be queued, so the max number of threads you’ll have is the core pool. maxPoolSize has no effect. So this

ThreadPoolExecutor executor =  new ThreadPoolExecutor(
       CORE_POOL_SIZE,
       MAX_POOL_SIZE,
       KEEP_ALIVE_TIME, TimeUnit.SECONDS,
       new LinkedBlockingQueue<Runnable>());

should become

ThreadPoolExecutor executor =  new ThreadPoolExecutor(
       CORE_POOL_SIZE,
       MAX_POOL_SIZE,
       KEEP_ALIVE_TIME, TimeUnit.SECONDS,
       new LinkedBlockingQueue<Runnable>(SOME_NUMBER));

also official docs from ThreadPoolExecutor about queues:

Using an unbounded queue (for example a LinkedBlockingQueue without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution.

Queue strategy

Using the terminology from oracle site, the current strategy (despite the comments) is the unbounded queue, which fits the case of tasks completely independent of others. You will agree this is not bolts case. Can we move to the first strategy?

I can read in comments that the bolts executor was designed trying to emulate the AsyncTask executor. But AsyncTasks are independent by design, can afford a big queue.
Bolts Task are dependent, chained, nested, and the docs suggest a different strategy for this design. We are experiencing lockups in the Parse SDK that you can read in the linked issue (there’s a huge comment explaining the internals). We can block the Task.BACKGROUND_EXECUTOR forever very easily, in situations like the following, when running concurrently the same action:

BACKGROUND_EXECUTOR pool (max 7 threads for 6 processors)
|- background-executor-thread-1 (needs another background thread to complete)
|- background-executor-thread-2 (needs another background thread to complete)
|- background-executor-thread-3 (needs another background thread to complete)
|- background-executor-thread-4 (needs another background thread to complete)
|- background-executor-thread-5 (needs another background thread to complete)
|- background-executor-thread-6 (needs another background thread to complete)
|- background-executor-thread-7 (needs another background thread to complete)

With 2 processors, this takes 3 concurrent actions to have the executor hang. I don’t think this is fixable from outside bolts, because

  • Tasks promote chaining, nesting and dependencies and that’s how you build upon them
  • It’s impossible to get rid of the BACKGROUND_EXECUTOR and use another, because the background executor is the fallback executor of ImmediateExecutor. You can mention a custom executor in every task call, but that’s bad performance wise because you lose the simplicity of ImmediateExecutor and don’t reuse threads.

I propose to move the queuing strategy towards the direct handoffs strategy:

Direct handoffs. A good default choice for a work queue is a SynchronousQueue that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies.

Proposals:

// direct handoff with limited max pool size
// this fits better bolts design
ThreadPoolExecutor executor =  new ThreadPoolExecutor(
       CORE_POOL_SIZE,
       BIG_NUMBER, // 64?
       KEEP_ALIVE_TIME, TimeUnit.SECONDS,
       new SynchronousQueue<Runnable>());

or

// bounded queue with a small value
ThreadPoolExecutor executor =  new ThreadPoolExecutor(
       CORE_POOL_SIZE,
       BIG_NUMBER, // 32?
       KEEP_ALIVE_TIME, TimeUnit.SECONDS,
       new LinkedBlockingQueue<Runnable>(VERY_SMALL_NUMBER));

Using a big queue would have no effect on lockups. If there are 7 core threads needing other 7 threads to complete (as in my example), and the queue can hold 128 commands, this won’t be solved. We would have to wait for 128 requests to hang before having the first resolved. Ideally, VERY_SMALL_NUMBER < CORE_POOL_SIZE to ensure at least a request is fulfilled.

Separate Bolts into 2 Modules for Tasks and AppLinks

I'd like to use the Task pattern in projects where I don't need AppLinks.

e.g. split Bolts into 2 modules:

Bolts/Tasks
Bolts/AppLinks // Depends on Tasks

Publish Bolts/AppLinks with the same Maven identifiers as currently used.

If desired AppLinks could further be split, into a part which does not rely on tasks and another that does:

Bolts/AppLinks
Bolts/Tasks
Bolts/AppLinksTasks // Depends on Tasks and AppLinks

With Bolts/AppLinks containing the core model, and Bolts/AppLinksTasks (or Bolts/AppLinksResolver).
Publish Bolts/AppLinksTasks with the same Maven identifiers as currently used.

If you're open to the idea I can submit a PR.

RejectedExecutionException

Perhaps I have misunderstood the documentation for Tasks, but I am having an issue with a maximum number of Tasks. In the doc it says:

They are independent of threading model, so you don't have to worry about reaching the maximum number of allowed threads, as can happen with AsyncTask.

I assumed this meant I could start as many Tasks as I wanted and the queue would be dealt with. Is that incorrect?

One user has a large (414) backlog of operations that need to be run. I have a service that processes each of these using Tasks. When starting all of these Tasks, I get the following exception:

java.util.concurrent.RejectedExecutionException: Task a.e@44efb458 rejected from java.util.concurrent.ThreadPoolExecutor@45252e88[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 1]
    at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2011)
    at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:793)
    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1339)
    at a.c.a(SourceFile:194)
    at a.c.a(SourceFile:186)
    at com.example.uploader.DataUploadService.getUploadTask(SourceFile:304)
    at com.example.uploader.DataUploadService.uploadData(SourceFile:264)
    at com.example.uploader.DataUploadService.dataUpload(SourceFile:182)
    at com.example.uploader.DataUploadService.onAPIServiceResponse(SourceFile:115)
    at com.example.api.d.a(SourceFile:157)
    at com.c.a.a.a.b.b.a(SourceFile:74)
    at com.c.a.a.a.b.b.onPostExecute(SourceFile:1)
    at android.os.AsyncTask.finish(AsyncTask.java:632)
    at android.os.AsyncTask.access$600(AsyncTask.java:177)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:212)
    at android.app.ActivityThread.main(ActivityThread.java:5151)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:878)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
    at dalvik.system.NativeStart.main(Native Method)

Where the function "uploadData" is:

private void uploadData(fileName) {
    getUploadTask(fileName).continueWith(new Continuation<Boolean, Void>() {
        @Override
        public Void then(Task<Boolean> task) throws Exception {
            boolean success = task.getResult();
            ...
            return null;
        }
    });
}

and "getUploadTask" is:

public static Task<Boolean> getUploadTask(final String fileName) {
    return Task.callInBackground(new Callable<Boolean>() {
        @Override
        public Boolean call() throws IOException {
            File f = new File(fileName);

            Boolean success = false;
            if (f.isFile()) {
                success = uploadFile(f);
            }
            return success;
        }
    });
}

Should I just implement my own queue around the Tasks?
Thanks.

getting bolts to compile under eclipse

I cloned the bolts android repository to my development machine, and imported the Bolts directory into eclipse (Luna, IIMAD) as an Android project.

In order to get it to compile cleanly, I had to make the following changes:

  • in the android manifest, change the package name from "bolts" to "com.bolts"
  • change the target android level from 8 to 21 (lesser levels may also work; I haven't tried them)
  • in the eclipse propertied for the project, change the java compiler potential null pointer reporting level from "Error" to "Warning"

If these changes are of interest and should be pulled into the github repository (I think they should.), I would be most happy to create a pull request for them.

FWIW, I have read all about github pull requests, but never yet done one. :-( So it might take a bit to get the process right. I have done a gazillion branches / merges / pushes to repo's at the companies I have worked for, but never a github pull request.

Thanks in advance,

Jim Renkel
[email protected]

Block of code passed to continueWithTask never gets executed

I've got a small set of users who are encountering and issue where ParseUser.fetchInBackground() appears to never call the subsequent code block passed to continueWithTask. (I realize thats a Parse method, but I think this is Bolts related).

My code looks like this:

  public static Task<Void> check() {
    final Task<Void> timeout = Async.delay(timeoutMs);
    final Task<Void> checkUser = checkUser();

    final ArrayList<Task<?>> tasks = new ArrayList<>();
    tasks.add(timeout);
    tasks.add(checkUser);

    return Task.whenAny(tasks).continueWithTask(new Continuation<Task<?>, Task<SubscriptionCheckerResult>>() {
      @Override
      public Task<SubscriptionCheckerResult> then(Task<Task<?>> wrapperTask) throws Exception {
         // ...
      }
    }, Task.BACKGROUND_EXECUTOR);
  }

  public static Task<Void> checkUser() {
    final ParseUser user = ParseUser.getCurrentUser();
    return user.fetchInBackground().onSuccessTask(new Continuation<ParseObject, Task<Void>>() {
      @Override
      public Task<Void> then(Task<ParseObject> task) throws Exception {
         // ...   
      }
    }, Task.BACKGROUND_EXECUTOR);
  }

And for a small percentage of users the code seems to never return from user.fetchInBackground.

(Related: a number of months back I adopted the practice of always specifying an executor, and changed a bunch of code to use the BACKGROUND_EXECUTOR, and caused a similar issue, and was unable to figure it out, so I reverted the change).

I've also added code which in the case of a timeout, adds a .continueWith call to the checkUser variable, and logs when it ultimately fails or succeeds, and in most cases it doesn't do either.

Bolts conflict with Facebook SDK?

Hi, I'm trying to add the latest Facebook Android SDK from Gradle:

    compile 'com.facebook.android:facebook-android-sdk:4.5.0'

And I get this error:

UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Multiple dex files define Lbolts/AggregateException;

Error:Execution failed for task ':app:dexFreeArmeabi-v7aDebug'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Library/Java/JavaVirtualMachines/jdk1.7.0_71.jdk/Contents/Home/bin/java'' finished with non-zero exit value 2

Execute Bacground Task in parallel.

In Task
I want to run the Task.callInBackground in parallel manner(like in Async,pasrse.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR))). after that continueWithTask in serial manner((like in Async,pasrse.executeOnExecutor(AsyncTask.SerialExecutor)))).

i am changing the executor to achive the above behavoiour
Executors.newCachedThreadPool(),Executors.newFixedThreadPool(15),Executors.newSingleThreadExecutor().
Both the task are run in serial manner(Task and then continueWithTask and then Task......).

Task<String> task = Task.callInBackground(new Callable<String>()
        {
            @Override
            public String call() throws Exception
            {
                String response = null;
                try {
                    System.out.println("Bolt url send " + request.url);
                    response = singleton.load(request);
                } catch (IOException e) {
                    if (request.callback != null && request.handler == null)
                        singleton.handler.onLoadFailed(request.callback);
                    else if (request.callback != null)
                        request.handler.onLoadFailed(request.callback);
                } 
                return response;
            }
        }).continueWithTask(new Continuation<String, Task<String>>()
        {
            @Override
            public Task<String> then(final Task<String> task) throws Exception
            {
                System.out.println("Bolt url recieved " + task.getResult());
                parse(request, task.getResult());
                return null;
            }
        },Executors.newSingleThreadExecutor());

How to achive the above behaviour using Bolt FrameWork.
Thanks.

Task.continueWithTask(task) is not supported?

Task<A, B> task0;
Task<B, C> task1;
Task<C, D> task2;
callInChain(task0, task1, task2)
is it more graceful?

if Task.continueWithTask(task) is supported, i can call TaskCompletionSource.setResult() asynchronously

Does anyone have a demo project available?

I'm trying to play around with Bolts-Android and it would be very helpful if someone could direct me towards a demo or a project that uses Bolts-Android.

Thank you! :D

Return a immutable singleton for `Task.forResult(null)` and `Task.cancelled()`

Currently each call to Task.forResult(null) and Task.cancelled() create new instances of Task and TaskCompletionSource, which are exactly the same for each execution.

These methods can be extremely common in codebases, especially Task.forResult(null), which is used internally in Bolts-Android and Parse-SDK-Android from converting null return values in continuations to stubbing non-asynchronous methods with an asynchronous method signature.

We can optimize these calls by reusing the same value and reduce the amount of memory allocated in applications relying on Bolts and we can do this since each return value is equivalent and essentially immutable.

/cc @nlutsenko

How do I invoke a task on an executor?

If I have some function like this:

Task<Void> doAsync() { // do stuff }}

How do I execute it on an executor? I tried using Call:

return Task.Call(new Callable<Void>() {
      public Void call() throws Exception {
         return doAsync();
     }
});

but obviously that doesn't work, the return type of Call is Task (what I want), but the return type of Callable is Void (not Task).

Thanks! Alex

Question: how to handle activity rotation / re-creation lifecycle with Tasks?

Hi,

This is not a bug, nor a feature request. This is a question.

Bolts seems a very nice library, but I do not really understand how you pictured its usage along with the Android lifecycle.

Suppose I do obtain a Task:

Task<MyResult> myTask = ....;

Now I want to somehow link that task to my UI (show a spinner before showing the result or things like this).

I have to maintain that task across activity recreation.

  • Placing it in static variable is bad practice.
  • I can create a Loader, but then why do I need a task? My loader will just wait for the result and give me it
  • I can associate my task to the ApplicationContext (instead of the activity) and modify the database or a shared preference and update the UI: it's a lot more work then with a loader that does the thing

I'm sure you considered this while developing bolts, how am I supposed to retain the Task, or at least a callback to it across activity instances (destroy/recreate cycle)?

Thanks

API 19 DUPLICATE ENTRY

When I change API 23 to 19 send this Error

Error:Execution failed for task ':app:transformClassesWithJarMergingForDebug'.

com.android.build.api.transform.TransformException: java.util.zip.ZipException: duplicate entry: com/parse/AbstractQueryController$1.class

compile 'com.parse:parse-android:1.13.1'
compile 'com.parse.bolts:bolts-android:1.3.0'

confusion with creating of async methods

public Task fetchAsync(ParseObject obj) {
final Task.TaskCompletionSource tcs = Task.create();
obj.fetchInBackground(new GetCallback() {
public void done(ParseObject object, ParseException e) {
if (e == null) {
tcs.setResult(object);
} else {
tcs.setError(e);
}
}
});
return tcs.getTask();
}

i'm a bit confused with this code.. how come the tcs is type of Task can be used as return type for the method with return type of Task...i also tried to run this part of code..and it gives me incompatible types error..any explanation will be much appreciated. thanks :)

How to stop

I use Boits run asyncMethod like this

Task.callInBackground(new Callable<AuthData>() {
            @Override
            public AuthData call() throws Exception {
                // TODO  network   do  login 
                AuthData data = new AuthData("121212", "23423423");
                Thread.sleep(5000);
                return data;
            }
        }).onSuccess(new Continuation<AuthData, String>() {
            @Override
            public String then(Task<AuthData> result) throws Exception {
                // TODO something other 
                return "success";
            }
        });

But if I dropped out of Activity or want to stop task, what should I do

JetBrains kotlin problems

Tris simple code does not work in kotlin language

Task.create<String>().setResult("ggg")

IDE says:

Type mismatch.
Required: TResult 
Found: kotlin.String

May be you should declare TaskCompletionSource as non-internal class?

README needs to be updated

Although bolts.Task.TaskCompletionSource is now deprecated on the newest release version, if you follow the way the README tells you to declare it using the older version of the library, it makes IDE warn you just like the screenshot below. (using bolts version 1.1.2.)

tcs

I will use the latest version of the library so it is not a huge problem for me, but I guess README either needs to state what classes are deprecated from now on, or temporarily explain how to declare tcs with the newest version of the library and maybe with older versions as well?

Are you still updating?

See if you haven't submitted it for a long time. Would you like to ask if you will continue to update this library?

onSuccessInUi() shortcut for onSuccess(..., Task.UI_THREAD_EXECUTOR)?

We often use yours lib for some async operations and after that we inform user about status operation. So almost every time we write something like:

Task.callInBackground(() -> {
    // do something extra heavy
    return someReturnObject;
})
    .onSuccess(new Continuation<SomeReturnObject, Void>() {
        @Override
        public Void then(Task<SomeReturnObject> task) throws Exception {
            Toast.makeText(context, task.getResult().getSomethingNicely(), Toast.LENGTH_SHORT).show();
            return null;
        }
    }, Task.UI_THREAD_EXECUTOR);

As many people we're lazy, forgetful and will be in ecstasy if we can write this in simpler form:

...
.onSuccessInUi(new Continuation<SomeReturnObject, Void>() {
    @Override
    public Void then(Task<SomeReturnObject> task) throws Exception {
        Toast.makeText(context, task.getResult().getSomethingNicely(), Toast.LENGTH_SHORT).show();
        return null;
    }
});

Any thoughts about this idea? 😉

Publish releases on maven central

To make it easier for users of the library please publish builds of each release on maven central. This makes it easy to integrate bolts in Android projects with the new gradle-based build system. Maven central then contains also a jar that can be downloaded and directly put into libs directory for older Android projects.

continueWhile

This is more of a performance or memory leak/resource management item but as far as I can tell the way continueWhile is written will effectively create a chain of tasks that will keep growing each iteration until the loop completes. In a small loop this probably does not matter but in a long running or infinite loop this grows indefinitely until failure.
I believe this can be prevented by using a completion source to manage reporting loop completion and avoiding continueWithTask or related anything that chains the .then's together. The continuation could be rewritten as a Task returning method so returning false ends the loop. the result of each iteration would use continueWith and proxy the error to the task completion source or dispatch the next iteration if result is good.

AppLinks.getTargetUrlFromInboundIntent never return targetUrl after app installed

As follow by this doc
https://developers.facebook.com/docs/applinks/android

Application should receive data from deep link, after app has been installed.
But native FB application send to GooglePlay only :
market://details?id=my.app.package&referrer=utm_source=apps.facebook.com&utm_campaign=fb4a&utm_content=%7B%22app%22%3A0%2C%22t%22%3A1436879844%7D
There is no info from deep link

And one first launch I try use in my launch screen next methods
AppLinks.getTargetUrlFromInboundIntent
and AppLinkData.fetchDeferredAppLinkData
but they get me null.

Add "unobserved" exception handling

An “unobserved” exception is one that’s stored into the task but then never looked at in any way by the consuming code. There are many ways of observing the exception, including Wait()’ing on the Task, accessing a Task’s Result, looking at the Task’s Exception property, and so on. If code never observes a Task’s exception, then when the Task goes away, the TaskScheduler.UnobservedTaskException gets raised, giving the application one more opportunity to “observe” the exception. And if the exception still remains unobserved, the exception escalation policy is then enabled by the exception going unhandled on the finalizer thread.

...exceptions indicate something has gone wrong, and crashing helps developers to immediately identify that the application has entered an unreliable state.

(Source: http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5.aspx)

In .NET, "unobserved" exceptions will bubble up and intentionally crash the application. This would be a useful feature to allow developers to be notified (via a crash) of an "unobserved" exception since it means their application has entered an unreliable state.

Since this would be a breaking change, it might be worthwhile to disable this feature by default and require it to be explicitly turned on by developers.

Basic Use of This Library in Android Studio

I have followed the instructions on the page regarding setting up the compile declarations in Android Studio. Whenever I try to use the sample code listed on the main Readme, all calls with findAsync, saveAsync, and many more are continually popping up with cannot resolve method.

Is there another dependency this library needs to run?
Is there another step to setup this other than the gradle commands?

Easier way to unit test background Tasks

This is a suggestion:
It'd be great to be able, in unit tests, to run all the tasks on the UI thread, even the ones that were set up to run on a background thread. This would make testing code with Tasks easier.

Right now, the solution to unit test some code where a background Task is to set up a custom background executor and change it during tests. See https://medium.com/@trionkidnapper/unit-testing-asynchronous-tasks-from-bolts-android-e780f02bf1be#88c7

The stacktrace of a wrapped AggregateException excludes the inner exceptions

Printing the stacktrace of a wrapped AggregateException does not include the inner exceptions:

Exception e1 = new RuntimeException("Failure 1");
Exception e2 = new RuntimeException("Failure 2");

Exception aggregate = new AggregateException(Arrays.asList(e1, e2));

aggregate.printStackTrace();
// bolts.AggregateException: There were multiple errors.
//     at bolts.TaskTest.testThing(TaskTest.java:1160)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
//     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
//     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
//     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
//     at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
//     at org.junit.rules.RunRules.evaluate(RunRules.java:20)
//     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
//     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
//     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
//     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
//     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
//     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
//     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
//     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
//     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
//     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
//     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
// Caused by: java.lang.RuntimeException: Failure 1
//     at bolts.TaskTest.testThing(TaskTest.java:1157)
//     ... 28 more
// 
//   Inner throwable #0: java.lang.RuntimeException: Failure 1
//     at bolts.TaskTest.testThing(TaskTest.java:1157)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
//     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
//     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
//     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
//     at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
//     at org.junit.rules.RunRules.evaluate(RunRules.java:20)
//     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
//     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
//     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
//     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
//     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
//     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
//     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
//     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
//     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
//     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
//     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
// 
// 
//   Inner throwable #1: java.lang.RuntimeException: Failure 2
//     at bolts.TaskTest.testThing(TaskTest.java:1158)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
//     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
//     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
//     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
//     at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
//     at org.junit.rules.RunRules.evaluate(RunRules.java:20)
//     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
//     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
//     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
//     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
//     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
//     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
//     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
//     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
//     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
//     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
//     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

new Exception(aggregate).printStackTrace();
// java.lang.Exception: bolts.AggregateException: There were multiple errors.
//     at bolts.TaskTest.testThing(TaskTest.java:1164)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
//     at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
//     at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
//     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
//     at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
//     at org.junit.rules.RunRules.evaluate(RunRules.java:20)
//     at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
//     at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
//     at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
//     at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
//     at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
//     at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
//     at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
//     at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
//     at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
//     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
//     at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
//     at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
//     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
//     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
//     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
//     at java.lang.reflect.Method.invoke(Method.java:606)
//     at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
// Caused by: bolts.AggregateException: There were multiple errors.
//     at bolts.TaskTest.testThing(TaskTest.java:1160)
//     ... 28 more
// Caused by: java.lang.RuntimeException: Failure 1
//     at bolts.TaskTest.testThing(TaskTest.java:1157)
//     ... 28 more

It seems like the proper way to handle this would be to utilize Throwable#addSuppressed(throwable) instead, but this requires android-19 :(

Parallel execution with different tasks types

In the example provided for parallel execution, all tasks are the same type. Is there a way to do this with different tasks types and check the results for those tasks on the continuation?

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comments");
query.whereEqualTo("post", 123);

findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Void>() {
  public Task<Void> then(List<ParseObject> results) throws Exception {
    // Collect one task for each delete into an array.
    ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
    for (ParseObject result : results) {
      // Start this delete immediately and add its task to the list.
      tasks.add(deleteAsync(result));
    }
    // Return a new task that will be marked as completed when all of the deletes are
    // finished.
    return Task.whenAll(tasks);
  }
}).onSuccess(new Continuation<Void, Void>() {
  public Void then(Void ignored) throws Exception {
    // Every comment was deleted.
    return null;
  }
});

com.parse.ParseException: bolts.ExecutorException: An exception was thrown by an Executor

Below is my code.
dependencies {
compile 'com.facebook.android:facebook-android-sdk:[4,5)'
compile 'com.parse:parse-android:1.15.7'
compile 'com.parse.bolts:bolts-tasks:1.4.0'
compile 'com.parse.bolts:bolts-applinks:1.4.0'
}
private void appLevel_Lang(final Context cntxt) {

    if (db == null) {
        db = new DataBaseHelper(cntxt);
    }
    try {
        final ParseQuery<ParseObject> query = ParseQuery.getQuery("appSupportedLanguages");
        query.setLimit(100);
      
        Date dbLastUpdatedDate = db.getLastUpdateDateOfTable("appSupportedLanguages");

        if (dbLastUpdatedDate != null) {
            query.whereGreaterThan("updatedAt", dbLastUpdatedDate);
        }
        query.orderByAscending("updatedAt");
        query.findInBackground(new FindCallback<ParseObject>() {
            @Override
            public void done(List<ParseObject> applvl_LangList, ParseException e) {

                if (e == null) {

                    if (applvl_LangList.size() > 0) {
                        String lastUpdatedDate = ParseQueries.getNSDateFormatterUpdateAtForParse().format(applvl_LangList.get(applvl_LangList.size() - 1).getUpdatedAt());

                        for (ParseObject p : applvl_LangList) {
                            AppLevelLanguage appLevelLanguage = new AppLevelLanguage();
                            appLevelLanguage.objectID = p.getObjectId();
                            appLevelLanguage.key = p.getString("key");
                            appLevelLanguage.updatedAt = lastUpdatedDate;
                            ArrayList<String> arrLangColNames = (ArrayList<String>) ParseConfig.getCurrentConfig().get("supportedLanguages");
                            for (String strLangCode : arrLangColNames) {
                                p.getString(strLangCode);
                                appLevelLanguage.langHashMap.put(strLangCode, p.getString(strLangCode));
                            }
                             db.insertOrUpdateAppSupportedLanguageTable(appLevelLanguage);
                        }
                    }
                    if (applvl_LangList.size() == query.getLimit()) {
                        appLevel_Lang(cntxt);
                    } else {
                        Log.d("", "AppSupportedLanguages is not equal to limit");
                    }

                } else {
                    Log.d("AppSupportedLanguages", "Error: " + e.getMessage());
                }
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

}

This is my new Project.The above similar code is running my older project.

Task.whenAll should return AggregateException on multiple cancelled task

Currently we set the whole Task.whenAll result as cancelled when one of the task is cancelled and there's no error https://github.com/BoltsFramework/Bolts-Android/blob/master/Bolts/src/main/java/bolts/Task.java#L474. This is incorrect since when we consider this scenario

  1. There're 4 Tasks in Task.whenAll.
  2. Two of them are cancelled, two of them succeed.
  3. Task.whenAll will return cancelled.

The correct result should be an AggregateException that contains 2 TaskCancelledException or something similar.

AppLinkTest#testWebViewAppLinkParsingFailure fails on android-19

It consistently fails on android-19, but not on android-21.

bolts.AppLinkTest > testWebViewAppLinkParsingFailure[Google Nexus 5 - 5.0.0 - API 21 - 1080x1920 - 5.0] SUCCESS 

bolts.AppLinkTest > testWebViewAppLinkParsingFailure[Motorola Moto X - 4.4.4 - API 19 - 720x1280 - 4.4.4] FAILED 
    junit.framework.AssertionFailedError
    at bolts.AppLinkTest.testWebViewAppLinkParsingFailure(AppLinkTest.java:230)

Full output: https://gist.github.com/grantland/7756822e6912f8021202

[Nativescript] Plugin Development Help Please

Hello! I just started getting into native app development using NativeScript and I'm attempting to develop a plugin for this repository.

Here are the TypeScript declarations file generated for Bolts-Android:
(Sorry, I would make a gist but there's no TypeScript syntax support 😭 )

/// <reference path="./java.io.PrintStream.d.ts" />
/// <reference path="./java.io.PrintWriter.d.ts" />
/// <reference path="./java.lang.String.d.ts" />
/// <reference path="./java.lang.Throwable.d.ts" />
/// <reference path="./java.util.List.d.ts" />
declare module bolts {
    export class AggregateException exports java.lang.Exception {
        public constructor();
        public constructor(param0: string);
        public constructor(param0: string, param1: java.lang.Throwable);
        public constructor(param0: java.lang.Throwable);
        public constructor(param0: string, param1: native.Array<java.lang.Throwable>);
        public constructor();
        public constructor(param0: string);
        public constructor(param0: string, param1: java.lang.Throwable);
        public constructor(param0: java.lang.Throwable);
        public constructor(param0: string, param1: java.util.List);
        public constructor();
        public constructor(param0: string);
        public constructor(param0: string, param1: java.lang.Throwable);
        public constructor(param0: java.lang.Throwable);
        public constructor(param0: java.util.List);
        public getInnerThrowables(): java.util.List;
        public printStackTrace(): void;
        public printStackTrace(param0: java.io.PrintWriter): void;
        public printStackTrace(param0: java.io.PrintStream): void;
        public printStackTrace(): void;
        public printStackTrace(param0: java.io.PrintStream): void;
        public printStackTrace(param0: java.io.PrintWriter): void;
        public getErrors(): java.util.List;
        public getCauses(): native.Array<java.lang.Throwable>;
    }
}

/// <reference path="./java.lang.Runnable.d.ts" />
/// <reference path="./java.util.concurrent.Executor.d.ts" />
/// <reference path="./java.util.concurrent.ExecutorService.d.ts" />
/// <reference path="./java.util.concurrent.ThreadFactory.d.ts" />
/// <reference path="./java.util.concurrent.ThreadPoolExecutor.d.ts" />
declare module bolts {
    export class AndroidExecutors exports java.lang.Object {
        public static newCachedThreadPool(): java.util.concurrent.ExecutorService;
        public static newCachedThreadPool(param0: java.util.concurrent.ThreadFactory): java.util.concurrent.ExecutorService;
        public static allowCoreThreadTimeout(param0: java.util.concurrent.ThreadPoolExecutor, param1: boolean): void;
        public static uiThread(): java.util.concurrent.Executor;
    }
    export module AndroidExecutors {
        export class UIThreadExecutor exports java.lang.Object {
            public execute(param0: java.lang.Runnable): void;
        }
    }
}

/// <reference path="./java.lang.Runnable.d.ts" />
/// <reference path="./java.util.concurrent.ExecutorService.d.ts" />
declare module bolts {
    export class BoltsExecutors exports java.lang.Object {
        public static background(): java.util.concurrent.ExecutorService;
    }
    export module BoltsExecutors {
        export class ImmediateExecutor exports java.lang.Object {
            public execute(param0: java.lang.Runnable): void;
        }
    }
}

/// <reference path="./bolts.CancellationTokenRegistration.d.ts" />
/// <reference path="./java.lang.Runnable.d.ts" />
declare module bolts {
    export class CancellationToken exports java.lang.Object {
        public isCancellationRequested(): boolean;
        public register(param0: java.lang.Runnable): bolts.CancellationTokenRegistration;
        public throwIfCancellationRequested(): void;
        public toString(): string;
    }
}

declare module bolts {
    export class CancellationTokenRegistration exports java.lang.Object {
        public close(): void;
    }
}

/// <reference path="./bolts.CancellationToken.d.ts" />
declare module bolts {
    export class CancellationTokenSource exports java.lang.Object {
        public constructor();
        public isCancellationRequested(): boolean;
        public getToken(): bolts.CancellationToken;
        public cancel(): void;
        public cancelAfter(param0: number): void;
        public close(): void;
        public toString(): string;
    }
}

/// <reference path="./java.lang.Object.d.ts" />
declare module bolts {
    export class Capture exports java.lang.Object {
        public constructor();
        public constructor();
        public constructor(param0: java.lang.Object);
        public get(): java.lang.Object;
        public set(param0: java.lang.Object): void;
    }
}

/// <reference path="./bolts.Task.d.ts" />
/// <reference path="./java.lang.Object.d.ts" />
declare module bolts {
    export class Continuation exports java.lang.Object {
        public then(param0: bolts.Task): java.lang.Object;
    }
}

/// <reference path="./java.lang.Exception.d.ts" />
/// <reference path="./java.lang.String.d.ts" />
/// <reference path="./java.lang.Throwable.d.ts" />
declare module bolts {
    export class ExecutorException exports java.lang.RuntimeException {
        public constructor();
        public constructor(param0: string);
        public constructor(param0: string, param1: java.lang.Throwable);
        public constructor(param0: java.lang.Throwable);
        public constructor(param0: java.lang.Exception);
    }
}

/// <reference path="./bolts.CancellationToken.d.ts" />
/// <reference path="./bolts.Continuation.d.ts" />
/// <reference path="./bolts.Task.d.ts" />
/// <reference path="./bolts.UnobservedTaskException.d.ts" />
/// <reference path="./java.lang.Exception.d.ts" />
/// <reference path="./java.lang.Object.d.ts" />
/// <reference path="./java.util.Collection.d.ts" />
/// <reference path="./java.util.concurrent.Callable.d.ts" />
/// <reference path="./java.util.concurrent.Executor.d.ts" />
/// <reference path="./java.util.concurrent.ExecutorService.d.ts" />
/// <reference path="./java.util.concurrent.TimeUnit.d.ts" />
declare module bolts {
    export class Task exports java.lang.Object {
        public static getUnobservedExceptionHandler(): bolts.Task.UnobservedExceptionHandler;
        public static setUnobservedExceptionHandler(param0: bolts.Task.UnobservedExceptionHandler): void;
        public static create(): bolts.Task.TaskCompletionSource;
        public isCompleted(): boolean;
        public isCancelled(): boolean;
        public isFaulted(): boolean;
        public getResult(): java.lang.Object;
        public getError(): java.lang.Exception;
        public waitForCompletion(): void;
        public waitForCompletion(param0: number, param1: java.util.concurrent.TimeUnit): boolean;
        public static forResult(param0: java.lang.Object): bolts.Task;
        public static forError(param0: java.lang.Exception): bolts.Task;
        public static cancelled(): bolts.Task;
        public static delay(param0: number): bolts.Task;
        public static delay(param0: number, param1: bolts.CancellationToken): bolts.Task;
        public cast(): bolts.Task;
        public makeVoid(): bolts.Task;
        public static callInBackground(param0: java.util.concurrent.Callable): bolts.Task;
        public static callInBackground(param0: java.util.concurrent.Callable, param1: bolts.CancellationToken): bolts.Task;
        public static call(param0: java.util.concurrent.Callable, param1: java.util.concurrent.Executor): bolts.Task;
        public static call(param0: java.util.concurrent.Callable, param1: java.util.concurrent.Executor, param2: bolts.CancellationToken): bolts.Task;
        public static call(param0: java.util.concurrent.Callable): bolts.Task;
        public static call(param0: java.util.concurrent.Callable, param1: bolts.CancellationToken): bolts.Task;
        public static whenAnyResult(param0: java.util.Collection): bolts.Task;
        public static whenAny(param0: java.util.Collection): bolts.Task;
        public static whenAllResult(param0: java.util.Collection): bolts.Task;
        public static whenAll(param0: java.util.Collection): bolts.Task;
        public continueWhile(param0: java.util.concurrent.Callable, param1: bolts.Continuation): bolts.Task;
        public continueWhile(param0: java.util.concurrent.Callable, param1: bolts.Continuation, param2: bolts.CancellationToken): bolts.Task;
        public continueWhile(param0: java.util.concurrent.Callable, param1: bolts.Continuation, param2: java.util.concurrent.Executor): bolts.Task;
        public continueWhile(param0: java.util.concurrent.Callable, param1: bolts.Continuation, param2: java.util.concurrent.Executor, param3: bolts.CancellationToken): bolts.Task;
        public continueWith(param0: bolts.Continuation, param1: java.util.concurrent.Executor): bolts.Task;
        public continueWith(param0: bolts.Continuation, param1: java.util.concurrent.Executor, param2: bolts.CancellationToken): bolts.Task;
        public continueWith(param0: bolts.Continuation): bolts.Task;
        public continueWith(param0: bolts.Continuation, param1: bolts.CancellationToken): bolts.Task;
        public continueWithTask(param0: bolts.Continuation, param1: java.util.concurrent.Executor): bolts.Task;
        public continueWithTask(param0: bolts.Continuation, param1: java.util.concurrent.Executor, param2: bolts.CancellationToken): bolts.Task;
        public continueWithTask(param0: bolts.Continuation): bolts.Task;
        public continueWithTask(param0: bolts.Continuation, param1: bolts.CancellationToken): bolts.Task;
        public onSuccess(param0: bolts.Continuation, param1: java.util.concurrent.Executor): bolts.Task;
        public onSuccess(param0: bolts.Continuation, param1: java.util.concurrent.Executor, param2: bolts.CancellationToken): bolts.Task;
        public onSuccess(param0: bolts.Continuation): bolts.Task;
        public onSuccess(param0: bolts.Continuation, param1: bolts.CancellationToken): bolts.Task;
        public onSuccessTask(param0: bolts.Continuation, param1: java.util.concurrent.Executor): bolts.Task;
        public onSuccessTask(param0: bolts.Continuation, param1: java.util.concurrent.Executor, param2: bolts.CancellationToken): bolts.Task;
        public onSuccessTask(param0: bolts.Continuation): bolts.Task;
        public onSuccessTask(param0: bolts.Continuation, param1: bolts.CancellationToken): bolts.Task;
        public static BACKGROUND_EXECUTOR: java.util.concurrent.ExecutorService;
        public static UI_THREAD_EXECUTOR: java.util.concurrent.Executor;
    }
    export module Task {
        export class TaskCompletionSource exports bolts.TaskCompletionSource {
        }
        export class UnobservedExceptionHandler exports java.lang.Object {
            public unobservedException(param0: bolts.Task, param1: bolts.UnobservedTaskException): void;
        }
    }
}

/// <reference path="./bolts.Task.d.ts" />
/// <reference path="./java.lang.Exception.d.ts" />
/// <reference path="./java.lang.Object.d.ts" />
declare module bolts {
    export class TaskCompletionSource exports java.lang.Object {
        public constructor();
        public getTask(): bolts.Task;
        public trySetCancelled(): boolean;
        public trySetResult(param0: java.lang.Object): boolean;
        public trySetError(param0: java.lang.Exception): boolean;
        public setCancelled(): void;
        public setResult(param0: java.lang.Object): void;
        public setError(param0: java.lang.Exception): void;
    }
}

/// <reference path="./bolts.Task.d.ts" />
declare module bolts {
    export class UnobservedErrorNotifier exports java.lang.Object {
        public constructor();
        public constructor(param0: bolts.Task);
        public finalize(): void;
        public setObserved(): void;
    }
}

/// <reference path="./java.lang.String.d.ts" />
/// <reference path="./java.lang.Throwable.d.ts" />
declare module bolts {
    export class UnobservedTaskException exports java.lang.RuntimeException {
        public constructor();
        public constructor(param0: string);
        public constructor(param0: string, param1: java.lang.Throwable);
        public constructor(param0: java.lang.Throwable);
    }
}

I'm following the README.md in an attempt to create a Task on a background thread. In the part on creating-async-methods, what class is the new GetCallback() method in?

My basic Task looks like this:

doSomethingAsync() {
    return bolts.Task.call(new bolts.Continuation(class {
        done(t) {
            console.dump(t);
            return t
        }
    }), bolts.Task.BACKGROUND_EXECUTOR)
}

But I get an error, could not resolve method bolts.Task.call.

Does anyone have an experience with TypeScript that can point me in the right direction?

Application context

Hi all, but if initialized from application context, the uri web intent not start.
Add Flag intent for new task?

AppLink never navigated, Task never returns

W/art﹕ Attempt to remove local SIRT entry from IRT, ignoring
W/art﹕ Attempt to remove local SIRT entry from IRT, ignoring
W/art﹕ Attempt to remove local SIRT entry from IRT, ignoring
W/art﹕ Thread[67,tid=25805,Native,Thread*=0x4f3ef2d8,peer=0x651ab6f8,"Thread-19480"] attached without supplying a name
I/chromium﹕ [INFO:CONSOLE(1)] "Uncaught TypeError: Object [object Object] has no method 'setValue'", source:  (1)

Ultimately resolver#getAppLinkFromUrlInBackground(Uri) gives these logs and the task never returns. Not with an exception, no nothing.

Exceptions

Hi, do you have any suggestions on how to manage exceptions? I end up getting very short stacktraces that start in Bolts code, and I lose all my context.

What i've started doing is adding ContinueWith calls that look for exceptions, and wrap them.

Progress in whenAll() and whenAllResult()

HELLo moto ;)

Is there any possibility to have a "progress listener" to completion of whenAll() and whenAllResult() methods? For now we have only onSuccess(new Continuation<TTaskResult, TContinuationResult>()) listener that is indicated when all tasks are completed. But I want to show progress indicator to user and can't get the idea how to do that.

edit:
Ok, we can do that in that way

tasksList.add(
    Task.callInBackground(() -> {
        // do something extra heavy
        return someDoubleResult;
    }));
...

final Capture<Integer> currentProgress = new Capture<>(0);
final int progressCount = tasksList.size();

for (Task<Double> taskWithFinish : tasksList) {
    taskWithFinish.onSuccess(task -> {
        currentProgress.set(currentProgress.get() + 1);
        updateProgress((float) currentProgress.get() / progressCount);
        return task.getResult();
    });
}

Task.whenAllResult(tasksList)
    .onSuccess(new Continuation<List<Double>, Void>() {
        @Override
        public Void then(Task<List<Double>> task) throws Exception {
            // close progress indicator
            return null;
        }
    }, Task.UI_THREAD_EXECUTOR);

Will be nice to do that in simpler way, eg.

tasksList.add(
    Task.callInBackground(() -> {
        // do something extra heavy
        return someDoubleResult;
    }));
...

Task.whenAllResult(tasksList)
    .onProgress(new Progress<Double>() {
        @Override
        public void onProgress(Task<Double> task, int finishedTaskNr, int tasksSumCount) {
            updateProgressBar((float) finishedTaskNr /  tasksSumCount);
        }
    })
    .onSuccess(new Continuation<List<Double>, Void>() {
        @Override
        public Void then(Task<List<Double>> task) throws Exception {
            // close progress indicator
            return null;
        }
    }, Task.UI_THREAD_EXECUTOR);

Getting OOM

Fatal Exception: java.lang.OutOfMemoryError: Could not allocate JNI Env at java.lang.Thread.nativeCreate(Thread.java) at java.lang.Thread.start(Thread.java:731) at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:941) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1348) at bolts.Task.call(SourceFile:348) at bolts.Task.callInBackground(SourceFile:321) at com.touchtalent.bobbleapp.services.BobbleKeyboard.refreshStickerSuggestionCache(SourceFile:1122) at com.touchtalent.bobbleapp.services.BobbleKeyboard.onCodeInput(SourceFile:2629) at com.android.inputmethod.keyboard.PointerTracker.callListenerOnCodeInput(SourceFile:423) at com.android.inputmethod.keyboard.PointerTracker.detectAndSendKey(SourceFile:1421) at com.android.inputmethod.keyboard.PointerTracker.onUpEventInternal(SourceFile:1294) at com.android.inputmethod.keyboard.PointerTracker.onUpEvent(SourceFile:1234) at com.android.inputmethod.keyboard.PointerTracker.processMotionEvent(SourceFile:778) at com.android.inputmethod.keyboard.MainKeyboardView.processMotionEvent(SourceFile:845) at com.android.inputmethod.keyboard.MainKeyboardView.onTouchEvent(SourceFile:832) at android.view.View.dispatchTouchEvent(View.java:10723) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2865) at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2550) at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:559) at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1870) at android.app.Dialog.dispatchTouchEvent(Dialog.java:1057) at android.inputmethodservice.SoftInputWindow.dispatchTouchEvent(SoftInputWindow.java:147) at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:521) at android.view.View.dispatchPointerEvent(View.java:10952) at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5117) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4969) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4500) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4553) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4519) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4652) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4527) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4709) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4500) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4553) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4519) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4527) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4500) at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7007) at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:6936) at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:6897) at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7117) at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185) at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(InputEventReceiver.java) at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:176) at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7081) at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7144) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:927) at android.view.Choreographer.doCallbacks(Choreographer.java:702) at android.view.Choreographer.doFrame(Choreographer.java:632) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:913) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6682) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.