Android级连异步任务——Bolts-Android使用及原理

Parse开源了一个面向iOS和Android的底层库集合,统称为Bolts。根据Parse的公告,Bolts是Parse和Facebook共同努力将两家公司各自独立开发的小型底层工具类合并的结果。Bolts-Android为一个级联的异步任务。

Bolts-Android github地址:
https://github.com/BoltsFramework/Bolts-Android

本文案例的 github地址:
https://github.com/AndroidHighQualityCodeStudy/Android_Task_ThirdCode_Bolts

一、Bolts 使用

  • 任务抛到某个线程执行:当前线程、后台线程、UI线程执行任务
  • 任务级联执行:UI线程、后台线程、UI线程执行任务
  • 任务并行执行:Task.whenAll 多任务并行执行
  • 自定义任务线程池
  • 延时执行任务
  • 取消执行的任务

1.1 任务抛到某个线程执行

任务切换到当前线程后台线程UI线程执行。

  • 后台线程:网络请求、DB请求;
  • UI线程:主线程或UI更新相关操作
// 第1部分:当前线程、后台线程、UI线程运行任务
// 
// 运行在当前线程中
public Task<Boolean> runOnCurrentThread() {
   //
   return Task.call(new Callable<Boolean>() {
       @Override
       public Boolean call() throws Exception {
           // Current_Thread
           return true;
       }
   });
}

// 运行在后台线程
public Task<Boolean> runOnBackgroundThread() {
   //
   return Task.call(new Callable<Boolean>() {
       @Override
       public Boolean call() throws Exception {
           // Background_Thread
           return true;
       }
   }, Task.BACKGROUND_EXECUTOR);
}

// 运行在UI线程中
public Task<Boolean> runOnUIThread() {
   //
   return Task.call(new Callable<Boolean>() {
       @Override
       public Boolean call() throws Exception {
           // UI_Thread
           return true;
       }
   }, Task.UI_THREAD_EXECUTOR);
}

1.2 任务级联执行

任务先在UI线程执行,然后返回结果切换到后台线程执行,最后再次切换到UI线程执行。

// 第2部分:UI线程、后台线程、UI线程运行任务
// 
// 任务顺序执行
public Task synchronousTask(CancellationToken cancellationToken) {
   return Task.call(new Callable<Void>() {
       @Override
       public Void call() throws Exception {
           // UI_Thread
           LogUtils.d(TAG, "---1 UI_Thread---");
           return null;
       }
   }, Task.UI_THREAD_EXECUTOR, cancellationToken).onSuccess(new Continuation<Void, Boolean>() {
       @Override
       public Boolean then(Task<Void> task) throws Exception {
           // Background_Thread
           LogUtils.d(TAG, "---2 Background_Thread---");
           return true;
       }
   }, Task.BACKGROUND_EXECUTOR).continueWith(new Continuation<Boolean, Void>() {
       @Override
       public Void then(Task<Boolean> task) throws Exception {
           // UI_Thread
           LogUtils.d(TAG, "---3 UI_Thread---");
           return null;
       }
   }, Task.UI_THREAD_EXECUTOR);
}

1.3 任务并行执行

Task.whenAll 多任务并行执行举例:


//多任务并行
public void whenAll() {
   ArrayList<Task<Void>> tasks = new ArrayList<Task<Void>>();
   for (int i = 0; i < 3; i++) {
       final int index = i;
       // Start this delete immediately and add its task to the list.
       tasks.add(Task.call(new Callable<Void>() {
           @Override
           public Void call() throws Exception {
               // UI_Thread
               LogUtils.d(TAG, "---###########################---");
               LogUtils.d(TAG, "index: " + index);
               return null;
           }
       }, Task.BACKGROUND_EXECUTOR));
   }

   Task.whenAll(tasks);
}

1.4 自定义任务线程池

我们除了有 Task.BACKGROUND_EXECUTOR 线程池,还可以自定义执行任务的线程池,例如下文自定的 NETWORK_EXECUTOR(只处理网络请求)DISK_EXECUTOR(只处理Sdcard的读写)等线程池。

// 自定义线程池
static final Executor NETWORK_EXECUTOR = Executors.newCachedThreadPool();
static final Executor DISK_EXECUTOR = Executors.newCachedThreadPool();

//
public void changeThreadPool() {
   Task.call(new Callable<Boolean>() {
       @Override
       public Boolean call() throws Exception {
           // NETWORK_Thread
           return null;
       }
   }, NETWORK_EXECUTOR).continueWith(new Continuation<Boolean, String>() {
       @Override
       public String then(Task<Boolean> task) throws Exception {
           // NETWORK_Thread
           return null;
       }
   }).continueWith(new Continuation<String, Integer>() {
       @Override
       public Integer then(Task<String> task) throws Exception {
           // DISK_Thread
           return null;
       }
   }, DISK_EXECUTOR);
}

