Bolts

Bolts下载Tasks continueWith 方法链式任务错误处理创建任务创建异步方法顺序任务并行任务Task Executors捕获变量取消Task App Links 处理App Link导航到一个url 添加应用和导航数据解决应用程序链接元数据 分析 监听App链接测量事件应用链接事件字段

Github查看 英文版

Bolts是一个为了方便移动APP开发而设计的低级库集合。Bolts由Parse和Facebook设计用来我们内部使用的,现在我们决定开源这些库让其他人也可以用。使用这些库不需要添加使用任何Parse的服务,也不需要拥有一个Parse或者Facebook的开发者账号。

Bolts包含:

    “Tasks”,它使得复杂的异步代码更易于管理。一个任务类似于JavaScript的Promise,但是它可以用于ios和Android。一个App Links protocol的实现,帮助您链接到其他应用程序中的内容,并处理传入的多层链接。

想了解更多信息,请参考Bolts Android API Reference。

下载

下载最新的jar包或者在Gradle中定义:

dependencies {
  compile 'com.parse.bolts:bolts-tasks:1.4.0'
  compile 'com.parse.bolts:bolts-applinks:1.4.0'
}
snapshots Tasks TaskTaskTaskTaskTaskTaskcallbacksAsyncTaskTasks Taskcallbacks金字塔Task意大利面条 saveAsyncfindAsyncTask continueWith TaskcontinueWithContinuationContinuationthenthenTaskthen
saveAsync(obj).continueWith(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    if (task.isCancelled()) {
      // 取消了保存
    } else if (task.isFaulted()) {
      // 保存失败
      Exception error = task.getError();
    } else {
      // 保存成功
      ParseObject object = task.getResult();
    }
    return null;
  }
});
Task
/**
 异步获取一个字符串。
 */
public Task<String> getStringAsync() {
  //我们假设getIntAsync()方法返回一个Task<Integer>。
  return getIntAsync().continueWith(
    //这个Continuation接收一个Integer作为输入,提供一个String作为输出。
    //它必须接收一个Integer因为这是上一个Task的返回值。
    new Continuation<Integer, String>() {
      //为了方便将getIntAsync()返回的Task传递给 "then"。
      public String then(Task<Integer> task) throws Exception {
        Integer number = task.getResult();
        return String.format("%d", Locale.US, number);
      }
    }
  );
}
onSuccesscontinueWith
saveAsync(obj).onSuccess(new Continuation<ParseObject, Void>() {
  public Void then(Task<ParseObject> task) throws Exception {
    // 对象保存成功
    return null;
  }
});

链式任务

TaskcontinueWithTaskcontinueWithTaskcontinueWithTaskTaskcontinueWithTaskTask金字塔onSuccessTaskonSuccessTaskcontinueWithonSuccesscontinueWithTaskonSuccessTask
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 {
    // 所有任务完成
    return null;
  }
});

错误处理

continueWithonSuccesscontinueWithTask
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);
    // 强制把回调变成异常。
    throw new RuntimeException("There was an error.");
  }
}).onSuccessTask(new Continuation<ParseObject, Task<List<ParseObject>>>() {
  public Task<List<ParseObject>> then(Task<ParseObject> task) throws Exception {
    // 跳过这个 continuation。
    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()) {
      //错误处理器将被调用。
      //这个异常会变成 "There was an error"。
      //我们通过返回一个新的值来处理异常
      //这个任务完成时的返回值是null。
      return null;
    }

    // 这里也会跳过
    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 {
    //所有的操作都完成啦!这里被调用了。
    //这个任务的结果是null
    return null;
  }
});

在一串成功回调的最后加一个错误处理器,这样比较方便。

创建任务

findAsyncsaveAsyncTaskTaskTaskCompletionSourceTaskTasksetResultsetErrorsetCancelled
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();
}
TaskTask
Task<String> successful = Task.forResult("The good result.");

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

创建异步方法

