android开发 之 Bolts-Android

目录

多线程编程对比: iOS与Android

android的多线程编程有

  • Thread

  • Handler

  • AsyncTask

  • IntentService

android多线程详细可以参考android学习 之 Service

iOS的多线程编程有

  • NSthread

  • GCD

  • NSOperationQueue

iOS多线程详细可以参考Objective-C学习 之 GCD / iOS最佳实践 之 优先使用NSOperationQueue而不是GCD / iOS开发 之 Queue和Thread

对比两个平台的多线程编程, android还是稍逊一些

  • Handler和IntentService的多线程编程, 开销有些大, 而GCD配合Block, 简洁好用并且功能也很强大

  • AsyncTask处理消息队列和多线程同步, 是比较费劲的, 而NSOperationQueue, 仍然是简单好用五颗星

那有没有一种方案可以让android变得和iOS一样简单好用呢?

答案就是今天的主角BoltsFramework/Bolts-Android

Bolts-Android

Bolts-Android由Parse"荣誉"出品, 为什么说是"荣誉"呢?

因为作为Baas的鼻祖, Parse已经成功地死在了沙滩上, 但是被Facebook关门之后, 它仍然发挥了余热, Parse的很多项目都在Github Parse上开源了

废话不多说, 直接看例子吧

Tasks

想要在当前线程执行Task, 就是这么简单

Task.call(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in current thread");
        return true;
    }
});

如果想在后台线程执行Task

Task.callInBackground(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in background thread");
        return true;
    }
});

如果想在UI主线程执行Task

Task.call(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in main thread");
        return true;
    }
}, Task.UI_THREAD_EXECUTOR);

如果想要延时执行Task

Task.delay(2000).continueWith(new Continuation<Void, Void>() {
    @Override
    public Void then(Task<Void> task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " " + "call in main thread (after delay 2 seconds)");
        return null;
    }
});

是不是简单到让人无法相信? 其实这里的Task和iOS中的GCD编程是类似的(详细可以参考Objective-C学习 之 GCD)

Chaining Tasks

热完身, 我们来看下Bolts-Android的函数式编程(关于函数式编程详细可以参考谈谈函数式响应式编程(Functional Reactive Programming))

Task.call(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " calling");
        return true;
    }
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation<Boolean, String>() {
    @Override
    public String then(Task<Boolean> task) throws Exception {
        Thread.sleep(2000);
        XLog.d(Thread.currentThread().getName() + " onSuccess " + task.getResult());

        return "hello bolts";
    }
}, Task.BACKGROUND_EXECUTOR).continueWith(new Continuation<String, Void>() {
    @Override
    public Void then(Task<String> task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " continueWith " + task.getResult());
        return null;
    }
}, Task.UI_THREAD_EXECUTOR);

打印结果如下

main calling
pool-2-thread-1 onSuccess true
main continueWith hello bolts

通过函数式的链式编程, 轻松地实现了Task的同步和多线程的切换(怎么忽然就想到了RxJava了呢? 看来下期可以来个Bolts-Android大战RxJava的专题)

Group Tasks

Group Task是我取得名字哈, 所谓Group Tasks, 就是指多个Task组成的一组Tasks

Task.call(new Callable<Boolean>() {
    @Override
    public Boolean call() throws Exception {
        XLog.d(Thread.currentThread().getName() + " calling");
        return true;
    }
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation<Boolean, List<Task<Void>>>() {
    @Override
    public List<Task<Void>> then(Task<Boolean> task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " tasks start");
        List<Task<Void>> tasks = new ArrayList<Task<Void>>();
        tasks.add(asyncOperation1());
        tasks.add(asyncOperation2());
        Task.whenAll(tasks).waitForCompletion();
        XLog.d(Thread.currentThread().getName() + " tasks end");
        return tasks;
    }
}, Task.BACKGROUND_EXECUTOR).continueWith(new Continuation<List<Task<Void>>, Void>() {
    @Override
    public Void then(Task<List<Task<Void>>> task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " done");
        return null;
    }
}, Task.UI_THREAD_EXECUTOR);

