在新公司的项目中,是由bolts框架来搭建网络请求的,由于之前没有接触过这个框架,因此只能从头看起(其实看下来相对来说我更喜欢用RxJava+Retrofit搭建apps的请求层,but,whatever...),下面的内容很多都是在网上看完资料后自己写Demo来验证获得的,部分是翻译GitHub的Bolts框架的说明文档,也有部分是我自己在网上的教程的基础上自己验证后的代码,如有雷同,纯属巧合。
1、简介
Bolts是一个用于简化移动app开发的轻量级函数库集合,是由Parse和Facebook编写来进行内部使用的,后来他们决定开源,提供给广大开发者使用。“Tasks”,使复杂的异步代码结构变得易于管理。“Task”的作用在Android和ios中就像是JavaScript的Promise。
2、下载
从这里下载jar包或者在Gradle中进行编译。
dependencies { compile 'com.parse.bolts:bolts-tasks:1.4.0' compile 'com.parse.bolts:bolts-applinks:1.4.0' }
3、Tasks
为了构建一个响应迅速的Android应用,你不能在UI线程中运行任何的耗时操作,并且要小心避免阻塞UI线程,这意味着你需要在后台中执行各种操作。为了让这一过程变得更简单,我们增加了一个类叫做“Task”。一个Task代表一个异步操作。Task用于继续处理操作返回的结果,是由异步操作返回的。当一个Task被函数返回,它已经开始完成它的任务了。Task不与特定的线程模型进行绑定:它代表的是一个操作被完成,而不是一个操作正在被执行。Task与其他异步方法(例如AsyncTask)相比有许多优势。
(1) Task占用更少的系统资源,因为Task在等待其他Tasks的时候不占用线程
(2) 执行一系列Task的时候不需要像你使用CallBack时一样写出金字塔式的嵌套代码。
(3) Task是可以组合的,允许你执行分支、并行和复合型的错误处理,不需要用到嵌套的代码和各种复杂命名的CallBack.
(4) 你可以有序的整理你基于任务的代码并执行它们,而不是将你的逻辑分散在凌乱的回调函数中。
4、调用
我们知道,Bolts的核心是异步,那么与RxJava类似,我们可以决定我们的代码应该运行在UI线程,还是当前线程,还是后台线程,因此Task类中定义了几种线程池,方便我们在使用这个框架的时候作为一个参数传进去实现在不同的线程中调用我们的代码,这几种线程池代码如下:
/** * An {@link java.util.concurrent.Executor} that executes tasks in parallel. */ public static final ExecutorService BACKGROUND_EXECUTOR = BoltsExecutors.background(); /** * An {@link java.util.concurrent.Executor} that executes tasks in the current thread unless * the stack runs too deep, at which point it will delegate to {@link Task#BACKGROUND_EXECUTOR} in * order to trim the stack. */ private static final Executor IMMEDIATE_EXECUTOR = BoltsExecutors.immediate(); /** * An {@link java.util.concurrent.Executor} that executes tasks on the UI thread. */ public static final Executor UI_THREAD_EXECUTOR = AndroidExecutors.uiThread();
BACKGROUND_EXECUTOR 是运行在后台线程。
IMMEDIATE_EXECUTOR按照我的理解是运行在当前线程(这个我不确定,因为没用到过因此没有测试,但是应该没错)
UI_THREAD_EXECUTOR 是运行在UI线程(即主线程)
了解了这几个线程池的差别后,我们就可以开始使用Bolts来运行我们的代码了,下面的代码中的getImageFromWeb()方法是一个从网上下载一张图片并保存在全局变量bitmap中的方法,具体实现忽略。
(1)运行一个单个任务
Task.call(new Callable<Bitmap>() { @Override public Bitmap call() throws Exception {//这里可以返回空值,把返回类型设为Void即可,返回其他类型的用法将在下面提到 //你要执行的代码 Bitmap bitmap = getImageFromWeb(); return <span style="font-family: Arial, Helvetica, sans-serif;">bitmap</span><span style="font-family: Arial, Helvetica, sans-serif;">; //这里返回的bitmap将在下面讲到的任务执行结果中</span> } }, Task.BACKGROUND_EXECUTOR);//这里的参数可以参考上面填入别的线程池,控制这段代码在哪个线程运行
(2)“continueWith”方法
每一个Task都有一个方法叫做continueWith并且它带有一个参数Continuation。Continuation是一个你需要实现的接口,你需要实现它的方法then。Then方法会在Task完成后被调用,你可以通过这个方法来检查Task是否成功以及获得它的结果。
Task.call(new Callable<Bitmap>() { @Override public Bitmap call() throws Exception {//这里可以返回空值,把返回类型设为Void即可,返回其他类型的用法将在下面提到 //你要执行的代码 Bitmap bitmap = getImageFromWeb(); return bitmap; //这里返回的bitmap将在下面讲到的任务执行结果处理中 } }, Task.BACKGROUND_EXECUTOR)//在后台线程中运行 .continueWith(new Continuation<Bitmap, Void>() { @Override public Void then(Task<Bitmap> task) throws Exception { // set ui if (task.isFaulted()) { Exception exc = task.getError();//图片加载失败,进行错误处理 } else if (task.isCompleted()) { //图片加载成功 setImage(task.getResult());//task.getResult()是获取上面执行任务成功后返回结果的方法 } return null; } }, Task.UI_THREAD_EXECUTOR);//在UI线程中运行
(3)如果我们只需要关心上一个任务执行成功才要进行处理,那么我们可以用.onSuccess方法来代替.continueWith方法,具体用法如下:
Task.call(new Callable<Bitmap>() { @Override public Bitmap call() throws Exception {//需要返回一个Bitmap //你要执行的代码 Bitmap bitmap = getImageFromWeb(); return bitmap; //这里返回的bitmap将在下面讲到的任务执行结果中 } }, Task.BACKGROUND_EXECUTOR)//后台线程运行 .onSuccess(new Continuation<Bitmap, Object>() { @Override public Void then(Task<Bitmap> task) throws Exception { //图片加载成功 setImage(task.getResult());//将图片显示出来 return null; } }, Task.UI_THREAD_EXECUTOR);//主线程运行
(4)在知道如何使用上面的两种方法之后,我们就可以运用它们来执行一个顺序任务了,下面的示例代码中需要执行三个任务,执行任务1成功后,才会执行任务2,而任务2不论成功失败都会在任务3中进行处理。
Task.call(new Callable<Bitmap>() { @Override public Bitmap call() throws Exception { //你要执行的代码,任务1 Bitmap bitmap = getImageFromWeb(); return bitmap; //这里返回的bitmap将在下面讲到的任务执行结果中 } }, Task.BACKGROUND_EXECUTOR)//后台线程运行 .onSuccess(new Continuation<Bitmap, Bitmap>() { @Override public Bitmap then(Task<Bitmap> task) throws Exception { //do something,任务2 Bitmap b = task.getResult(); return b; } }, Task.BACKGROUND_EXECUTOR) //后台线程运行 .continueWith(new Continuation<Bitmap, Void>() { @Override public Void then(Task<Bitmap> task) throws Exception { // 任务3 if (task.isFaulted()) { //图片加载失败,进行错误处理 } else if (task.isCompleted()) { //图片加载成功 setImage(task.getResult()); } return null; } }, Task.UI_THREAD_EXECUTOR);//主线程运行
(5)除了顺序执行任务,我们还可以并行执行任务,下面的代码中,任务1创建了任务2和任务3用于并列执行,当两个任务都执行结束的时候会调用任务4进行结果处理。
Task.call(new Callable<List<Task<Bitmap>>>() { @Override public List<Task<Bitmap>> call() throws Exception { List<Task<Bitmap>> tasks = new ArrayList<Task<Bitmap>>(); Task<Bitmap> task1 = Task.call(new Callable<Bitmap>() { @Override public Bitmap call() throws Exception { // 任务2 return getImageFromWeb(); } }, Task.BACKGROUND_EXECUTOR); Task<Bitmap> task2 = Task.call(new Callable<Bitmap>() { @Override public Bitmap call() throws Exception { // 任务3 return getImageFromWeb(); } }, Task.BACKGROUND_EXECUTOR); tasks.add(task1); tasks.add(task2); Task.whenAll(tasks).waitForCompletion(); return tasks; //这里返回的 } }, Task.BACKGROUND_EXECUTOR)//后台线程运行 .continueWith(new Continuation<List<Task<Bitmap>>, Void>() { @Override public Void then(Task<List<Task<Bitmap>>> task) throws Exception { // 任务4 if (task.isFaulted()) { //进行错误处理 Exception exception = task.getError(); } else if (task.isCompleted()) { //取出两个并列执行的Task,获取执行结束的返回值 for(Task<Bitmap> t:task.getResult()){ if(t.isCompleted()){ //task执行成功 setImage(t.getResult()); } } } return null; } }, Task.UI_THREAD_EXECUTOR);//主线程运行
(6)task执行continueWith之前还可以进行延时,只需要在调用continueWith之前调用.delay(time)即可。