3

Introduction

RxJava

RxJava is a library for composing asynchronous, event-based programs using observable sequences on the Java VM. RxJava is essentially a library that implements asynchronous operations.

Project address: https://github.com/ReactiveX/RxJava

XTask

XTask is a highly extensible Android task execution framework. Through it, you can freely define and combine tasks to achieve the functions you want, especially for dealing with complex business processes, you can flexibly add pre-tasks or adjust the execution order.

Project address: https://github.com/xuexiangjys/XTask

background

XTask is an open source project created by me based on the design idea of RxJava and combined with the experience used in actual projects. Its purpose is to replace some of the usage scenarios of RxJava in Android and improve the development experience and maintainability.

I believe that people who have used RxJava know that RxJava has many flaws. I will briefly list a few below:

  • RxJava was not first used in Android, so it was designed to be quite complex and cumbersome from the beginning. A library can often reach about 3M, which is still very large in application volume compared to mobile terminals.
  • There are far more than 100 kinds of operators, and users are often confused, and confused use can easily lead to some fatal problems, such as memory leaks.
  • Since RxJava is an event-based library, it lacks log information for some key execution tasks, which makes it difficult to troubleshoot when something goes wrong.

And XTask was open sourced by me in order to solve the above problems.

use contrast

First of all, there is no doubt that RxJava is an excellent open source framework. XTask is not used to replace RxJava. I do not have this ability, and neither does google.

But in some small and common scenarios, we can completely replace the use of RxJava. For example the following two scenarios:

  • Complex serial task processing
  • Complex concurrent task processing

Below I will show you the difference through two small examples.

complex serial tasks

I believe that we will encounter many complex business processes in the usual development process, and many of these processes are linked to each other, and we need to go step by step. If there is any error in the middle, the execution will be stopped.

Next, I will take the case process of [high imitation Internet celebrity product] as an example to briefly explain how to achieve this process through RxJava and XTask .

case analysis

The process of high imitation Internet celebrity products

1. Get product information -> 2. Check the factories that can be produced -> 3. Contact the factory to produce products -> 4. Send it to the marketing department to evaluate the price -> 5. Product launch

Entity class design

There are mainly three entity classes involved here: Product, ProductInfo and ProductFactory.

 /**
 * 产品
 */
public class Product {
    /**
     * 产品信息
     */
    private ProductInfo info;
    /**
     * 产品生产地址
     */
    private String address;
    /**
     * 产品价格
     */
    private String price;
    /**
     * 产品发布时间
     */
    private String publicTime;
}

/**
 * 产品信息
 */
public class ProductInfo {
    /**
     * 编号
     */
    private String id;
    /**
     * 品牌
     */
    private String brand;
    /**
     * 质量
     */
    private String quality;
}

/**
 * 产品工厂
 */
public class ProductFactory {
    /**
     * 工厂id
     */
    private String id;
    /**
     * 工厂地址
     */
    private String address;
}

Case realization

business process processing

There are a total of 5 business processes above, and we simplify them into the following 4 processors for processing.

  • 1. Get product information: GetProductInfoProcessor (productId -> ProductInfo)
  • 2. Find related factories: SearchFactoryProcessor (ProductInfo -> ProductFactory)
  • 3. Evaluate the product and give the price: GivePriceProcessor (Product -> Product)
  • 4. Product release: PublicProductProcessor (Product -> Product)
business process tandem
  • common writing

In ordinary writing, we directly use the method of interface callback, and execute it layer by layer.

 AppExecutors.get().singleIO().execute(() -> {
    // 1.获取产品信息
    new GetProductInfoProcessor(logger, productId).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<ProductInfo>() {
        @Override
        public void onSuccess(final ProductInfo productInfo) {
            // 2.查询可生产的工厂
            new SearchFactoryProcessor(logger, productInfo).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<ProductFactory>() {
                @Override
                public void onSuccess(final ProductFactory factory) {
                    // 3.联系工厂生产产品
                    log("开始生产产品...");
                    Product product = factory.produce(productInfo);
                    // 4.送去市场部门评估售价
                    new GivePriceProcessor(logger, product).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<Product>() {
                        @Override
                        public void onSuccess(Product product) {
                            // 5.产品上市
                            PublicProductProcessor publicProductProcessor = new PublicProductProcessor(logger, product);
                            publicProductProcessor.setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<Product>() {
                                @Override
                                public void onSuccess(Product product) {
                                    log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
                                    log("仿冒生产网红产品完成, " + product);
                                }
                            }).process();
                        }
                    }).process();
                }
            }).process();
        }
    }).process();
});
  • RxJava writing