其中asyncOperation1和asyncOperation2的定义如下

private Task<Void> asyncOperation1() {
    return Task.callInBackground(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            XLog.i("asyncOperation1 start");

            try {
                Thread.sleep(1000);
            } catch (Exception e) {
                XLog.e(e.getMessage());
            }

            XLog.i("asyncOperation1 end");
            return null;
        }
    });
}

private Task<Void> asyncOperation2() {
    return Task.callInBackground(new Callable<Void>() {
        @Override
        public Void call() throws Exception {
            XLog.i("asyncOperation2 start");

            try {
                Thread.sleep(3000);
            } catch (Exception e) {
                XLog.e(e.getMessage());
            }

            XLog.i("asyncOperation2 end");
            return null;
        }
    });
}

后面的continueWith会等asyncOperation1和asyncOperation2都执行完成后再执行

打印结果如下

main calling
pool-2-thread-1 tasks start
asyncOperation1 start
asyncOperation2 start
asyncOperation1 end
asyncOperation2 end
pool-2-thread-1 tasks end
main done

有没有小试牛刀的感觉? 我们接着来看下面的用法

Tasks in Parallel

final Executor PARALLEL_EXECUTOR = Executors.newCachedThreadPool();

Task.call(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0) {
                XLog.i(Thread.currentThread().getName() + " i = " + i);
            }
        }
        return null;
    }
}, PARALLEL_EXECUTOR);

Task.call(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 1) {
                XLog.i(Thread.currentThread().getName() + " i = " + i);
            }
        }
        return null;
    }
}, PARALLEL_EXECUTOR);

打印结果如下

pool-2-thread-1 i = 0
pool-2-thread-2 i = 50
pool-2-thread-1 i = 1
pool-2-thread-2 i = 51
pool-2-thread-1 i = 2
pool-2-thread-2 i = 52
pool-2-thread-1 i = 3
pool-2-thread-2 i = 53
pool-2-thread-1 i = 4
pool-2-thread-2 i = 54
pool-2-thread-1 i = 5
pool-2-thread-1 i = 6
pool-2-thread-2 i = 55
pool-2-thread-2 i = 56
pool-2-thread-1 i = 7
pool-2-thread-1 i = 8
pool-2-thread-2 i = 57
pool-2-thread-2 i = 58
pool-2-thread-1 i = 9
pool-2-thread-2 i = 59
pool-2-thread-1 i = 10
pool-2-thread-2 i = 60
pool-2-thread-1 i = 11
pool-2-thread-2 i = 61
pool-2-thread-1 i = 12
pool-2-thread-2 i = 62
pool-2-thread-1 i = 13
pool-2-thread-2 i = 63
pool-2-thread-1 i = 14
pool-2-thread-2 i = 64
pool-2-thread-1 i = 15
pool-2-thread-2 i = 65
pool-2-thread-1 i = 16
pool-2-thread-2 i = 66
pool-2-thread-1 i = 17
pool-2-thread-2 i = 67
pool-2-thread-1 i = 18
pool-2-thread-1 i = 19
pool-2-thread-2 i = 68
pool-2-thread-1 i = 20
pool-2-thread-2 i = 69
pool-2-thread-1 i = 21
pool-2-thread-2 i = 70
pool-2-thread-1 i = 22
pool-2-thread-2 i = 71
pool-2-thread-1 i = 23
pool-2-thread-2 i = 72
pool-2-thread-1 i = 24
pool-2-thread-2 i = 73
pool-2-thread-2 i = 74
pool-2-thread-1 i = 25
pool-2-thread-2 i = 75
pool-2-thread-1 i = 26
pool-2-thread-2 i = 76
pool-2-thread-1 i = 27
pool-2-thread-2 i = 77
pool-2-thread-1 i = 28
pool-2-thread-2 i = 78
pool-2-thread-1 i = 29
pool-2-thread-2 i = 79
pool-2-thread-1 i = 30
pool-2-thread-1 i = 31
pool-2-thread-2 i = 80
pool-2-thread-1 i = 32
pool-2-thread-2 i = 81
pool-2-thread-1 i = 33
pool-2-thread-2 i = 82
pool-2-thread-1 i = 34
pool-2-thread-2 i = 83
pool-2-thread-2 i = 84
pool-2-thread-1 i = 35
pool-2-thread-2 i = 85
pool-2-thread-1 i = 36
pool-2-thread-2 i = 86
pool-2-thread-1 i = 37
pool-2-thread-2 i = 87
pool-2-thread-1 i = 38
pool-2-thread-1 i = 39
pool-2-thread-2 i = 88
pool-2-thread-1 i = 40
pool-2-thread-2 i = 89
pool-2-thread-1 i = 41
pool-2-thread-2 i = 90
pool-2-thread-1 i = 42
pool-2-thread-2 i = 91
pool-2-thread-1 i = 43
pool-2-thread-2 i = 92
pool-2-thread-1 i = 44
pool-2-thread-2 i = 93
pool-2-thread-1 i = 45
pool-2-thread-2 i = 94
pool-2-thread-1 i = 46
pool-2-thread-2 i = 95
pool-2-thread-1 i = 47
pool-2-thread-2 i = 96
pool-2-thread-1 i = 48
pool-2-thread-2 i = 97
pool-2-thread-1 i = 49
pool-2-thread-2 i = 98
pool-2-thread-2 i = 99

