android开发 之 Bolts-Android

作者: 诺之林 | 来源:发表于2016-08-02 18:19 被阅读3300次

    目录

    多线程编程对比: 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还是"略重量级"了一些, 如果你有更好的方案和实现, 欢迎留言和分享

    参考

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

    相关文章

      网友评论

        本文标题:android开发 之 Bolts-Android

        本文链接:https://www.haomeiwen.com/subject/ngzfsttx.html