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 】