打印结果有两个发现

  • 线程池有两个线程: pool-2-thread-1和pool-2-thread-2

  • 线程池里的线程是并行执行的: 数字都是

Tasks in Serial

那有没有方法可以让线程池中只有一个线程, 所有Tasks按照queue的方式FIFO依次执行呢? 办法如下

final Executor SERIAL_EXECUTOR = Executors.newSingleThreadExecutor();

Task.call(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        for (int i = 0; i < 50; i++) {
            XLog.i(Thread.currentThread().getName() + " i = " + i);
        }
        return null;
    }
}, SERIAL_EXECUTOR);

call(new Callable<Void>() {
    @Override
    public Void call() throws Exception {
        for (int i = 50; i < 100; i++) {
            XLog.i(Thread.currentThread().getName() + " i = " + i);
        }
        return null;
    }
}, SERIAL_EXECUTOR);

打印结果如下

pool-2-thread-1 i = 0
pool-2-thread-1 i = 1
pool-2-thread-1 i = 2
pool-2-thread-1 i = 3
pool-2-thread-1 i = 4
pool-2-thread-1 i = 5
pool-2-thread-1 i = 6
pool-2-thread-1 i = 7
pool-2-thread-1 i = 8
pool-2-thread-1 i = 9
pool-2-thread-1 i = 10
pool-2-thread-1 i = 11
pool-2-thread-1 i = 12
pool-2-thread-1 i = 13
pool-2-thread-1 i = 14
pool-2-thread-1 i = 15
pool-2-thread-1 i = 16
pool-2-thread-1 i = 17
pool-2-thread-1 i = 18
pool-2-thread-1 i = 19
pool-2-thread-1 i = 20
pool-2-thread-1 i = 21
pool-2-thread-1 i = 22
pool-2-thread-1 i = 23
pool-2-thread-1 i = 24
pool-2-thread-1 i = 25
pool-2-thread-1 i = 26
pool-2-thread-1 i = 27
pool-2-thread-1 i = 28
pool-2-thread-1 i = 29
pool-2-thread-1 i = 30
pool-2-thread-1 i = 31
pool-2-thread-1 i = 32
pool-2-thread-1 i = 33
pool-2-thread-1 i = 34
pool-2-thread-1 i = 35
pool-2-thread-1 i = 36
pool-2-thread-1 i = 37
pool-2-thread-1 i = 38
pool-2-thread-1 i = 39
pool-2-thread-1 i = 40
pool-2-thread-1 i = 41
pool-2-thread-1 i = 42
pool-2-thread-1 i = 43
pool-2-thread-1 i = 44
pool-2-thread-1 i = 45
pool-2-thread-1 i = 46
pool-2-thread-1 i = 47
pool-2-thread-1 i = 48
pool-2-thread-1 i = 49
pool-2-thread-1 i = 50
pool-2-thread-1 i = 51
pool-2-thread-1 i = 52
pool-2-thread-1 i = 53
pool-2-thread-1 i = 54
pool-2-thread-1 i = 55
pool-2-thread-1 i = 56
pool-2-thread-1 i = 57
pool-2-thread-1 i = 58
pool-2-thread-1 i = 59
pool-2-thread-1 i = 60
pool-2-thread-1 i = 61
pool-2-thread-1 i = 62
pool-2-thread-1 i = 63
pool-2-thread-1 i = 64
pool-2-thread-1 i = 65
pool-2-thread-1 i = 66
pool-2-thread-1 i = 67
pool-2-thread-1 i = 68
pool-2-thread-1 i = 69
pool-2-thread-1 i = 70
pool-2-thread-1 i = 71
pool-2-thread-1 i = 72
pool-2-thread-1 i = 73
pool-2-thread-1 i = 74
pool-2-thread-1 i = 75
pool-2-thread-1 i = 76
pool-2-thread-1 i = 77
pool-2-thread-1 i = 78
pool-2-thread-1 i = 79
pool-2-thread-1 i = 80
pool-2-thread-1 i = 81
pool-2-thread-1 i = 82
pool-2-thread-1 i = 83
pool-2-thread-1 i = 84
pool-2-thread-1 i = 85
pool-2-thread-1 i = 86
pool-2-thread-1 i = 87
pool-2-thread-1 i = 88
pool-2-thread-1 i = 89
pool-2-thread-1 i = 90
pool-2-thread-1 i = 91
pool-2-thread-1 i = 92
pool-2-thread-1 i = 93
pool-2-thread-1 i = 94
pool-2-thread-1 i = 95
pool-2-thread-1 i = 96
pool-2-thread-1 i = 97
pool-2-thread-1 i = 98
pool-2-thread-1 i = 99

