当前位置:网站首页>Comparison between xtask and kotlin coroutine

Comparison between xtask and kotlin coroutine

2022-04-23 17:40:00 xuexiangjys

background

XTask I'm based on RxJava Design idea , An open source project created by combining the experience used in the actual project , Its purpose is to replace RxJava stay Android Some usage scenarios in , Improve the development experience and maintainability .

I wrote an article a while ago 《XTask And RxJava The use of contrast 》 article , Originally, it was only from the comparison of the differences between the two , Let us have a more intuitive and comprehensive understanding of XTask, However, some punks began to comment or write in private letters below “ use Kotlin It doesn't smell good ”、“ and kotlin How does it compare to the collaborative process ” etc. .

The first thing I want to say is this , Xie Cheng is not as amazing as some people boast , In the final analysis, it is just an application framework , The main solution is the asynchronous execution in the development process , This is not the same as it is RxJava It's similar ; secondly , Coprocessor is not kotlin First put forward , The concept of collaborative process can be traced back to 20 century 50 years , At present, mainstream languages such as python、C++ and go Languages support and implement collaborative processes ; Last , In this world, there has never been a framework of ten thousand profits , Any technology that doesn't talk about usage scenarios is touted , It's all about hooligans .

But since you want to compare , Then I'll arrange it now !

But before comparison , Let me briefly introduce these two frameworks first .

brief introduction

XTask

XTask It is a highly expansionary Android Task execution framework . Through it , You are free to define and combine tasks to achieve the functions you want , It is especially suitable for dealing with complex business processes , You can flexibly add predecessor tasks or adjust the execution order .

Address of the project :
https://github.com/xuexiangjys/XTask

Using document :
https://github.com/xuexiangjys/XTask/wiki

Kotlin Coroutine

kotlinx.coroutines By JetBrains The development of a rich function of the process library . It contains many of the primitives covered in this guide that enable advanced collaboration , Include launch、async wait .

A coroutine is not a system level thread , Many times, a collaborative process is called “ Lightweight threads ”、“ Micro thread ”. stay Java China is similar to Runnable.

Project address :
https://github.com/Kotlin/kotlinx.coroutines

Chinese document :
https://www.kotlincn.net/docs/reference/coroutines/coroutines-guide.html

Use comparison

It's the same as last time , This time, I'll show you their differences from the following two small and commonly used scenes .

  • Complex serial task processing
  • Complex concurrent task processing

Complex serial tasks

I believe that we will encounter many complex business processes in our usual development process , Many of these processes are set in one ring , It needs to go step by step , Any error in the middle will stop execution .

I'll take [ High imitation net red products ] Take the case process of , Briefly explain how to pass Kotlin Coroutine and XTask To implement this process .

case analysis

Process of high imitation net red products

1. Get product information -> 2. Inquire about the factory that can produce -> 3. Contact the factory to produce products -> 4. Send it to the marketing department to evaluate the selling price -> 5. Product launch

Entity class design

This is mainly about 3 Entity classes : Product、ProductInfo and ProductFactory.

/**
 *  product 
 */
class Product {
    /**
     *  Product information 
     */
    var info: ProductInfo
    /**
     *  Product manufacturing address 
     */
    var address: String
    /**
     *  product price 
     */
    var price: String? = null
    /**
     *  Product launch time 
     */
    var publicTime: String? = null
}
/**
 *  Product information 
 */
class ProductInfo {
    /**
     *  Number 
     */
    var id: String
    /**
     *  brand 
     */
    var brand: String? = null
    /**
     *  quality 
     */
    var quality: String? = null
}
/**
 *  Product Factory 
 */
class ProductFactory {
    /**
     *  factory id
     */
    var id: String
    /**
     *  Factory address 
     */
    var address: String
}

Case realization

Business process processing

Above shared 5 Business processes , We simplify it into the following 4 One processor for processing .

  • 1. Get product information : GetProductInfoProcessor (productId -> ProductInfo)
  • 2. Find relevant factories : SearchFactoryProcessor (ProductInfo -> ProductFactory)
  • 3. Evaluate the product , Give price : GivePriceProcessor (Product -> Product)
  • 4. Product release : PublicProductProcessor (Product -> Product)
Business process tandem
  • Common writing

In common writing, we directly use the interface callback , Layer by layer .