1.5 延时执行任务

Task.delay(100).continueWith(new Continuation<Void, Void>() {
    @Override
    public Void then(Task<Void> task) throws Exception {
        mTcpSocketAgent.initClientSocket(serverIp);
        mTcpSocketAgent.addMsgCallback(mTcpMsgCallback);
        mTcpSocketAgent.startConnect();
        return null;
    }
},Task.UI_THREAD_EXECUTOR);

1.6 取消执行的任务

取消执行的任务。


//取消任务
public void cancelTask() {
   CancellationTokenSource cts = new CancellationTokenSource();
   Task<Boolean> stringTask = runOnBackgroundThread(cts.getToken());
   cts.cancel();
}

//运行在后台线程中
public Task<Boolean> runOnBackgroundThread(CancellationToken cancellationToken) {
   //
   return Task.call(new Callable<Boolean>() {
       @Override
       public Boolean call() throws Exception {
           // Background_Thread
           return true;
       }
   }, Task.BACKGROUND_EXECUTOR, cancellationToken);
}

二、Bolts 原理

通过跟踪源码的形式,了解其运行原理。

  • 1、每一个Task类的对象,均包含一个Continuation列表,用来管理当前Task对象全部的Continuation任务
// 每一个Task对象都有一个自己的Continuation队列(continuations非全局,这样不同task的continuation就不会混淆)
private List<Continuation<TResult, Void>> continuations = new ArrayList<>();
  • 2、Continuation任务的执行时机
    当前Task对象的call()方法运行结束后,会调用trySetResult()方法,将运行结果保存下来;并循环执行continuations列表中的任务

Task.java类中trySetResult方法:

    //当前task,执行结束后的返回数据赋值
    //Sets the result on the Task if the Task hasn't already been completed.
    boolean trySetResult(TResult result) {
        synchronized (lock) {
            // 任务运行结束判断
            if (complete) {
                return false;
            }
            // 任务运行结束
            complete = true;
            // 任务结果赋值
            Task.this.result = result;
            // 唤醒lock wait等待(如果之前调用过waitForCompletion阻塞线程,那么此处会被唤醒)
            lock.notifyAll();
            // 循环执行continuations任务
            runContinuations();
            return true;
        }
    }
  • 3、Task.java类中runContinuations方法:
    每一个Continuation又是一个封装的Continuation.then() 方法中将任务提交到的不同线程池.
// 循环执行continuations任务
// 问题: Continuation并没有区分执行线程呀?
// 答:每一个Continuation又是一个封装的Continuation.then(),方法中将任务提交到的不同线程池
private void runContinuations() {
    synchronized (lock) {
        // 循环运行当前continuations列表任务
        for (Continuation<TResult, ?> continuation : continuations) {
            try {
               // 执行Continuation任务
                continuation.then(this);
           } catch (RuntimeException e) {
                throw e;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
       }
        // 当前Task的continuations队列置空
       continuations = null;
    }
}
  • 4、continuations如何将不同任务提交到不用的线程池
    每一个Continuation又是一个封装的Continuation.then(),方法中将任务提交到的不同线程池
    下文代码可以看出每次提交的Continuation任务中,均封装了一个completeImmediately(tcs, continuation, task, executor, ct);来确定其运行的线程池等信息。

Task.java类中continueWith方法:

public <TContinuationResult> Task<TContinuationResult> continueWith(
        final Continuation<TResult, TContinuationResult> continuation, final Executor executor,
        final CancellationToken ct) {

    // 上次task的完成情况
    boolean completed;
    // 构建一个新的task
    final TaskCompletionSource<TContinuationResult> tcs = new TaskCompletionSource<>();
    //
    synchronized (lock) {
        // 上次task的完成情况
        completed = this.isCompleted();
        // 如果上次task 未完成,添加到task列表
        if (!completed) {
            // 向continuations队列中添加一个“需提交到不同线程池执行的task任务”
            this.continuations.add(new Continuation<TResult, Void>() {
                @Override
                public Void then(Task<TResult> task) {
                    //
                    completeImmediately(tcs, continuation, task, executor, ct);
                    return null;
                }
            });
        }
    }
    // 上次任务完成,当前线程执行
    if (completed) {
        completeImmediately(tcs, continuation, this, executor, ct);
    }
    //
    return tcs.getTask();
}
  • 5、可通过Task类waitForCompletion()方法,阻塞当前线程,直到Task任务运行结束
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bjxiaxueliang

您的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值