Error Handling

看完上面的内容, 想必各位从此都是android多线程的"砖家"了吧, 不过请稍等片刻, 还有一个重要的问题没有解释

Task.call(new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(2000);

        if (1 < 10) {
            XLog.d(Thread.currentThread().getName() + " RuntimeException");
            throw new RuntimeException("There was an error.");
        }
        return "hello bolts";
    }
}, Task.BACKGROUND_EXECUTOR).onSuccess(new Continuation<String, Void>() {
    @Override
    public Void then(Task<String> task) throws Exception {
        XLog.d(Thread.currentThread().getName() + " onSuccess");
        return null;
    }
}, Task.UI_THREAD_EXECUTOR).continueWith(new Continuation<Void, Void>() {
    @Override
    public Void then(Task<Void> task) throws Exception {
        if (task.isFaulted()) {
            XLog.e(Thread.currentThread().getName() + " error");
            return null;
        }
        XLog.d(Thread.currentThread().getName() + " done");
        return null;
    }
}, Task.UI_THREAD_EXECUTOR);

请问这里的打印结果是如何呢?

  • A: onSuccess

  • B: error

  • C: done

  • D: onSuccess & done

正确答案是B

小结

android多线程的方案还有诸如: yigit/android-priority-jobqueue, 不过经过实测, 和Bolts-Android比较起来, android-priority-jobqueue还是"略重量级"了一些, 如果你有更好的方案和实现, 欢迎留言和分享

参考

更多文章, 请支持我的个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,615评论 4 59
  • 本文章转载于搜狗测试 敏捷测试逐渐成为行业趋势,优势在于快速迭代,所以在这个过程中,测试不能成为短板,测试工作也就...
    夜境阅读 9,795评论 0 1
  • 贻笑大方: 差龄畸婚遭众喷, 男氓女贱贻笑人, 一朝扬灰妃子泪, 空叹梦碎看墅晕。 二八佳人空自误, 八二老朽辱自...
    云之憾阅读 181评论 0 0
  • 今天跟周先生一起拾掇枣子,一起分出大小,一起打包。 今天和周先生约定,以后睡前一起看书,交流心得。 家里有人和自己...
    大老爷的不老书阅读 185评论 0 1