AppExecutors.get().singleIO().execute {
    // 1. Get product information 
    GetProductInfoProcessor(binding?.logger, productId).setProcessorCallback(object :
        ProcessorCallbackAdapter<ProductInfo?>() {
        override fun onSuccess(productInfo: ProductInfo?) {
            // 2. Inquire about the factory that can produce 
            SearchFactoryProcessor(binding?.logger, productInfo!!).setProcessorCallback(
                object : ProcessorCallbackAdapter<ProductFactory?>() {
                    override fun onSuccess(factory: ProductFactory?) {
                        // 3. Contact the factory to produce products 
                        log(" Start producing products ...")
                        val product = factory?.produce(productInfo)
                        // 4. Send it to the marketing department to evaluate the selling price 
                        GivePriceProcessor(binding?.logger, product!!).setProcessorCallback(
                            object : ProcessorCallbackAdapter<Product?>() {
                                override fun onSuccess(product: Product?) {
                                    // 5. Product launch 
                                    PublicProductProcessor(
                                        binding?.logger,
                                        product
                                    ).setProcessorCallback(object :
                                        ProcessorCallbackAdapter<Product?>() {
                                        override fun onSuccess(product: Product?) {
                                            log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
                                            log(" The fake production of net red products is completed , $product")
                                        }
                                    }).process()
                                }
                            }).process()
                    }
                }).process()
        }
    }).process()
  • Kotlin Coroutine How to write it

Kotlin Coroutine The biggest advantage is that asynchronous code can be synchronized , Just use withContext Can finish . In fact, this is not a new thing , This is the sum of js、dart In the language await similar .

mainScope.launch {
    val productInfo = withContext(Dispatchers.IO) {
        // 1. Get product information 
        GetProductInfoProcessor(binding?.logger, productId).process()
    }
    val factory = withContext(Dispatchers.IO) {
        // 2. Inquire about the factory that can produce 
        SearchFactoryProcessor(binding?.logger, productInfo).process()
    }
    // 3. Contact the factory to produce products 
    log(" Start producing products ...")
    var product = factory.produce(productInfo)
    product = withContext(Dispatchers.IO) {
        // 4. Send it to the marketing department to evaluate the selling price 
        GivePriceProcessor(binding?.logger, product).process()
        // 5. Product launch 
        PublicProductProcessor(binding?.logger, product).process()
    }
    log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
    log(" The fake production of net red products is completed , $product")
}
  • Kotlin Flow How to write it

Kotlin Flow yes Kotlin Coroutine Part of ecology , Must rely on it to use . It's benchmarking RxJava Designed , be-all API and RxJava Basically the same , In most scenarios, equivalent substitution can be achieved .

As shown in the following code ,flowOf Just analogy just,map Even the name is the same ,flowIn analogy subscribeOn,collect analogy subscribe.

mainScope.launch {
    flowOf(productId)
        .map { id ->
            // 1. Get product information 
            GetProductInfoProcessor(binding?.logger, id).process()
        }
        .map { productInfo ->
            // 2. Inquire about the factory that can produce 
            SearchFactoryProcessor(binding?.logger, productInfo).process() to productInfo
        }
        .map { pair ->
            // 3. Contact the factory to produce products 
            log(" Start producing products ...")
            val product = pair.first.produce(pair.second)
            // 4. Send it to the marketing department to evaluate the selling price 
            GivePriceProcessor(binding?.logger, product).process()
        }.map { product ->
            // 5. Product launch 
            PublicProductProcessor(binding?.logger, product).process()
        }.flowOn(Dispatchers.IO)
        .collect { product ->
            log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
            log(" The fake production of net red products is completed , $product")
        }
}
  • XTask How to write it

With common writing and RxJava The difference in writing is ,XTask It encapsulates all business processors one by one Task in , Then add the corresponding... In the order of task execution Task Can finish .

XTask.getTaskChain()
    .setTaskParam(TaskParam.get(ProductTaskConstants.KEY_PRODUCT_ID, productId)) // 1. Get product information 
    .addTask(GetProductInfoTask(binding?.logger)) // 2. Inquire about the factory that can produce , 3. Contact the factory to produce products 
    .addTask(SearchFactoryTask(binding?.logger)) // 4. Send it to the marketing department to evaluate the selling price 
    .addTask(GivePriceTask(binding?.logger)) // 5. Product launch 
    .addTask(PublicProductTask(binding?.logger))
    .setTaskChainCallback(object : TaskChainCallbackAdapter() {
        override fun onTaskChainCompleted(engine: ITaskChainEngine, result: ITaskResult) {
            log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
            val product = result.dataStore.getObject(
                ProductTaskConstants.KEY_PRODUCT,
                Product::class.java
            )
            log(" The fake production of net red products is completed , $product")
        }
    }).start()

Case execution results

  • Program execution result

  • XTask Execution log list


Complex parallel tasks

In addition to the common serial tasks we discussed above , We will also encounter some complex parallel processes in the normal development process . These processes are often individually executable , Although there is little correlation , But it is also a process executed for a certain goal at the same time .

Now I'll take the common [ Show product details ] Take the case process of , Briefly explain how to pass Kotlin Coroutine and XTask To implement this process .

case analysis

The process of displaying product details
  • 1. Unique item number ID Get product brief information
  • 2. Get product details :

    • 2.1 Obtain the production information of goods
    • 2.2 Get the price information of goods
    • 2.3 Get promotional information about the product
    • 2.4 Get the rich text information of the product
  • 3. Display commodity information

One step 2 Medium 4 The sub steps can be carried out at the same time , Concurrent processes that do not affect each other .

Entity class design

This is mainly about 6 Entity classes : BriefInfo、Product、FactoryInfo、PriceInfo、PromotionInfo and RichInfo.

/**
 *  Product brief information 
 */
open class BriefInfo {
    var id: String
    var name: String? = null
    var factoryId: String? = null
    var priceId: String? = null
    var promotionId: String? = null
    var richId: String? = null
}

/**
 *  product 
 */
class Product(briefInfo: BriefInfo) : BriefInfo(briefInfo) {
    /**
     *  Production information 
     */
    var factory: FactoryInfo? = null
    /**
     *  Price information 
     */
    var price: PriceInfo? = null
    /**
     *  Promotional information 
     */
    var promotion: PromotionInfo? = null
    /**
     *  Rich text information 
     */
    var rich: RichInfo? = null
}

/**
 *  Factory production information 
 */
class FactoryInfo(var id: String) {
    /**
     *  Production address 
     */
    var address: String? = null
    /**
     *  Date of manufacture 
     */
    var productDate: String? = null
    /**
     *  Expiration date 
     */
    var expirationDate: String? = null
}

/**
 *  Price information 
 */
class PriceInfo(var id: String) {
    /**
     *  Ex works price 
     */
    var factoryPrice = 0f
    /**
     *  trade price 
     */
    var wholesalePrice = 0f
    /**
     *  Retail price 
     */
    var retailPrice = 0f
}

/**
 *  Product promotion information 
 */
class PromotionInfo(var id: String) {
    /**
     *  Promotion type 
     */
    var type = 0
    /**
     *  Promotion content 
     */
    var content: String? = null
    /**
     *  Effective date 
     */
    var effectiveDate: String? = null
    /**
     *  Expiration date 
     */
    var expirationDate: String? = null
}

/**
 *  Rich text information 
 */
class RichInfo(var id: String) {
    /**
     *  Description information 
     */
    var description: String? = null
    /**
     *  Picture links 
     */
    var imgUrl: String? = null
    /**
     *  Video link 
     */
    var videoUrl: String? = null
}

Case realization

Business process processing

Above shared 3 A big business process ,4 Sub business processes , We simplify it into the following 5 One processor for processing .

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

Normally, we need to call back through the interface + The way of synchronous lock , Realize the concurrency and collaboration of tasks .

AppExecutors.get().singleIO().execute {
    GetBriefInfoProcessor(binding?.logger, productId).setProcessorCallback(object :
        AbstractProcessor.ProcessorCallbackAdapter<BriefInfo?>() {
        override fun onSuccess(briefInfo: BriefInfo?) {
            val product = Product(briefInfo!!)
            val latch = CountDownLatch(4)

            // 2.1  Obtain the production information of goods 
            AppExecutors.get().networkIO().execute {
                GetFactoryInfoProcessor(
                    binding?.logger,
                    product.factoryId!!
                ).setProcessorCallback(object :
                    AbstractProcessor.ProcessorCallbackAdapter<FactoryInfo?>() {
                    override fun onSuccess(result: FactoryInfo?) {
                        product.factory = result
                        latch.countDown()
                    }
                }).process()
            }
            // 2.2  Get the price information of goods 
            AppExecutors.get().networkIO().execute {
                GetPriceInfoProcessor(
                    binding?.logger,
                    product.priceId!!
                ).setProcessorCallback(
                    object : AbstractProcessor.ProcessorCallbackAdapter<PriceInfo?>() {
                        override fun onSuccess(result: PriceInfo?) {
                            product.price = result
                            latch.countDown()
                        }
                    }).process()
            }
            // 2.3  Get promotional information about the product 
            AppExecutors.get().networkIO().execute {
                GetPromotionInfoProcessor(
                    binding?.logger,
                    product.promotionId!!
                ).setProcessorCallback(object :
                    AbstractProcessor.ProcessorCallbackAdapter<PromotionInfo?>() {
                    override fun onSuccess(result: PromotionInfo?) {
                        product.promotion = result
                        latch.countDown()
                    }
                }).process()
            }
            // 2.4  Get the rich text information of the product 
            AppExecutors.get().networkIO().execute {
                GetRichInfoProcessor(
                    binding?.logger,
                    product.richId!!
                ).setProcessorCallback(
                    object : AbstractProcessor.ProcessorCallbackAdapter<RichInfo?>() {
                        override fun onSuccess(result: RichInfo?) {
                            product.rich = result
                            latch.countDown()
                        }
                    }).process()
            }
            try {
                latch.await()
                log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
                log(" Inquiry of product information is completed , $product")
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        }
    }).process()
}
  • Kotlin Coroutine How to write it

Kotlin Coroutine Implementing parallel tasks is very simple , Just use async+await Can be done in a way , And it's very concise and clear .

mainScope.launch {
    // 1. Get product brief information 
    val briefInfo = withContext(Dispatchers.IO) {
        GetBriefInfoProcessor(binding?.logger, productId).process()
    }

    val product = Product(briefInfo)
    // 2.1  Obtain the production information of goods 
    val factory = async(Dispatchers.IO) {
        GetFactoryInfoProcessor(binding?.logger, product.factoryId!!).process()
    }
    // 2.2  Get the price information of goods 
    val price = async(Dispatchers.IO) {
        GetPriceInfoProcessor(binding?.logger, product.factoryId!!).process()
    }
    // 2.3  Get promotional information about the product 
    val promotion = async(Dispatchers.IO) {
        GetPromotionInfoProcessor(binding?.logger, product.factoryId!!).process()
    }
    // 2.4  Get the rich text information of the product 
    val rich = async(Dispatchers.IO) {
        GetRichInfoProcessor(binding?.logger, product.factoryId!!).process()
    }
    product.factory = factory.await()
    product.price = price.await()
    product.promotion = promotion.await()
    product.rich = rich.await()

    log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
    log(" Inquiry of product information is completed , $product")
}
  • Kotlin Flow How to write it

and RxJava similar , stay Kotlin Flow Execute parallel tasks in , In general use flatMapMerge and zip Combination of , Merge the task flow . But to be honest , With the above Kotlin Coroutine The implementation method is relatively cumbersome .

mainScope.launch {
    flowOf(productId)
        .map { id ->
            // 1. Get product brief information 
            GetBriefInfoProcessor(binding?.logger, id).process()
        }
        .map { briefInfo -> Product(briefInfo) }
        .flatMapMerge { product ->
            // 2.1  Obtain the production information of goods 
            flowFactory(product)
                // 2.2  Get the price information of goods 
                .zip(flowPrice(product)) { factoryInfo, priceInfo ->
                    product.apply {
                        factory = factoryInfo
                        price = priceInfo
                    }
                    // 2.3  Get promotional information about the product 
                }.zip(flowPromotion(product)) { _, promotionInfo ->
                    product.apply {
                        promotion = promotionInfo
                    }
                    // 2.4  Get the rich text information of the product 
                }.zip(flowRich(product)) { _, richInfo ->
                    product.apply {
                        rich = richInfo
                    }
                }
        }.flowOn(Dispatchers.IO)
        .collect { product ->
            log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
            log(" Inquiry of product information is completed , $product")
        }
}
  • XTask How to write it

XTask It encapsulates all business processors one by one Task in , Then parallel tasks need to pass through a ConcurrentGroupTask( Synchronize group tasks ) Package , Others are added in the normal execution order Task that will do .

XTask.getTaskChain()
    .setTaskParam(
        TaskParam.get(
            ProductTaskConstants.KEY_PRODUCT_ID,
            productId
        )
    ) // 1. Get product brief information 
    .addTask(GetBriefInfoTask(binding?.logger))
    .addTask(
        XTask.getConcurrentGroupTask(ThreadType.SYNC) // 2.1  Obtain the production information of goods 
            .addTask(GetFactoryInfoTask(binding?.logger)) // 2.2  Get the price information of goods 
            .addTask(GetPriceInfoTask(binding?.logger)) // 2.3  Get promotional information about the product 
            .addTask(GetPromotionInfoTask(binding?.logger)) // 2.4  Get the rich text information of the product 
            .addTask(GetRichInfoTask(binding?.logger))
    )
    .setTaskChainCallback(object : TaskChainCallbackAdapter() {
        override fun onTaskChainCompleted(engine: ITaskChainEngine, result: ITaskResult) {
            log(" The total time is :" + (System.currentTimeMillis() - startTime) + "ms")
            val product: Product = result.dataStore.getObject(
                ProductTaskConstants.KEY_PRODUCT,
                Product::class.java
            )
            log(" Inquiry of product information is completed , $product")
        }
    }).start()

Case execution results

  • Program execution result

  • XTask Execution log list


Use comparison to summarize

From the use comparison above , We can briefly summarize the following points :

programmatically

1.Kotlin Coroutine It follows the principle of functional programming , You can write non blocking code in a blocking way , Solve the common callback hell in concurrency . Eliminates the difficulty of collaboration between concurrent tasks , Coprocessing allows us to easily write complex concurrent code . From this point of view ,Kotlin Coroutine No doubt it's very excellent , Because it can greatly reduce the design complexity of asynchronous programs .

2.XTask It follows the principle of object-oriented programming , Each process corresponds to a concrete or abstract Task. The good thing is , Reduce the coupling between business and data structure , At the same time, it also reduces the coupling between various services . In this way, even if your data structure or business process changes greatly , The main body of the function implementation will not change greatly , More just each sub business Task Internal changes and adjustments , It really realizes high reuse and low coupling .

summary : If from the perspective of simplicity of programming , undoubtedly Kotlin Coroutine It's a complete victory , After all, this is the advantage of functional programming . But from the perspective of programming coupling , that XTask There are still some advantages . So two different programming methods , Follow two different programming principles , There is no comparison between the better and the worse .

It's hard to get started

1. If you leave kotlin Flow If you don't talk ,Kotlin Coroutine It's relatively easy to get started . Compared with RXJava for , Maybe it's better for us Android Development .

2.XTask As a Android Designed task execution framework , The function is relatively single . There are no complex operators , It's just “ Task chain 、 Mission 、 Group task 、 Task parameters and execution results ” These five elements , It is relatively simple and easy to use .

summary : On the whole , The two are basically the same , however Kotlin Coroutine There are more relevant materials , So it may be easier to get started , And more versatile .

Development efficiency

1. The biggest advantage of functional programming is that the code is concise and written quickly . At this point Kotlin Coroutine No doubt it's very excellent , Basically hang a bunch of asynchronous execution frameworks .

2.XTask Because each business sub step needs to write a Task class , In comparison, the efficiency is obviously lower .

summary : On the whole ,Kotlin Coroutine Perfect victory XTask.

Maintainability

1.Kotlin Coroutine It follows the principle of functional programming , In essence, it is process oriented programming . All business processes are strongly coupled with data , When the business process changes , It will inevitably lead to changes in the trunk code . And for developers who are new to the project, when they take over the project , Excessive exposure of internal implementation details , It is difficult to understand the main business of the project from an overall perspective , It is easy to produce the result that local modification affects the overall situation .

2.XTask It follows the principle of object-oriented programming , At the beginning of design, we strictly follow the principle of object-oriented design pattern . Fully reduce business and business 、 Coupling between business and data flow , In this way, even if your data structure or business process changes significantly , The backbone code will not change much . and XTask Have a strong logging system , It can clearly record the execution process of your current task chain and the information of your thread ( automatic ), When there is a problem in task execution , Can quickly locate the location of the problem . For developers who are new to the project , It can also quickly understand the main business of the project from the log of task execution process . After the main business process has a clear understanding, we can take a closer look at the sub business , Only in this way can we fully understand the business of the project , It is also more conducive to the maintenance of the project .

summary : On the whole ,XTask Is better than Kotlin Coroutine Of .

performance

In performance ,XTask In order to realize the isolation between business and data , The structure of shared data is designed , Comparison Kotlin Coroutine for , More data copying and data storage processes , So in terms of time and space ,Kotlin Coroutine Are better than XTask Of .


Last

Based on the above discussion ,Kotlin Coroutine On the whole, it is better than XTask Of .

  • If you are a fan of functional programming , Then it must be the choice Kotlin Coroutine; If you are a fan of object-oriented programming , that XTask It must be a good choice ;
  • If we pursue the efficiency of development , Then priority can be given to Kotlin Coroutine; From the perspective of stability and maintainability of future projects , choice XTask I will not let you down ;
  • If you use kotlin Development , Then don't think about it , Just choose Kotlin Coroutine 了 ; If you still love it very much Java Development Android, Then be sure to consider XTask.

The source code involved in this article has been placed in github On , Project home page :
https://github.com/xuexiangjys/KotlinSample

Like friends can pay attention to XTask Project home page for : https://github.com/xuexiangjys/XTask.

I am a xuexiangjys, I love learning , Like programming , betake Android Architecture research and open source project experience sharing technology up Lord . Get more information , Welcome to WeChat search official account : 【 my Android Open source journey 】

版权声明
本文为[xuexiangjys]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231612438433.html