To perform serial tasks in RxJava, generally use map or flatMap , since it is one-to-one, use map to execute.

 disposable = Observable.just(productId)
        // 1.获取产品信息
        .map(id -> new GetProductInfoProcessor(logger, id).process())
        // 2.查询可生产的工厂
        .map(productInfo -> new Pair<>(new SearchFactoryProcessor(logger, productInfo).process(), productInfo))
        .map(productPair -> {
            // 3.联系工厂生产产品
            log("开始生产产品...");
            Product product = productPair.first.produce(productPair.second);
            // 4.送去市场部门评估售价
            return new GivePriceProcessor(logger, product).process();
        })
        // 5.产品上市
        .map(product -> new PublicProductProcessor(logger, product).process())
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(product -> {
            log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
            log("仿冒生产网红产品完成, " + product);
        });
  • XTask writing

Different from the common writing method and the RxJava writing method, XTask encapsulates all the business processors in one Task, and then adds the corresponding Tasks in sequence according to the execution order of the tasks to complete.

 XTask.getTaskChain()
        .setTaskParam(TaskParam.get(ProductTaskConstants.KEY_PRODUCT_ID, productId))
        // 1.获取产品信息
        .addTask(new GetProductInfoTask(logger))
        // 2.查询可生产的工厂, 3.联系工厂生产产品
        .addTask(new SearchFactoryTask(logger))
        // 4.送去市场部门评估售价
        .addTask(new GivePriceTask(logger))
        // 5.产品上市
        .addTask(new PublicProductTask(logger))
        .setTaskChainCallback(new TaskChainCallbackAdapter() {
            @Override
            public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
                log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
                Product product = result.getDataStore().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class);
                log("仿冒生产网红产品完成, " + product);
            }
        }).start();

Case execution result

  • program execution result

  • List of XTask execution logs


complex parallel tasks

In addition to the common serial tasks we discussed above, we also encounter some complex parallel processes in the usual development process. These processes are often executable independently, and although they are not closely related, they are processes that are simultaneously executed for a certain goal.

Next, I will take the common case process of [display product details] as an example, and briefly explain how to achieve this process through RxJava and XTask .

case analysis

Process for displaying product details
  • 1. Obtain the brief information of the product according to the unique ID of the product
  • 2. Get the product details:

    • 2.1 Obtaining the production information of the product
    • 2.2 Get the price information of the product
    • 2.3 Get the promotion information of the product
    • 2.4 Get rich text information of products
  • 3. Display product information

The four sub-steps in step 2 are concurrent processes that can be performed at the same time without affecting each other.

Entity class design

There are mainly 6 entity classes involved here: BriefInfo, Product, FactoryInfo, PriceInfo, PromotionInfo and RichInfo.

 /**
 * 产品简要信息
 */
public class BriefInfo {
    private String id;

    protected String name;

    private String factoryId;

    private String priceId;

    private String promotionId;

    private String richId;
}

/**
 * 产品
 */
public class Product extends BriefInfo {
    /**
     * 生产信息
     */
    private FactoryInfo factory;
    /**
     * 价格信息
     */
    private PriceInfo price;
    /**
     * 促销信息
     */
    private PromotionInfo promotion;
    /**
     * 富文本信息
     */
    private RichInfo rich;
}

/**
 * 工厂生产信息
 */
public class FactoryInfo {
    private String id;
    /**
     * 生产地址
     */
    private String address;
    /**
     * 生产日期
     */
    private String productDate;
    /**
     * 过期日期
     */
    private String expirationDate;
}

/**
 * 价格信息
 */
public class PriceInfo {
    private String id;
    /**
     * 出厂价
     */
    private float factoryPrice;
    /**
     * 批发价
     */
    private float wholesalePrice;
    /**
     * 零售价
     */
    private float retailPrice;
}

/**
 * 产品促销信息
 */
public class PromotionInfo {
    private String id;
    /**
     * 促销类型
     */
    private int type;
    /**
     * 促销内容
     */
    private String content;
    /**
     * 生效日期
     */
    private String effectiveDate;
    /**
     * 失效日期
     */
    private String expirationDate;
}

/**
 * 富文本信息
 */
public class RichInfo {
    private String id;
    /**
     * 描述信息
     */
    private String description;
    /**
     * 图片链接
     */
    private String imgUrl;
    /**
     * 视频链接
     */
    private String videoUrl;
}

Case realization

business process processing