TaskfetchAsync
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();
}
saveAsyncfindAsyncdeleteAsyncTaskcallInBackgroundTaskcall
Task.callInBackground(new Callable<Void>() {
  public Void call() {
    // 做一堆东西。
  }
}).continueWith(...);

顺序任务

当你想要按顺序执行一系列的异步操作时,Task是非常方便的,每个操作都会等待上一个操作完成。比如说,想象你要删除所有你博客中的评论。

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 {
    //创建一个简单的已完成task作为基础案例。
    Task<Void> task = Task.forResult(null);
    for (final ParseObject result : results) {
      //对于每个item,扩展一个带删除item方法的task。
      task = task.continueWithTask(new Continuation<Void, Task<Void>>() {
        public Task<Void> then(Task<Void> ignored) throws Exception {
          //返回一个task,当删除完成时task会被标记为结束
          return deleteAsync(result);
        }
      });
    }
    return task;
  }
}).continueWith(new Continuation<Void, Void>() {
  public Void then(Task<Void> ignored) throws Exception {
    //所有评论都删除了
    return null;
  }
});

并行任务

whenAllTaskTask.whenAllTaskTaskTaskTaskTask
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 {
    // 将每个删除的任务收集到数组中。
    ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
    for (ParseObject result : results) {
      //立即开始删除并且将它的task添加到列表中
      tasks.add(deleteAsync(result));
    }
    // 返回一个新的task,当所有删除操作完成后这个task会被标记为完成
    return Task.whenAll(tasks);
  }
}).onSuccess(new Continuation<Void, Void>() {
  public Void then(Task<Void> ignored) throws Exception {
    // 所有的评论都删除了
    return null;
  }
});

Task Executors

continueWithonSuccessjava.util.concurrent.ExecutorExecutorTask.call()CallableTask.callInBackground
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 {
    //工作被指定在NETWORK_EXECUTOR执行
    return client.execute(request);
  }
}, NETWORK_EXECUTOR).continueWithTask(new Continuation<HttpResponse, Task<byte[]>>() {
  @Override
  public Task<byte[]> then(Task<HttpResponse> task) throws Exception {
    //由于没有指定executor,这里继续在NETWORK_EXECUTOR上执行
    return processResponseAsync(response);
  }
}).continueWithTask(new Continuation<byte[], Task<Void>>() {
  @Override
  public Task<Void> then(Task<byte[]> task) throws Exception {
    //我们不想让磁盘读写阻塞NETWORK_EXECUTOR,所有指定使用DISK_EXECUTOR
    return writeToDiskAsync(task.getResult());
  }
}, DISK_EXECUTOR);
Task.UI_THREAD_EXECUTORTask.BACKGROUND_EXECUTOR
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);

捕获变量

finalCapturegetset
//在Task的回调中捕获一个变量
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中有保存成功的数量
    return successfulSaveCount.get();
  }
});

取消Task

TaskCancellationTokenSourceCancellationTokenSourceTaskCancellationTokenSourcecancel()Task
CancellationTokenSource cts = new CancellationTokenSource();

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

cts.cancel();
CancellationTokenisCancellationRequested()
/**
 异步获取`Integer`
 */
public Task<Integer> getIntAsync(final CancellationToken ct) {
  // 创建一个任务
  final TaskCompletionSource<Integer> tcs = new TaskCompletionSource<>();

  new Thread() {
    @Override
    public void run() {
      // 在开始时检查是否取消
      if (ct.isCancellationRequested()) {
        tcs.setCancelled();
        return;
      }

      int result = 0;
      while (result < 100) {
        // 在一个循环中轮询isCancellationRequested
        if (ct.isCancellationRequested()) {
          tcs.setCancelled();
          return;
        }
        result++;
      }
      tcs.setResult(result);
    }
  }.start();

  return tcs.getTask();
}
App Links

App Links 提供了一个跨平台的机制让开发者可以为他们的内容定义和发布一个多层链接的scheme,允许其他APP直接连接。不管你是构建一个接收传入链接的还是可能链接到其他app内容的app,Bolts都会提供工具来简化App链接协议的实现。