There are 3 major business processes and 4 sub-business processes in the above, and we simplify them into the following 5 processors for processing.

  • 1. Get product brief information: GetBriefInfoProcessor (productId -> BriefInfo)
  • 2. Get the production information of the product: GetFactoryInfoProcessor (factoryId -> FactoryInfo)
  • 3. Get the price information of the product: GetPriceInfoProcessor (priceId -> PriceInfo)
  • 4. Get the promotion information of the product: GetPromotionInfoProcessor (promotionId -> PromotionInfo)
  • 5. Get the rich text information of the product: GetRichInfoProcessor (richId -> RichInfo)
business process tandem
  • common writing

In the common writing method, we need to realize the concurrency and coordination of tasks through interface callback + synchronization lock.

 AppExecutors.get().singleIO().execute(() -> {
    new GetBriefInfoProcessor(logger, productId).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<BriefInfo>() {
        @Override
        public void onSuccess(BriefInfo briefInfo) {
            final Product product = new Product(briefInfo);
            CountDownLatch latch = new CountDownLatch(4);

            // 2.1 获取商品的生产信息
            AppExecutors.get().networkIO().execute(() -> {
                new GetFactoryInfoProcessor(logger, product.getFactoryId()).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<FactoryInfo>() {
                    @Override
                    public void onSuccess(FactoryInfo result) {
                        product.setFactory(result);
                        latch.countDown();
                    }
                }).process();
            });
            // 2.2 获取商品的价格信息
            AppExecutors.get().networkIO().execute(() -> {
                new GetPriceInfoProcessor(logger, product.getPriceId()).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<PriceInfo>() {
                    @Override
                    public void onSuccess(PriceInfo result) {
                        product.setPrice(result);
                        latch.countDown();
                    }
                }).process();
            });
            // 2.3 获取商品的促销信息
            AppExecutors.get().networkIO().execute(() -> {
                new GetPromotionInfoProcessor(logger, product.getPromotionId()).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<PromotionInfo>() {
                    @Override
                    public void onSuccess(PromotionInfo result) {
                        product.setPromotion(result);
                        latch.countDown();
                    }
                }).process();
            });
            // 2.4 获取商品的富文本信息
            AppExecutors.get().networkIO().execute(() -> {
                new GetRichInfoProcessor(logger, product.getRichId()).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter<RichInfo>() {
                    @Override
                    public void onSuccess(RichInfo result) {
                        product.setRich(result);
                        latch.countDown();
                    }
                }).process();
            });
            try {
                latch.await();
                log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
                log("查询商品信息完成, " + product);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).process();
});
  • RxJava writing

To perform parallel tasks in RxJava, generally use merge or zip , because of the need for collaboration, use zip to merge the task flow.

 disposable = Observable.just(productId)
        // 1.获取商品简要信息
        .map(id -> new GetBriefInfoProcessor(logger, id).process())
        .map(Product::new)
        .flatMap(product ->
                Observable.zip(
                        // 2.1 获取商品的生产信息
                        Observable.fromCallable(() -> new GetFactoryInfoProcessor(logger, product.getFactoryId()).process()).subscribeOn(Schedulers.io()),
                        // 2.2 获取商品的价格信息
                        Observable.fromCallable(() -> new GetPriceInfoProcessor(logger, product.getPriceId()).process()).subscribeOn(Schedulers.io()),
                        // 2.3 获取商品的促销信息
                        Observable.fromCallable(() -> new GetPromotionInfoProcessor(logger, product.getPromotionId()).process()).subscribeOn(Schedulers.io()),
                        // 2.4 获取商品的富文本信息
                        Observable.fromCallable(() -> new GetRichInfoProcessor(logger, product.getRichId()).process()).subscribeOn(Schedulers.io()), (factoryInfo, priceInfo, promotionInfo, richInfo) -> product.setFactory(factoryInfo)
                                .setPrice(priceInfo)
                                .setPromotion(promotionInfo)
                                .setRich(richInfo)
                )
        )
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(product -> {
            log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
            log("查询商品信息完成, " + product);
        });
  • XTask writing

XTask encapsulates all business processors in one Task, and then parallel tasks need to be wrapped by a ConcurrentGroupTask (synchronous group task), and other tasks can be added in the normal execution order.

 XTask.getTaskChain()
        .setTaskParam(TaskParam.get(ProductTaskConstants.KEY_PRODUCT_ID, productId))
        // 1.获取商品简要信息
        .addTask(new GetBriefInfoTask(logger))
        .addTask(XTask.getConcurrentGroupTask(ThreadType.SYNC)
                // 2.1 获取商品的生产信息
                .addTask(new GetFactoryInfoTask(logger))
                // 2.2 获取商品的价格信息
                .addTask(new GetPriceInfoTask(logger))
                // 2.3 获取商品的促销信息
                .addTask(new GetPromotionInfoTask(logger))
                // 2.4 获取商品的富文本信息
                .addTask(new GetRichInfoTask(logger)))
        .setTaskChainCallback(new TaskChainCallbackAdapter() {
            @Override
            public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
                log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms");
                Product product = result.getDataStore().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class);
                log("查询商品信息完成, " + product);
            }
        }).start();

Case execution result

  • program execution result

  • List of XTask execution logs


Use comparison summary

From the above comparison, we can briefly summarize the following points:

programmatically

1. RxJava follows the principle of functional reactive programming, and the processing process is based on data flow processing. The advantage of this is that we can feel the change process of data most intuitively and effectively. Of course, the disadvantage is that it is too detailed and specific, does not conform to the principles of object-oriented design patterns, and increases the cost of code maintenance in the future. Of course, if the data structure is relatively stable, such a programming method is acceptable, but if the data or business changes frequently, this programming method is simply hell.

2. XTask follows the principle of object-oriented programming, and each processing process corresponds to a specific or abstract Task. The advantage of this is that the coupling between the business and the data structure is reduced, and the coupling between the various businesses is also reduced. In this way, even if there are major changes in your data structure or business process, the main body of the function implementation will not have major changes, but only the changes and adjustments within each sub-business task, which truly realizes high reuse and low coupling.

Summary : Two different programming methods follow two different programming principles and cannot be compared.

Difficulty getting started

If you are a veteran of RxJava development, there is no comparison, here I am just speaking from a beginner's point of view.

1. RxJava has huge and complex operators. Hundreds of operators will surely confuse beginners. If they are forced to be used in unfamiliar situations, it will easily lead to misuse and cause many unexpected problems (such as memory leaks). or OOM, etc.).

2. XTask, as a task execution framework specially designed for Android, has a relatively single function. There are no complicated operators, there are only five components of "task chain, task, group task, task parameters and execution result", which are relatively simple and easy to use.

Summary : Overall, XTask is better than RxJava.

Development efficiency

1. The development efficiency of RxJava mainly depends on the developer's proficiency in the use of RxJava operators. The more proficient you can use operators, the higher the development efficiency and the less chance of problems.

2. XTask is relatively smooth, and the development efficiency has little to do with the proficiency of use (mainly it is not difficult to get started). However, since each business sub-step needs to write a Task class, for those who are more proficient in using RxJava, the efficiency will be significantly lower.

Summary : Overall, RxJava is better than XTask in the long run.

maintainability

1. RxJava follows the principle of functional reactive programming, which is essentially procedural-oriented programming. All business processes are strongly coupled with data. When the data structure or business process changes, it will inevitably affect the changes of the main code. Moreover, when developers who are new to the project take over the project, they can often see changes in the local business data flow. They cannot understand the main business of the project from a global perspective, and it is easy to produce local changes that affect the overall results.

2. XTask follows the principle of object-oriented programming, and strictly follows the principle of object-oriented design pattern at the beginning of the design. Fully reduce the coupling between business and business, business and data flow, so that even if your data structure or business process changes significantly, the main code will not change greatly. Moreover, XTask has a strong logging system, which can clearly record the execution process of your current task chain and the information of the thread (automatic). When there is a problem with the task execution, it can quickly locate the problem. Location. For developers who are new to the project, they can also quickly understand the main business of the project from the log of the task execution process. After you have a clear understanding of the main business process, you can look at the sub-business carefully, so that you can fully understand the business of the project, and it is more conducive to the maintenance of the project.

Summary : Overall, XTask beats RxJava.

performance

In terms of performance, XTask has designed a shared data structure in order to realize the isolation between business and data. Compared with RxJava, there are more data copying and data storage processes. Therefore, in terms of time and space, RxJava Both are better than XTask.

At last

Based on the above discussion, XTask and RxJava each have their own advantages. As I said at the beginning of the article: XTask is not intended to replace RxJava. XTask is only a supplement of RxJava in the Android task execution process. Friends who like it can follow the XTask project homepage: https://github.com/xuexiangjys/XTask .

I am xuexiangjys, a technology up master who loves learning, programming, and is committed to Android architecture research and open source project experience sharing. For more information, welcome to WeChat search public account: [My Android Open Source Journey]

xuexiangjys
357 声望4.1k 粉丝