处理App Link

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

  //在某种程度上你的AndroidMainifest.xml中的intent filter可能已经被path过滤了一遍

  // 使用App Link中的target URL 来定位内容。
  Uri targetUrl = AppLinks.getTargetUrlFromInboundIntent(getIntent());
  if (targetUrl != null) {
    //这是被app link启动的activity。

    //targetUrl是外部共享的URL。在大多数情况下,你会在数据中嵌入你的内容标识符

    //如果你需要访问从你的网站或者app中传到meta标签中的数据,可以在AppLinkData中拿到
    Bundle applinkData = AppLinks.getAppLinkData(getIntent());
    String id = applinkData.getString("id");

    //你也可以从AppLinkData中得到referrer data
    Bundle referrerAppData = applinkData.getBundle("referer_app_link");

    //Apps也可以很轻松的检查App Link中的Extras。
    Bundle extras = AppLinks.getAppLinkExtras(getIntent());
    String fbAccessToken = extras.getString("fb_access_token");
  } else {
    //不是app链接,你已有的代码放这里
  }
}

导航到一个url

通过App Link,你的应用程序可以在用户导航到链接时提供最佳用户体验(由接收应用定义)。 Bolts简化了这个过程,自动执行跟踪链接所需的步骤:

    在指定的URL从HTML获取应用链接元数据解析的应用链接逐步执行与正在使用的设备相关的App Link targets,检查设备上是否存在可以处理targets的应用如果应用程序存在,请使用指定的al_applink_data构建“Intent”,并导航到“Intent”否则,请使用指定的原始网址打开浏览器

在最简单的情况下,只需一行代码即可导航到可能具有App Link的URL:

AppLinkNavigation.navigateInBackground(getContext(), url);

添加应用和导航数据

在大多数情况下,导航时需要传递给应用程序的数据将包含在URL本身中,因此无论应用程序是否安装在设备上,用户都将看到正确的内容。 然而有时候应用程序会传送与应用间导航相关的资料,或是想利用应用程序使用的信息来修改App链接协议,以调整应用程序的行为(比如一个显示回引用应用程序的链接)。

如果你想要充分利用这些特性,你可以拆分导航过程。 首先,你必须拥有您要导航的应用程序链接:

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;
      }
    });

然后,你可以使用你想导航的附加数据构建一个APP Link请求:

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();

解决应用程序链接元数据

Bolts允许自定义App Link方案,可以用作性能优化(例如缓存元数据)或作为允许开发人员使用集中式索引来获得App Link元数据的机制。 一个自定义的App Link解析器只需要能够获取URL并返回一个包含适用于此设备的AppLink.Target的有序列表的AppLink。 Bolts创造性地提供了其中之一,使用隐藏的“WebView”在设备上执行此方案。

AppLinkNavigationAppLinkResolver
AppLinkNavigation.navigateInBackground(url, resolver);

或者,你可以使用内置API替换掉默认的解析器:

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

分析

Bolts介绍测量事件。 应用程序链接将两个Measurement Events广播到应用程序,该应用程序可以捕获并与应用程序中的现有分析组件集成。(启用Analytics需要Android Support Library v4)

al_nav_outal_nav_inIntent

监听App链接测量事件

还有其他分析工具与Bolts的应用程序链接事件相集成,但你也可以自己收听这些事件:

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");
          // 继承你自己的 日志/分子 组件
        }
      }
    },
    new IntentFilter(MeasurementEvent.MEASUREMENT_EVENT_NOTIFICATION_NAME)
);

应用链接事件字段

应用程序链接测量事件以字符串键值对形式从应用程序链接Intent中发送附加信息。 下面是一些对两个事件的有用字段:

al_nav_ininputURLinputURLSchemeinputURLrefererURLal_applink_datareferer_app_linkrefererAppNameal_applink_datareferer_app_linksourceApplicationtargetURLal_applink_datatarget_urlversional_nav_outoutputURLoutputURLSchemeoutputURLsourceURLsourceURLHostsuccess“0”type“app”“web”“fail”version

欢迎关注我的公众号,三七文档库: