Repository: xuexiangjys/XTask Branch: master Commit: ed9c0e79ca6e Files: 173 Total size: 698.9 KB Directory structure: gitextract_r5lsl1_p/ ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── .idea/ │ └── copyright/ │ ├── profiles_settings.xml │ └── xuexiang.xml ├── JitPackUpload.gradle ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── channel │ ├── debug.jks │ ├── multiple-channel.gradle │ ├── proguard-rules.pro │ ├── src/ │ │ ├── androidTest/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── xuexiang/ │ │ │ └── xtaskdemo/ │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── xuexiang/ │ │ │ │ └── xtaskdemo/ │ │ │ │ ├── MyApp.java │ │ │ │ ├── activity/ │ │ │ │ │ └── MainActivity.java │ │ │ │ ├── core/ │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ ├── BaseContainerFragment.java │ │ │ │ │ ├── BaseFragment.java │ │ │ │ │ ├── BaseSimpleListFragment.java │ │ │ │ │ └── SimpleListAdapter.java │ │ │ │ ├── fragment/ │ │ │ │ │ ├── CustomTaskFragment.java │ │ │ │ │ ├── MainFragment.java │ │ │ │ │ ├── TestThreadPoolFragment.java │ │ │ │ │ ├── UseCaseFragment.java │ │ │ │ │ ├── XTaskStepFragment.java │ │ │ │ │ ├── XTaskThreadFragment.java │ │ │ │ │ ├── task/ │ │ │ │ │ │ ├── Job.java │ │ │ │ │ │ ├── JobTask.java │ │ │ │ │ │ ├── StepATask.java │ │ │ │ │ │ └── StepBTask.java │ │ │ │ │ └── usecase/ │ │ │ │ │ ├── AppStartFragment.java │ │ │ │ │ ├── ComplexBusinessFragment.java │ │ │ │ │ ├── ConcurrentProcessFragment.java │ │ │ │ │ ├── appstart/ │ │ │ │ │ │ ├── job/ │ │ │ │ │ │ │ ├── AbstractMockJob.java │ │ │ │ │ │ │ ├── LongTimeJob.java │ │ │ │ │ │ │ ├── SingleJob.java │ │ │ │ │ │ │ └── TopPriorityJob.java │ │ │ │ │ │ └── task/ │ │ │ │ │ │ ├── AsyncInitTask.java │ │ │ │ │ │ └── MainInitTask.java │ │ │ │ │ ├── business/ │ │ │ │ │ │ ├── entity/ │ │ │ │ │ │ │ ├── Product.java │ │ │ │ │ │ │ ├── ProductFactory.java │ │ │ │ │ │ │ └── ProductInfo.java │ │ │ │ │ │ ├── processor/ │ │ │ │ │ │ │ ├── AbstractProcessor.java │ │ │ │ │ │ │ ├── GetProductInfoProcessor.java │ │ │ │ │ │ │ ├── GivePriceProcessor.java │ │ │ │ │ │ │ ├── PublicProductProcessor.java │ │ │ │ │ │ │ └── SearchFactoryProcessor.java │ │ │ │ │ │ └── task/ │ │ │ │ │ │ ├── AbstractTask.java │ │ │ │ │ │ ├── GetProductInfoTask.java │ │ │ │ │ │ ├── GivePriceTask.java │ │ │ │ │ │ ├── ProductTaskConstants.java │ │ │ │ │ │ ├── PublicProductTask.java │ │ │ │ │ │ └── SearchFactoryTask.java │ │ │ │ │ └── concurrent/ │ │ │ │ │ ├── entity/ │ │ │ │ │ │ ├── BriefInfo.java │ │ │ │ │ │ ├── FactoryInfo.java │ │ │ │ │ │ ├── PriceInfo.java │ │ │ │ │ │ ├── Product.java │ │ │ │ │ │ ├── PromotionInfo.java │ │ │ │ │ │ └── RichInfo.java │ │ │ │ │ ├── processor/ │ │ │ │ │ │ ├── GetBriefInfoProcessor.java │ │ │ │ │ │ ├── GetFactoryInfoProcessor.java │ │ │ │ │ │ ├── GetPriceInfoProcessor.java │ │ │ │ │ │ ├── GetPromotionInfoProcessor.java │ │ │ │ │ │ └── GetRichInfoProcessor.java │ │ │ │ │ └── task/ │ │ │ │ │ ├── GetBriefInfoTask.java │ │ │ │ │ ├── GetFactoryInfoTask.java │ │ │ │ │ ├── GetPriceInfoTask.java │ │ │ │ │ ├── GetPromotionInfoTask.java │ │ │ │ │ └── GetRichInfoTask.java │ │ │ │ └── utils/ │ │ │ │ ├── XToastUtils.java │ │ │ │ ├── sdkinit/ │ │ │ │ │ ├── UMengInit.java │ │ │ │ │ └── XBasicLibInit.java │ │ │ │ └── service/ │ │ │ │ └── JsonSerializationService.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── drawable-v24/ │ │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── layout/ │ │ │ │ ├── adapter_item_simple_list_2.xml │ │ │ │ ├── fragment_usecase_compare.xml │ │ │ │ └── fragment_usecase_template.xml │ │ │ ├── mipmap-anydpi-v26/ │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ └── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── styles_widget.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── xuexiang/ │ │ └── xtaskdemo/ │ │ └── ExampleUnitTest.java │ └── x-library-simple.gradle ├── art/ │ └── xtask.mdj ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── versions.gradle ├── xtask-core/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── xuexiang/ │ └── xtask/ │ ├── XTask.java │ ├── api/ │ │ ├── TaskChainEngine.java │ │ └── step/ │ │ ├── ConcurrentGroupTaskStep.java │ │ ├── SerialGroupTaskStep.java │ │ ├── SimpleTaskStep.java │ │ └── XTaskStep.java │ ├── core/ │ │ ├── ITaskChainCallback.java │ │ ├── ITaskChainEngine.java │ │ ├── ThreadType.java │ │ ├── param/ │ │ │ ├── IDataStore.java │ │ │ ├── ITaskParam.java │ │ │ ├── ITaskResult.java │ │ │ └── impl/ │ │ │ ├── MapDataStore.java │ │ │ ├── TaskParam.java │ │ │ └── TaskResult.java │ │ └── step/ │ │ ├── IGroupTaskStep.java │ │ ├── ITaskStep.java │ │ ├── ITaskStepController.java │ │ ├── ITaskStepHandler.java │ │ ├── ITaskStepLifecycle.java │ │ └── impl/ │ │ ├── AbstractGroupTaskStep.java │ │ ├── AbstractTaskStep.java │ │ ├── AutoDestroyTaskChainCallback.java │ │ ├── AutoNotifyTaskStepHandler.java │ │ ├── TaskChainCallbackAdapter.java │ │ └── TaskCommand.java │ └── utils/ │ ├── CancellerPoolUtils.java │ ├── CommonUtils.java │ └── TaskUtils.java └── xtask-thread/ ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src/ └── main/ ├── AndroidManifest.xml └── java/ └── com/ └── xuexiang/ └── xtask/ ├── logger/ │ ├── ILogger.java │ ├── LogcatLogger.java │ └── TaskLogger.java └── thread/ ├── XTaskExecutor.java ├── executor/ │ ├── ICategoryExecutorCore.java │ ├── IExecutorCore.java │ ├── IPriorityExecutorCore.java │ ├── IScheduledExecutorCore.java │ └── impl/ │ ├── CategoryExecutorCore.java │ ├── PriorityExecutorCore.java │ └── ScheduledExecutorCore.java ├── pool/ │ ├── DefaultScheduledThreadPoolExecutor.java │ ├── DefaultThreadPoolExecutor.java │ ├── PriorityThreadPoolExecutor.java │ ├── TaskRecordPolicy.java │ ├── TaskThreadFactory.java │ ├── base/ │ │ ├── BaseScheduledThreadPoolExecutor.java │ │ └── BaseThreadPoolExecutor.java │ └── cancel/ │ ├── CancelHandlerRunnable.java │ ├── ICancelable.java │ ├── ICanceller.java │ ├── ICancellerPool.java │ ├── IFuture.java │ ├── IScheduledFuture.java │ └── TaskCancellerPool.java ├── priority/ │ ├── IPriority.java │ ├── IPriorityCallable.java │ ├── IPriorityComparable.java │ ├── IPriorityFuture.java │ ├── IPriorityRunnable.java │ ├── IPriorityStrategy.java │ └── impl/ │ ├── DefaultPriorityCallable.java │ ├── DefaultPriorityFuture.java │ ├── DefaultPriorityRunnable.java │ ├── DefaultPriorityStrategy.java │ └── Priority.java └── utils/ ├── CancelUtils.java ├── ExecutorUtils.java └── PriorityUtils.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms custom: https://gitee.com/xuexiangjys/Resource/blob/master/doc/sponsor.md ================================================ FILE: .gitignore ================================================ *.iml .gradle /LocalRepository /keystores /local.properties /.idea/caches /.idea/codeStyles /.idea/inspectionProfiles /.idea/libraries /.idea/dictionaries /.idea/markdown-navigator /.idea/*.xml .DS_Store /build /captures .externalNativeBuild ================================================ FILE: .idea/copyright/profiles_settings.xml ================================================ ================================================ FILE: .idea/copyright/xuexiang.xml ================================================ ================================================ FILE: JitPackUpload.gradle ================================================ apply plugin: 'com.github.dcendents.android-maven' // 指定group,com.github.<用户名>,这里我默认填写的是我的github账号,请换成你自己的。 group='com.github.xuexiangjys' //--------------------------------------------- // 指定编码 tasks.withType(JavaCompile) { options.encoding = "UTF-8" } tasks.withType(Javadoc) { options.encoding = 'UTF-8' } if (project.hasProperty("android")) { // Android libraries task sourcesJar(type: Jar) { classifier = 'sources' from android.sourceSets.main.java.srcDirs } task javadoc(type: Javadoc) { failOnError false source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } } else { // Java libraries task sourcesJar(type: Jar, dependsOn: classes) { classifier = 'sources' from sourceSets.main.allSource } } javadoc { options { encoding "UTF-8" charSet 'UTF-8' author true version true links "http://docs.oracle.com/javase/7/docs/api" } } // 制作文档(Javadoc) task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } artifacts { archives javadocJar archives sourcesJar } ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018 xuexiangjys Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: README.md ================================================ # XTask [![](https://jitpack.io/v/xuexiangjys/XTask.svg)](https://jitpack.io/#xuexiangjys/XTask) [![api](https://img.shields.io/badge/API-14+-brightgreen.svg)](https://android-arsenal.com/api?level=14) [![I](https://img.shields.io/github/issues/xuexiangjys/XTask.svg)](https://github.com/xuexiangjys/XTask/issues) [![Star](https://img.shields.io/github/stars/xuexiangjys/XTask.svg)](https://github.com/xuexiangjys/XTask) 一个拓展性极强的Android任务执行框架。可自由定义和组合任务来实现你想要的功能,尤其适用于处理复杂的业务流程,可灵活添加前置任务或者调整执行顺序。例如:应用的启动初始化流程。 在使用前,请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XTask/wiki),重要的事情说三遍!!! 在使用前,请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XTask/wiki),重要的事情说三遍!!! 在使用前,请一定要仔细阅读[使用说明文档](https://github.com/xuexiangjys/XTask/wiki),重要的事情说三遍!!! ## 关于我 | 公众号 | 掘金 | 知乎 | CSDN | 简书 | 思否 | 哔哩哔哩 | 今日头条 |---------|---------|--------- |---------|---------|---------|---------|---------| | [我的Android开源之旅](https://t.1yb.co/Irse) | [点我](https://juejin.im/user/598feef55188257d592e56ed/posts) | [点我](https://www.zhihu.com/people/xuexiangjys/posts) | [点我](https://xuexiangjys.blog.csdn.net/) | [点我](https://www.jianshu.com/u/6bf605575337) | [点我](https://segmentfault.com/u/xuexiangjys) | [点我](https://space.bilibili.com/483850585) | [点我](https://img.rruu.net/image/5ff34ff7b02dd) ## X系列库快速集成 为了方便大家快速集成X系列框架库,我提供了一个空壳模版供大家参考使用: https://github.com/xuexiangjys/TemplateAppProject 除此之外,我还特别制作了几期[X系列视频教程](https://space.bilibili.com/483850585/channel/detail?cid=104998)供大家学习参考. --- ## 特征 * 支持6种线程类型方式执行任务。 * 支持任务链中各任务的执行线程调度和控制。 * 支持快捷任务创建,同时支持自定义任务。 * 支持串行和并行等组任务。 * 支持任务间数据共享。 * 支持自由组合任务执行。 * 支持任务链执行取消。 * 支持取消所有任务链和指定名称的任务链。 * 支持任务链调用顺序记录和查询。 * 支持自定义任务执行的线程池。 ## 组成结构 * 任务链`ITaskChainEngine`:任务链执行引擎,负责统筹调度各任务步骤。 * 任务步骤`ITaskStep`:负责具体任务逻辑处理。 * 数据存储仓库`IDataStore`:存放数据的仓库,主要用于保存任务参数中的数据。 * 任务参数`ITaskParam`:负责任务路径记录以及任务产生的参数管理。 * 任务执行结果`ITaskResult`:存放任务最终执行的结果以及产生的数据。 * 任务组`IGroupTaskStep`:负责统筹调度各子任务步骤。 [点击查看框架UML设计图](https://github.com/xuexiangjys/XTask/blob/master/art/xtask_uml.png) ## 日志一览 ![task_log.png](https://s4.ax1x.com/2022/02/16/HWdTHJ.png) ![task_log2.png](https://s4.ax1x.com/2022/02/16/HWwnbQ.png) --- ## 集成指南 ### 添加Gradle依赖 1.先在项目根目录的 `build.gradle` 的 `repositories` 添加: ``` allprojects { repositories { ... maven { url "https://jitpack.io" } } } ``` 2.然后在应用项目(一般是app)的 `build.gradle` 的 dependencies 添加: ``` dependencies { ... // XTask implementation 'com.github.xuexiangjys.XTask:xtask-core:1.0.2' } ``` ## 使用方法 > [XTask](https://github.com/xuexiangjys/XTask/blob/master/xtask-core/src/main/java/com/xuexiang/xtask/XTask.java)作为对外统一的API入口,所有常用的方法都能从中找到。 ### 打开调试模式 当需要定位问题,需要进行调试时,可打开调试模式,这样便可开启框架的日志。 ``` XTask.debug(true); ``` ### XTask的API介绍 方法名 | 描述 |---|--- debug | 设置是否打开调试 setLogger | 自定义日志打印 setIsLogThreadName | 设置是否打印任务执行所在的线程名,默认false getTaskChain | 获取任务链执行引擎 getTask | 获取简化的任务 getTaskBuilder | 获取简化任务的构建者 getConcurrentGroupTask | 获取并行任务组 getSerialGroupTask | 获取串行任务组 cancelTaskChain | 取消指定任务链执行 cancelAllTaskChain | 取消所有任务链执行 postToMain | 执行任务到主线程 postToMainDelay | 延迟执行任务到主线程 submit | 执行普通异步任务 emergentSubmit | 执行紧急异步任务 backgroundSubmit | 执行后台异步任务 ioSubmit | 执行io耗时的异步任务 groupSubmit | 执行分组异步任务 schedule | 执行延期任务 scheduleAtFixedRate | 执行周期任务(固定间期) scheduleWithFixedDelay | 执行周期任务(固定延期) ### 如何执行一条任务链 下面是一整个完整的例子: ``` // 1.创建一条任务链(必须) final TaskChainEngine engine = XTask.getTaskChain(); // 2.设置任务链的初始化参数(可选) engine.setTaskParam(TaskParam.get("chainName", engine.getName())); TaskParam taskParam = TaskParam.get("param1", 100) .put("param2", true); // 3.创建多个任务,并向任务链中添加(必须) XTaskStep taskStep = XTask.getTask(new TaskCommand() { @Override public void run() { ITaskParam param = getTaskParam(); Log.e(TAG, getName() + " start, param1:" + param.get("param1") + ", chainName:" + param.get("chainName")); param.put("param1", 200); param.put("param3", "this is param3!"); } }, taskParam); engine.addTask(taskStep) .addTask(XTask.getTask(new TaskCommand() { @Override public void run() { ITaskParam param = getTaskParam(); Log.e(TAG, getName() + " start, param1:" + param.get("param1") + ", param3:" + param.get("param3")); param.put("param2", false); } })); // 4.设置任务链执行回调(可选) ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); Map data = result.getDataStore().getData(); for (Map.Entry entry : data.entrySet()) { Log.e(TAG, "key:" + entry.getKey() + ", value:" + entry.getValue()); } } // 5.任务链执行(必须) }).start(); ``` 1.创建一条任务链.(必须) ``` TaskChainEngine engine = XTask.getTaskChain(); ``` 2.设置任务链的初始化参数.(可选) ``` engine.setTaskParam(TaskParam.get("chainName", engine.getName())); ``` 3.创建多个任务,并向任务链中添加.(必须) ``` // 设置任务初始化参数 TaskParam taskParam = TaskParam.get("param1", 100) .put("param2", true); XTaskStep taskStep = XTask.getTask(new TaskCommand() { @Override public void run() { // ...执行任务 } }, taskParam); engine.addTask(taskStep) .addTask(XTask.getTask(new TaskCommand() { @Override public void run() { // ...执行任务 } })); ``` 【注意】对于任务执行完成,需要注意以下两点: * 如果任务执行成功,就调用`notifyTaskSucceed`,任务执行失败,就调用`notifyTaskFailed`。这里任务无论成功还是失败,只要执行完成都需要调用`notifyTaskXXX`通知任务链该任务完成,否则任务将无法正常执行。 * `TaskCommand`和`SimpleTaskStep`默认提供了自动通知执行结果的功能,但是AbstractTaskStep没有提供,需要手动通知。 4.设置任务链执行回调.(可选) 调用setTaskChainCallback设置任务链执行回调。 ``` engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public boolean isCallBackOnMainThread() { // 回调是否返回主线程, 默认是true return false; } @Override public void onTaskChainStart(@NonNull ITaskChainEngine engine) { Log.e(TAG, "task chain start"); } @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); } @Override public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain error"); } }) ``` 5.任务链执行.(必须) 调用start执行任务链。 ``` ICanceller canceller = engine.start(); ``` ## 任务创建 创建任务有两种方式: * 通过XTask.getTask构建 * 继承`SimpleTaskStep`/`AbstractTaskStep`实现任务的自定义 ### 通过XTask创建 > 通过XTask.getTask, 传入对应的属性进行构建 属性名 | 描述 | 默认值 |---|---|--- name | 任务步骤名称 | XTaskStep-N(N为自增数) command | 任务执行内容 | / threadType | 线程执行类型 | ThreadType.ASYNC taskParam | 任务参数 | new TaskParam() taskHandler | 任务处理者 | null isAutoNotify | 是否自动通知任务执行结果 | true ``` XTaskStep taskStep = XTask.getTask(new TaskCommand() { @Override public void run() { // todo } }, ThreadType.ASYNC, taskParam); ``` ### 通过继承创建 > 通过继承`SimpleTaskStep`或者`AbstractTaskStep`实现具体功能。 ``` public class StepATask extends SimpleTaskStep { @Override public void doTask() throws Exception { // todo // 不需要手动通知任务链任务完成 } } public class StepBTask extends AbstractTaskStep { @Override public void doTask() throws Exception { // todo // 需手动通知任务链任务完成 notifyTaskSucceed(TaskResult.succeed()); } @Override public String getName() { return "StepATask"; } } ``` ## 任务执行原则 每一个任务都是依托于任务链进行流程控制。任何任务都需要遵循以下原则: * 任何任务无论失败还是成功,都需要调用`notifyTaskSucceed`或者`notifyTaskFailed`去通知任务链任务的完成情况。`TaskCommand`和`SimpleTaskStep`默认提供了自动通知执行结果的功能。 * 一旦任务链中某个任务执行失败,整个链路都停止工作。 任务类型 | 任务执行说明 |---|--- TaskCommand | 自动通知执行结果。如需手动通知,只需设置`isAutoNotify`为false即可 SimpleTaskStep | 自动通知执行结果。如需手动通知,只需重写`isAutoNotify`方法为false即可 AbstractTaskStep | 需手动通知执行结果 ### TaskCommand手动通知执行结果 在通过XTask.getTask传入TaskCommand构建Task的时候,设置`isAutoNotify`为false即可手动通知执行结果。 ``` final TaskChainEngine engine = XTask.getTaskChain(); for (int i = 0; i < 5; i++) { int finalI = i; engine.addTask(XTask.getTask(new TaskCommand() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (finalI == 2) { notifyTaskFailed(404, "任务执行失败!"); } else { notifyTaskSucceed(TaskResult.succeed()); } } }, false)); // 设置手动通知执行结果 } engine.start(); ``` ### SimpleTaskStep手动通知执行结果 重写`SimpleTaskStep`的`isAutoNotify`方法为false即可手动通知执行结果。 ``` public class StepATask extends SimpleTaskStep { @Override public void doTask() throws Exception { // todo // 手动通知任务链任务完成 notifyTaskSucceed(); } @Override protected boolean isAutoNotify() { return false; } } ``` ## 参数传递 * 任何TaskStep我们都可以通过`getTaskParam`获取任务参数和任务执行结果`ITaskParam`。 * 上一个TaskStep保存处理过的任务参数会自动带入到下一个TaskStep中去,因此最后一个TaskStep拥有之前所有任务的参数数据。 ``` XTask.getTask(new TaskCommand() { @Override public void run() { ITaskParam param = getTaskParam(); Log.e(TAG, getName() + " start, param1:" + param.get("param1") + ", param3:" + param.get("param3")); param.put("param2", false); } }) ``` ## 线程控制 设置任务的threadType类型,即可完成对任务运行线程的控制。目前支持6种线程处理方式。 类型 | 描述 | 线程池构成 |---|---|--- MAIN | 主线程(UI线程) | / ASYNC | 异步线程(开子线程,普通线程池) | 核心线程数和最大线程为CPU数,0s keepTime,LinkedBlockingQueue(128),线程优先级5 ASYNC_IO | 异步线程(开子线程,io线程池) | 核心线程数和最大线程为(2*CPU数+1),30s keepTime,LinkedBlockingQueue(128),线程优先级5 ASYNC_EMERGENT | 异步线程(开子线程,紧急线程池) | 核心线程数为2,最大线程为∞,60s keepTime,SynchronousQueue(不阻塞),线程优先级10 ASYNC_BACKGROUND | 异步线程(开子线程,优先级较低线程池) | 核心线程数和最大线程为2,0s keepTime,LinkedBlockingQueue(128),线程优先级1 SYNC | 同步线程(直接执行) | / ``` // 1.构造时传入线程 XTaskStep taskStep = XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_EMERGENT); // 2.设置线程的方法 taskStep.setThreadType(ThreadType.ASYNC_IO); ``` ## 任务组 目前共有串行任务组(SerialGroupTaskStep)和并行任务组(ConcurrentGroupTaskStep) ### 串行任务组 串行任务组是按顺序依次执行,和任务链的处理方式类似。使用XTask.getSerialGroupTask获取。 ``` final TaskChainEngine engine = XTask.getTaskChain(); SerialGroupTaskStep group1 = XTask.getSerialGroupTask("group1"); for (int i = 0; i < 5; i++) { group1.addTask(XTask.getTask(new SimpleTaskCommand(500))); } SerialGroupTaskStep group2 = XTask.getSerialGroupTask("group2"); for (int i = 0; i < 5; i++) { group2.addTask(XTask.getTask(new SimpleTaskCommand(1000))); } ICanceller canceller = engine.addTask(group1) .addTask(group2) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, path:" + result.getPath()); } }) .start(); addCanceller(canceller); ``` ### 并行任务组 并行任务组是组内所有任务同时执行,待所有任务都完成后才视为任务组完成。使用XTask.getConcurrentGroupTask获取。 ``` final TaskChainEngine engine = XTask.getTaskChain(); ConcurrentGroupTaskStep group1 = XTask.getConcurrentGroupTask("group1"); for (int i = 0; i < 5; i++) { group1.addTask(XTask.getTask(new SimpleTaskCommand(100 * (i + 1)))); } ConcurrentGroupTaskStep group2 = XTask.getConcurrentGroupTask("group2"); for (int i = 0; i < 5; i++) { group2.addTask(XTask.getTask(new SimpleTaskCommand(200 * (i + 1)))); } ICanceller canceller = engine.addTask(group1) .addTask(group2) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, path:" + result.getPath()); } }) .start(); addCanceller(canceller); ``` ## 任务链取消 > 通过调用`ICanceller`的`cancel`方法或者`XTask`的`cancelTaskChain`即可完成任务链的取消。 ### 单个任务链取消 调用任务链`TaskChainEngine`的`start`方法之后,会返回一个任务取消者`ICanceller`,调用它的`cancel`方法即可完成任务取消。 ``` ICanceller canceller = engine.start(); // 取消任务链的执行 canceller.cancel(); ``` ### 多个任务链取消 任务链在执行的时候,会默认加入到一个任务取消池,调用`XTask`的`cancelTaskChain`方法,传入多个任务链的名称或者`ICanceller`的名称,即可完成多个任务链的取消。 ``` Set pool = new HashSet<>(); ICanceller canceller1 = engine1.start(); ICanceller canceller2 = engine2.start(); // 使用ICanceller的名称 pool.add(canceller1.getName()); // 使用任务链的名称 pool.add(engine2.getName()); // 取消多个任务链的执行 XTask.cancelTaskChain(pool); ``` ### 全部任务链取消 调用`XTask`的`cancelAllTaskChain`方法取消全部任务链的执行 ``` XTask.cancelAllTaskChain(); ``` --- ## 如果觉得项目还不错,可以考虑打赏一波 > 你的打赏是我维护的动力,我将会列出所有打赏人员的清单在下方作为凭证,打赏前请留下打赏项目的备注! ![pay.png](https://raw.githubusercontent.com/xuexiangjys/Resource/master/img/pay/pay.png) ## 联系方式 > 更多资讯内容,欢迎扫描关注我的个人微信公众号:【我的Android开源之旅】 ![](https://s1.ax1x.com/2022/04/27/LbGMJH.jpg) ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' apply plugin: 'img-optimizer' //打包时,记得设置true启用 if (isNeedPackage.toBoolean() && isUseBooster.toBoolean()) { apply plugin: 'com.didiglobal.booster' } android { compileSdkVersion build_versions.target_sdk buildToolsVersion build_versions.build_tools defaultConfig { applicationId "com.xuexiang.xtaskdemo" minSdkVersion 17 targetSdkVersion build_versions.target_sdk versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } } signingConfigs { if (isNeedPackage.toBoolean()) { release { storeFile file(app_release.storeFile) storePassword app_release.storePassword keyAlias app_release.keyAlias keyPassword app_release.keyPassword } } debug { storeFile file("./debug.jks") storePassword "123456" keyAlias "debug" keyPassword "123456" } } buildTypes { release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' if (isNeedPackage.toBoolean()) { signingConfig signingConfigs.release Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newDataInputStream()) def appID = properties.getProperty("APP_ID_UMENG") if (appID != null) { buildConfigField "String", "APP_ID_UMENG", appID } else { buildConfigField "String", "APP_ID_UMENG", '""' } } else { signingConfig signingConfigs.debug buildConfigField "String", "APP_ID_UMENG", '""' } } debug { debuggable true minifyEnabled false signingConfig signingConfigs.debug buildConfigField "String", "APP_ID_UMENG", '""' } } lintOptions { abortOnError false } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation deps.junit androidTestImplementation deps.runner androidTestImplementation deps.espresso.core // 任务库 implementation project(':xtask-core') // implementation 'com.github.xuexiangjys.XTask:xtask-core:1.0.2' // rxjava3 implementation 'io.reactivex.rxjava3:rxandroid:3.0.0' implementation 'io.reactivex.rxjava3:rxjava:3.1.3' implementation deps.androidx.multidex // 屏幕适配AutoSize implementation 'me.jessyan:autosize:1.2.1' // umeng统计 implementation 'com.umeng.umsdk:common:9.3.8' implementation 'com.umeng.umsdk:asms:1.2.1' // 美团多渠道打包 implementation 'com.meituan.android.walle:library:1.1.6' } // x-library依赖脚本 apply from: 'x-library-simple.gradle' // walle多渠道打包 apply from: 'multiple-channel.gradle' ================================================ FILE: app/channel ================================================ # 美团 meituan # 三星 samsungapps # 小米 xiaomi # 91助手 91com # 魅族 meizu # 豌豆荚 wandou # Google Play googleplay # 百度 baidu # 360 360cn # 应用宝 myapp # 华为 huawei # 蒲公英 pgyer github ================================================ FILE: app/multiple-channel.gradle ================================================ apply plugin: 'walle' walle { // 指定渠道包的输出路径 apkOutputFolder = new File("${project.buildDir}/outputs/channels") // 定制渠道包的APK的文件名称 apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk' // 渠道配置文件 channelFile = new File("${project.getProjectDir()}/channel") } ================================================ FILE: app/proguard-rules.pro ================================================ #=========================================基础不变的混淆配置=========================================## #指定代码的压缩级别 -optimizationpasses 5 #包名不混合大小写 -dontusemixedcaseclassnames #不去忽略非公共的库类 -dontskipnonpubliclibraryclasses # 指定不去忽略非公共的库的类的成员 -dontskipnonpubliclibraryclassmembers #优化 不优化输入的类文件 -dontoptimize #预校验 -dontpreverify #混淆时是否记录日志 -verbose # 混淆时所采用的算法 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #保护注解 -keepattributes *Annotation* #忽略警告 -ignorewarnings ##记录生成的日志数据,gradle build时在本项目根目录输出## #apk 包内所有 class 的内部结构 -dump class_files.txt #未混淆的类和成员 -printseeds seeds.txt #列出从 apk 中删除的代码 -printusage unused.txt #混淆前后的映射 -printmapping mapping.txt # 并保留源文件名为"Proguard"字符串,而非原始的类名 并保留行号 -keepattributes SourceFile,LineNumberTable ########记录生成的日志数据,gradle build时 在本项目根目录输出-end##### #需要保留的东西 # 保持哪些类不被混淆 -keep public class * extends android.app.Fragment -keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider -keep public class * extends android.app.backup.BackupAgentHelper -keep public class * extends android.preference.Preference -keep public class * extends android.support.v4.** -keep public class com.android.vending.licensing.ILicensingService #如果有引用v4包可以添加下面这行 -keep public class * extends android.support.v4.app.Fragment ##########JS接口类不混淆,否则执行不了 -dontwarn com.android.JsInterface.** -keep class com.android.JsInterface.** {*; } #极光推送和百度lbs android sdk一起使用proguard 混淆的问题#http的类被混淆后,导致apk定位失败,保持apache 的http类不被混淆就好了 -dontwarn org.apache.** -keep class org.apache.**{ *; } -keep public class * extends android.view.View { public (android.content.Context); public (android.content.Context, android.util.AttributeSet); public (android.content.Context, android.util.AttributeSet, int); public void set*(...); } #保持 native 方法不被混淆 -keepclasseswithmembernames class * { native ; } #保持自定义控件类不被混淆 -keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet); } #保持自定义控件类不被混淆 -keepclassmembers class * extends android.app.Activity { public void *(android.view.View); } #保持 Parcelable 不被混淆 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; } #保持 Serializable 不被混淆 -keepnames class * implements java.io.Serializable #保持 Serializable 不被混淆并且enum 类也不被混淆 -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private static final java.io.ObjectStreamField[] serialPersistentFields; !static !transient ; !private ; !private ; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } #保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -keepclassmembers class * { public void *ButtonClicked(android.view.View); } #不混淆资源类 -keep class **.R$* {*;} #===================================混淆保护自己项目的部分代码以及引用的第三方jar包library=============================####### #如果引用了v4或者v7包 -dontwarn android.support.** # AndroidX 防止混淆 -dontwarn com.google.android.material.** -dontnote com.google.android.material.** -dontwarn androidx.** -keep class com.google.android.material.** {*;} -keep class androidx.** {*;} -keep public class * extends androidx.** -keep interface androidx.** {*;} -keepclassmembers class * { @androidx.annotation.Keep *; } # zxing -dontwarn com.google.zxing.** -keep class com.google.zxing.**{*;} #SignalR推送 -keep class microsoft.aspnet.signalr.** { *; } # 极光推送混淆 -dontoptimize -dontpreverify -dontwarn cn.jpush.** -keep class cn.jpush.** { *; } -dontwarn cn.jiguang.** -keep class cn.jiguang.** { *; } # 数据库框架OrmLite -keepattributes *DatabaseField* -keepattributes *DatabaseTable* -keepattributes *SerializedName* -keep class com.j256.** -keepclassmembers class com.j256.** { *; } -keep enum com.j256.** -keepclassmembers enum com.j256.** { *; } -keep interface com.j256.** -keepclassmembers interface com.j256.** { *; } #XHttp2 -keep class com.xuexiang.xhttp2.model.** { *; } -keep class com.xuexiang.xhttp2.cache.model.** { *; } -keep class com.xuexiang.xhttp2.cache.stategy.**{*;} -keep class com.xuexiang.xhttp2.annotation.** { *; } #okhttp -dontwarn com.squareup.okhttp3.** -keep class com.squareup.okhttp3.** { *;} -dontwarn okio.** -dontwarn javax.annotation.Nullable -dontwarn javax.annotation.ParametersAreNonnullByDefault -dontwarn javax.annotation.** #如果用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错 -keepattributes Signature -keep class com.google.gson.stream.** { *; } -keepattributes EnclosingMethod -keep class org.xz_sale.entity.**{*;} -keep class com.google.gson.** {*;} -keep class com.google.**{*;} -keep class sun.misc.Unsafe { *; } -keep class com.google.gson.stream.** { *; } -keep class com.google.gson.examples.android.model.** { *; } # Glide -keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * extends com.bumptech.glide.module.AppGlideModule -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { **[] $VALUES; public *; } # Retrofit -dontwarn retrofit2.** -keep class retrofit2.** { *; } -keepattributes Exceptions # RxJava RxAndroid -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } -dontwarn okio.** -dontwarn javax.annotation.Nullable -dontwarn javax.annotation.ParametersAreNonnullByDefault -dontwarn javax.annotation.** # fastjson -dontwarn com.alibaba.fastjson.** -keep class com.alibaba.fastjson.** { *; } -keepattributes Signature # xpage -keep class com.xuexiang.xpage.annotation.** { *; } -keep class com.xuexiang.xpage.config.** { *; } # xaop -keep @com.xuexiang.xaop.annotation.* class * {*;} -keep @org.aspectj.lang.annotation.* class * {*;} -keep class * { @com.xuexiang.xaop.annotation.* ; @org.aspectj.lang.annotation.* ; } -keepclassmembers class * { @com.xuexiang.xaop.annotation.* ; @org.aspectj.lang.annotation.* ; } # xrouter -keep public class com.xuexiang.xrouter.routes.**{*;} -keep class * implements com.xuexiang.xrouter.facade.template.ISyringe{*;} # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口 -keep interface * implements com.xuexiang.xrouter.facade.template.IProvider # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 -keep class * implements com.xuexiang.xrouter.facade.template.IProvider # xupdate -keep class com.xuexiang.xupdate.entity.** { *; } # xvideo -keep class com.xuexiang.xvideo.jniinterface.** { *; } # xipc -keep @com.xuexiang.xipc.annotation.* class * {*;} -keep class * { @com.xuexiang.xipc.annotation.* ; } -keepclassmembers class * { @com.xuexiang.xipc.annotation.* ; } # umeng统计 -keep class com.umeng.** {*;} -keepclassmembers class * { public (org.json.JSONObject); } -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # materialedittext -keep class com.xuexiang.xui.widget.edittext.materialedittext.** { *; } ================================================ FILE: app/src/androidTest/java/com/xuexiang/xtaskdemo/ExampleInstrumentedTest.java ================================================ package com.xuexiang.xtaskdemo; import android.content.Context; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.assertEquals; /** * Instrumented test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); assertEquals("com.xuexiang.templateproject", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/MyApp.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo; import android.app.Application; import android.content.Context; import androidx.multidex.MultiDex; import com.xuexiang.xtask.XTask; import com.xuexiang.xtaskdemo.utils.sdkinit.UMengInit; import com.xuexiang.xtaskdemo.utils.sdkinit.XBasicLibInit; /** * @author xuexiang * @since 2018/11/7 下午1:12 */ public class MyApp extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); //解决4.x运行崩溃的问题 MultiDex.install(this); } @Override public void onCreate() { super.onCreate(); initLibs(); // 设置XTask的调试模式 XTask.debug(isDebug()); // // 设置是否打印任务执行所在的线程名 // XTask.setIsLogThreadName(true); } /** * 初始化基础库 */ private void initLibs() { XBasicLibInit.init(this); UMengInit.init(this); } /** * @return 当前app是否是调试开发模式 */ public static boolean isDebug() { return BuildConfig.DEBUG; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/activity/MainActivity.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.activity; import android.os.Bundle; import com.xuexiang.xtaskdemo.core.BaseActivity; import com.xuexiang.xtaskdemo.fragment.MainFragment; /** * 程序入口,空壳容器 * * @author xuexiang * @since 2019-07-07 23:53 */ public class MainActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); openPage(MainFragment.class); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/core/BaseActivity.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.core; import android.content.Context; import android.os.Bundle; import com.xuexiang.xpage.base.XPageActivity; import com.xuexiang.xpage.base.XPageFragment; import com.xuexiang.xpage.core.CoreSwitchBean; import com.xuexiang.xrouter.facade.service.SerializationService; import com.xuexiang.xrouter.launcher.XRouter; import butterknife.ButterKnife; import butterknife.Unbinder; import io.github.inflationx.viewpump.ViewPumpContextWrapper; /** * 基础容器Activity * * @author XUE * @since 2019/3/22 11:21 */ public class BaseActivity extends XPageActivity { Unbinder mUnbinder; @Override protected void attachBaseContext(Context newBase) { //注入字体 super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase)); } @Override protected void onCreate(Bundle savedInstanceState) { initStatusBarStyle(); super.onCreate(savedInstanceState); mUnbinder = ButterKnife.bind(this); } /** * 初始化状态栏的样式 */ protected void initStatusBarStyle() { } /** * 打开fragment * * @param clazz 页面类 * @param addToBackStack 是否添加到栈中 * @return 打开的fragment对象 */ public T openPage(Class clazz, boolean addToBackStack) { CoreSwitchBean page = new CoreSwitchBean(clazz) .setAddToBackStack(addToBackStack); return (T) openPage(page); } /** * 打开fragment * * @return 打开的fragment对象 */ public T openNewPage(Class clazz) { CoreSwitchBean page = new CoreSwitchBean(clazz) .setNewActivity(true); return (T) openPage(page); } /** * 切换fragment * * @param clazz 页面类 * @return 打开的fragment对象 */ public T switchPage(Class clazz) { return openPage(clazz, false); } /** * 序列化对象 * * @param object * @return */ public String serializeObject(Object object) { return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); } @Override protected void onRelease() { mUnbinder.unbind(); super.onRelease(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/core/BaseContainerFragment.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.core; import android.content.res.Configuration; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import com.umeng.analytics.MobclickAgent; import com.xuexiang.xaop.annotation.SingleClick; import com.xuexiang.xpage.base.XPageContainerListFragment; import com.xuexiang.xui.widget.actionbar.TitleBar; import com.xuexiang.xui.widget.actionbar.TitleUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.xuexiang.xtaskdemo.core.SimpleListAdapter.KEY_SUB_TITLE; import static com.xuexiang.xtaskdemo.core.SimpleListAdapter.KEY_TITLE; /** * 修改列表样式为主副标题显示 * * @author xuexiang * @since 2018/11/22 上午11:26 */ public abstract class BaseContainerFragment extends XPageContainerListFragment { @Override protected void initPage() { initTitle(); initViews(); initListeners(); } protected TitleBar initTitle() { return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), new View.OnClickListener() { @Override public void onClick(View v) { popToBack(); } }); } @Override protected void initData() { mSimpleData = initSimpleData(mSimpleData); List> data = new ArrayList<>(); for (String content : mSimpleData) { Map item = new HashMap<>(); int index = content.indexOf("\n"); if (index > 0) { item.put(KEY_TITLE, String.valueOf(content.subSequence(0, index))); item.put(KEY_SUB_TITLE, String.valueOf(content.subSequence(index + 1, content.length()))); } else { item.put(KEY_TITLE, content); item.put(KEY_SUB_TITLE, ""); } data.add(item); } getListView().setAdapter(new SimpleListAdapter(getContext(), data)); initSimply(); } @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { onItemClick(view, position); } @SingleClick private void onItemClick(View view, int position) { onItemClick(position); } @Override public void onDestroyView() { getListView().setOnItemClickListener(null); super.onDestroyView(); } @Override public void onConfigurationChanged(Configuration newConfig) { //屏幕旋转时刷新一下title super.onConfigurationChanged(newConfig); ViewGroup root = (ViewGroup) getRootView(); if (root.getChildAt(0) instanceof TitleBar) { root.removeViewAt(0); initTitle(); } } @Override public void onResume() { super.onResume(); MobclickAgent.onPageStart(getPageName()); } @Override public void onPause() { super.onPause(); MobclickAgent.onPageEnd(getPageName()); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/core/BaseFragment.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.core; import android.content.res.Configuration; import android.os.Parcelable; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import com.umeng.analytics.MobclickAgent; import com.xuexiang.xpage.base.XPageActivity; import com.xuexiang.xpage.base.XPageFragment; import com.xuexiang.xpage.core.PageOption; import com.xuexiang.xpage.enums.CoreAnim; import com.xuexiang.xpage.utils.Utils; import com.xuexiang.xrouter.facade.service.SerializationService; import com.xuexiang.xrouter.launcher.XRouter; import com.xuexiang.xui.utils.WidgetUtils; import com.xuexiang.xui.widget.actionbar.TitleBar; import com.xuexiang.xui.widget.actionbar.TitleUtils; import com.xuexiang.xui.widget.progress.loading.IMessageLoader; import java.io.Serializable; import java.lang.reflect.Type; /** * 基础fragment * * @author xuexiang * @since 2018/5/25 下午3:44 */ public abstract class BaseFragment extends XPageFragment { private IMessageLoader mIMessageLoader; @Override protected void initPage() { initTitle(); initViews(); initListeners(); } protected TitleBar initTitle() { return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), v -> popToBack()); } @Override protected void initListeners() { } public IMessageLoader getMessageLoader() { if (mIMessageLoader == null) { mIMessageLoader = WidgetUtils.getMiniLoadingDialog(getContext()); } return mIMessageLoader; } public IMessageLoader getMessageLoader(String message) { if (mIMessageLoader == null) { mIMessageLoader = WidgetUtils.getMiniLoadingDialog(getContext(), message); } else { mIMessageLoader.updateMessage(message); } return mIMessageLoader; } @Override public void onConfigurationChanged(Configuration newConfig) { //屏幕旋转时刷新一下title super.onConfigurationChanged(newConfig); ViewGroup root = (ViewGroup) getRootView(); if (root.getChildAt(0) instanceof TitleBar) { root.removeViewAt(0); initTitle(); } } @Override public void onResume() { super.onResume(); MobclickAgent.onPageStart(getPageName()); } @Override public void onPause() { super.onPause(); MobclickAgent.onPageEnd(getPageName()); } //==============================页面跳转api===================================// /** * 打开一个新的页面【建议只在主tab页使用】 * * @param clazz 页面的类 * @param * @return */ public Fragment openNewPage(Class clazz) { return new PageOption(clazz) .setNewActivity(true) .open(this); } /** * 打开一个新的页面【建议只在主tab页使用】 * * @param pageName 页面名 * @param * @return */ public Fragment openNewPage(String pageName) { return new PageOption(pageName) .setAnim(CoreAnim.slide) .setNewActivity(true) .open(this); } /** * 打开一个新的页面【建议只在主tab页使用】 * * @param clazz 页面的类 * @param containActivityClazz 页面容器 * @param * @return */ public Fragment openNewPage(Class clazz, @NonNull Class containActivityClazz) { return new PageOption(clazz) .setNewActivity(true) .setContainActivityClazz(containActivityClazz) .open(this); } /** * 打开一个新的页面【建议只在主tab页使用】 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openNewPage(Class clazz, String key, Object value) { PageOption option = new PageOption(clazz).setNewActivity(true); return openPage(option, key, value); } public Fragment openPage(PageOption option, String key, Object value) { if (value instanceof Integer) { option.putInt(key, (Integer) value); } else if (value instanceof Float) { option.putFloat(key, (Float) value); } else if (value instanceof String) { option.putString(key, (String) value); } else if (value instanceof Boolean) { option.putBoolean(key, (Boolean) value); } else if (value instanceof Long) { option.putLong(key, (Long) value); } else if (value instanceof Double) { option.putDouble(key, (Double) value); } else if (value instanceof Parcelable) { option.putParcelable(key, (Parcelable) value); } else if (value instanceof Serializable) { option.putSerializable(key, (Serializable) value); } else { option.putString(key, serializeObject(value)); } return option.open(this); } /** * 打开页面 * * @param clazz 页面的类 * @param addToBackStack 是否加入回退栈 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, boolean addToBackStack, String key, String value) { return new PageOption(clazz) .setAddToBackStack(addToBackStack) .putString(key, value) .open(this); } /** * 打开页面 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, String key, Object value) { return openPage(clazz, true, key, value); } /** * 打开页面 * * @param clazz 页面的类 * @param addToBackStack 是否加入回退栈 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, boolean addToBackStack, String key, Object value) { PageOption option = new PageOption(clazz).setAddToBackStack(addToBackStack); return openPage(option, key, value); } /** * 打开页面 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, String key, String value) { return new PageOption(clazz) .putString(key, value) .open(this); } /** * 打开页面,需要结果返回 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param requestCode 请求码 * @param * @return */ public Fragment openPageForResult(Class clazz, String key, Object value, int requestCode) { PageOption option = new PageOption(clazz).setRequestCode(requestCode); return openPage(option, key, value); } /** * 打开页面,需要结果返回 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param requestCode 请求码 * @param * @return */ public Fragment openPageForResult(Class clazz, String key, String value, int requestCode) { return new PageOption(clazz) .setRequestCode(requestCode) .putString(key, value) .open(this); } /** * 打开页面,需要结果返回 * * @param clazz 页面的类 * @param requestCode 请求码 * @param * @return */ public Fragment openPageForResult(Class clazz, int requestCode) { return new PageOption(clazz) .setRequestCode(requestCode) .open(this); } /** * 序列化对象 * * @param object 需要序列化的对象 * @return 序列化结果 */ public String serializeObject(Object object) { return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); } /** * 反序列化对象 * * @param input 反序列化的内容 * @param clazz 类型 * @return 反序列化结果 */ public T deserializeObject(String input, Type clazz) { return XRouter.getInstance().navigation(SerializationService.class).parseObject(input, clazz); } @Override protected void hideCurrentPageSoftInput() { if (getActivity() == null) { return; } // 记住,要在xml的父布局加上android:focusable="true" 和 android:focusableInTouchMode="true" Utils.hideSoftInputClearFocus(getActivity().getCurrentFocus()); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/core/BaseSimpleListFragment.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.core; import android.content.res.Configuration; import android.os.Parcelable; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import com.umeng.analytics.MobclickAgent; import com.xuexiang.xpage.base.XPageActivity; import com.xuexiang.xpage.base.XPageFragment; import com.xuexiang.xpage.base.XPageSimpleListFragment; import com.xuexiang.xpage.core.PageOption; import com.xuexiang.xpage.enums.CoreAnim; import com.xuexiang.xrouter.facade.service.SerializationService; import com.xuexiang.xrouter.launcher.XRouter; import com.xuexiang.xui.widget.actionbar.TitleBar; import com.xuexiang.xui.widget.actionbar.TitleUtils; import java.io.Serializable; /** * @author xuexiang * @since 2018/12/29 下午12:41 */ public abstract class BaseSimpleListFragment extends XPageSimpleListFragment { @Override protected void initPage() { initTitle(); initViews(); initListeners(); } protected TitleBar initTitle() { return TitleUtils.addTitleBarDynamic((ViewGroup) getRootView(), getPageTitle(), new View.OnClickListener() { @Override public void onClick(View v) { popToBack(); } }); } @Override public void onConfigurationChanged(Configuration newConfig) { //屏幕旋转时刷新一下title super.onConfigurationChanged(newConfig); ViewGroup root = (ViewGroup) getRootView(); if (root.getChildAt(0) instanceof TitleBar) { root.removeViewAt(0); initTitle(); } } @Override public void onResume() { super.onResume(); MobclickAgent.onPageStart(getPageName()); } @Override public void onPause() { super.onPause(); MobclickAgent.onPageEnd(getPageName()); } //==============================页面跳转api===================================// /** * 打开一个新的页面【建议只在主tab页使用】 * * @param clazz 页面的类 * @param * @return */ public Fragment openNewPage(Class clazz) { return new PageOption(clazz) .setNewActivity(true) .open(this); } /** * 打开一个新的页面【建议只在主tab页使用】 * * @param pageName 页面名 * @param * @return */ public Fragment openNewPage(String pageName) { return new PageOption(pageName) .setAnim(CoreAnim.slide) .setNewActivity(true) .open(this); } /** * 打开一个新的页面【建议只在主tab页使用】 * * @param clazz 页面的类 * @param containActivityClazz 页面容器 * @param * @return */ public Fragment openNewPage(Class clazz, @NonNull Class containActivityClazz) { return new PageOption(clazz) .setNewActivity(true) .setContainActivityClazz(containActivityClazz) .open(this); } /** * 打开一个新的页面【建议只在主tab页使用】 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openNewPage(Class clazz, String key, Object value) { PageOption option = new PageOption(clazz).setNewActivity(true); return openPage(option, key, value); } public Fragment openPage(PageOption option, String key, Object value) { if (value instanceof Integer) { option.putInt(key, (Integer) value); } else if (value instanceof Float) { option.putFloat(key, (Float) value); } else if (value instanceof String) { option.putString(key, (String) value); } else if (value instanceof Boolean) { option.putBoolean(key, (Boolean) value); } else if (value instanceof Long) { option.putLong(key, (Long) value); } else if (value instanceof Double) { option.putDouble(key, (Double) value); } else if (value instanceof Parcelable) { option.putParcelable(key, (Parcelable) value); } else if (value instanceof Serializable) { option.putSerializable(key, (Serializable) value); } else { option.putString(key, serializeObject(value)); } return option.open(this); } /** * 打开页面 * * @param clazz 页面的类 * @param addToBackStack 是否加入回退栈 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, boolean addToBackStack, String key, String value) { return new PageOption(clazz) .setAddToBackStack(addToBackStack) .putString(key, value) .open(this); } /** * 打开页面 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, String key, Object value) { return openPage(clazz, true, key, value); } /** * 打开页面 * * @param clazz 页面的类 * @param addToBackStack 是否加入回退栈 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, boolean addToBackStack, String key, Object value) { PageOption option = new PageOption(clazz).setAddToBackStack(addToBackStack); return openPage(option, key, value); } /** * 打开页面 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param * @return */ public Fragment openPage(Class clazz, String key, String value) { return new PageOption(clazz) .putString(key, value) .open(this); } /** * 打开页面,需要结果返回 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param requestCode 请求码 * @param * @return */ public Fragment openPageForResult(Class clazz, String key, Object value, int requestCode) { PageOption option = new PageOption(clazz).setRequestCode(requestCode); return openPage(option, key, value); } /** * 打开页面,需要结果返回 * * @param clazz 页面的类 * @param key 入参的键 * @param value 入参的值 * @param requestCode 请求码 * @param * @return */ public Fragment openPageForResult(Class clazz, String key, String value, int requestCode) { return new PageOption(clazz) .setRequestCode(requestCode) .putString(key, value) .open(this); } /** * 打开页面,需要结果返回 * * @param clazz 页面的类 * @param requestCode 请求码 * @param * @return */ public Fragment openPageForResult(Class clazz, int requestCode) { return new PageOption(clazz) .setRequestCode(requestCode) .open(this); } /** * 序列化对象 * * @param object 需要序列化的对象 * @return 序列化结果 */ public String serializeObject(Object object) { return XRouter.getInstance().navigation(SerializationService.class).object2Json(object); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/core/SimpleListAdapter.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.core; import android.content.Context; import android.view.View; import android.widget.TextView; import com.xuexiang.xtaskdemo.R; import com.xuexiang.xui.adapter.listview.BaseListAdapter; import com.xuexiang.xutil.common.StringUtils; import java.util.List; import java.util.Map; /** * 主副标题显示适配器 * * @author xuexiang * @since 2018/12/19 上午12:19 */ public class SimpleListAdapter extends BaseListAdapter, SimpleListAdapter.ViewHolder> { public static final String KEY_TITLE = "title"; public static final String KEY_SUB_TITLE = "sub_title"; public SimpleListAdapter(Context context, List> data) { super(context, data); } @Override protected ViewHolder newViewHolder(View convertView) { ViewHolder holder = new ViewHolder(); holder.mTvTitle = convertView.findViewById(R.id.tv_title); holder.mTvSubTitle = convertView.findViewById(R.id.tv_sub_title); return holder; } @Override protected int getLayoutId() { return R.layout.adapter_item_simple_list_2; } @Override protected void convert(ViewHolder holder, Map item, int position) { holder.mTvTitle.setText(item.get(KEY_TITLE)); if (!StringUtils.isEmpty(item.get(KEY_SUB_TITLE))) { holder.mTvSubTitle.setText(item.get(KEY_SUB_TITLE)); holder.mTvSubTitle.setVisibility(View.VISIBLE); } else { holder.mTvSubTitle.setVisibility(View.GONE); } } public static class ViewHolder { /** * 标题 */ public TextView mTvTitle; /** * 副标题 */ public TextView mTvSubTitle; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/CustomTaskFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment; import android.util.Log; import androidx.annotation.NonNull; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.api.TaskChainEngine; import com.xuexiang.xtask.api.step.SimpleTaskStep; import com.xuexiang.xtask.core.ITaskChainEngine; import com.xuexiang.xtask.core.ThreadType; import com.xuexiang.xtask.core.param.ITaskParam; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtask.core.param.impl.TaskParam; import com.xuexiang.xtask.core.step.impl.TaskChainCallbackAdapter; import com.xuexiang.xtask.thread.pool.cancel.ICanceller; import com.xuexiang.xtaskdemo.core.BaseSimpleListFragment; import com.xuexiang.xtaskdemo.fragment.task.Job; import com.xuexiang.xtaskdemo.fragment.task.JobTask; import com.xuexiang.xtaskdemo.fragment.task.StepATask; import com.xuexiang.xtaskdemo.fragment.task.StepBTask; import com.xuexiang.xutil.common.ObjectUtils; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author xuexiang * @since 2/1/22 11:07 PM */ @Page(name = "自定义任务使用\n通过继承SimpleTaskStep/AbstractTaskStep实现") public class CustomTaskFragment extends BaseSimpleListFragment { public static final String TAG = "CustomTaskFragment"; private Set mPool = new HashSet<>(); @Override protected List initSimpleData(List lists) { lists.add("简单继承SimpleTaskStep使用"); lists.add("简单继承AbstractTaskStep使用"); lists.add("复杂任务使用"); return lists; } @Override protected void onItemClick(int position) { switch (position) { case 0: doSimpleTaskStep(); break; case 1: doSimpleAbstractTaskStep(); break; case 2: doComplexTaskStep(); break; default: break; } } /** * 简单继承SimpleTaskStep使用 */ private void doSimpleTaskStep() { final TaskChainEngine engine = XTask.getTaskChain(); ICanceller canceller = engine .addTask(new SimpleTaskStep("TaskStep1", ThreadType.ASYNC_IO) { @Override public void doTask() throws Exception { Log.e(CustomTaskFragment.TAG, "doing TaskStep1...., thread:" + Thread.currentThread().getName()); // 模拟耗时 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } getTaskParam().put(StepATask.KEY_TOTAL, 123); } }) .addTask(new SimpleTaskStep("TaskStep2", ThreadType.SYNC) { @Override public void doTask() throws Exception { Log.e(CustomTaskFragment.TAG, "doing TaskStep2...., thread:" + Thread.currentThread().getName()); // 模拟耗时 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } ITaskParam param = getTaskParam(); param.put(StepATask.KEY_TOTAL, param.getInt(StepATask.KEY_TOTAL) + 321); notifyTaskSucceed(); } @Override protected boolean isAutoNotify() { // 设置手动通知执行结果 return false; } }) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName() + ", result:" + result.getDataStore().getInt(StepATask.KEY_TOTAL)); } }) .start(); addCanceller(canceller); } /** * 简单继承AbstractTaskStep使用 */ private void doSimpleAbstractTaskStep() { final TaskChainEngine engine = XTask.getTaskChain(); ICanceller canceller = engine.addTask(new StepATask()) .addTask(new StepBTask()) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, result:" + result.getDataStore().getInt(StepATask.KEY_TOTAL)); } }) .start(); addCanceller(canceller); } /** * 复杂任务使用 */ private void doComplexTaskStep() { final TaskChainEngine engine = XTask.getTaskChain(); TaskParam taskParam = TaskParam.get(Job.KEY_JOB_TARGET, 100); engine.setTaskParam(taskParam); for (int i = 0; i < 5; i++) { engine.addTask(new JobTask()); } ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, result:" + result.getDataStore().getInt(Job.KEY_JOB_TARGET)); } }).start(); addCanceller(canceller); } private void addCanceller(ICanceller canceller) { if (canceller != null) { mPool.add(canceller.getName()); } } @Override public void onDestroyView() { if (!ObjectUtils.isEmpty(mPool)) { XTask.cancelTaskChain(mPool); } super.onDestroyView(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/MainFragment.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment; import android.view.KeyEvent; import com.xuexiang.xtaskdemo.core.BaseContainerFragment; import com.xuexiang.xtaskdemo.utils.XToastUtils; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xpage.enums.CoreAnim; import com.xuexiang.xui.widget.actionbar.TitleBar; import com.xuexiang.xutil.XUtil; import com.xuexiang.xutil.common.ClickUtils; /** * 主页面 * * @author xuexiang * @since 2018/11/7 下午1:16 */ @Page(name = "XTask", anim = CoreAnim.none) public class MainFragment extends BaseContainerFragment implements ClickUtils.OnClick2ExitListener { @Override protected Class[] getPagesClasses() { return new Class[]{ //此处填写fragment XTaskStepFragment.class, CustomTaskFragment.class, UseCaseFragment.class, XTaskThreadFragment.class, TestThreadPoolFragment.class }; } @Override protected TitleBar initTitle() { return super.initTitle().setLeftClickListener(view -> ClickUtils.exitBy2Click(2000, this)); } /** * 菜单、返回键响应 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { ClickUtils.exitBy2Click(2000, this); } return true; } @Override public void onRetry() { XToastUtils.toast("再按一次退出程序"); } @Override public void onExit() { XUtil.exitApp(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/TestThreadPoolFragment.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment; import android.util.Log; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.thread.priority.IPriority; import com.xuexiang.xtask.thread.pool.PriorityThreadPoolExecutor; import com.xuexiang.xtaskdemo.core.BaseSimpleListFragment; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * 线程池测试页面 * * @author xuexiang * @since 2019-07-08 00:52 */ @Page(name = "线程池测试页面") public class TestThreadPoolFragment extends BaseSimpleListFragment { private static final String TAG = "TestFragment"; @Override protected List initSimpleData(List lists) { lists.add("优先级线程池测试Runnable的优先级执行"); lists.add("普通线程池测试Runnable的执行"); lists.add("测试线程池利用率"); lists.add("线程优先级"); return lists; } @Override protected void onItemClick(int position) { final Runnable r1 = getRunnable("r1"); final Runnable r2 = getRunnable("r2"); final Runnable r3 = getRunnable("r3"); final Runnable r4 = getRunnable("r4"); final Runnable r5 = getRunnable("r5"); switch (position) { case 0: PriorityThreadPoolExecutor executor = PriorityThreadPoolExecutor.newBuilder(1).build(); // 优先级大的优先执行 executor.execute(r5, 1); Log.d(TAG, "==== add r5 ===="); executor.execute(r4, 2); Log.d(TAG, "==== add r4 ===="); executor.execute(r3, 3); Log.d(TAG, "==== add r3 ===="); executor.execute(r2, 4); Log.d(TAG, "==== add r2 ===="); executor.execute(r1, 5); Log.d(TAG, "==== add r1 ===="); break; case 1: ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.execute(r5); Log.d(TAG, "==== add r5 ===="); executorService.execute(r4); Log.d(TAG, "==== add r4 ===="); executorService.execute(r3); Log.d(TAG, "==== add r3 ===="); executorService.execute(r2); Log.d(TAG, "==== add r2 ===="); executorService.execute(r1); Log.d(TAG, "==== add r1 ===="); break; case 2: Log.d(TAG, "cpu count:" + Runtime.getRuntime().availableProcessors()); // 任务的最大数量 = maximumPoolSize + BlockingQueue.size, 这里length>30就会报错 int length = 30; ExecutorService fixedThreadPool = new ThreadPoolExecutor(5, 20, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10)); List list = new ArrayList<>(length); for (int i = 0; i < length; i++) { list.add(getRunnable("r" + i)); } for (int i = 0; i < list.size(); i++) { fixedThreadPool.execute(list.get(i)); } break; case 3: Thread thread1 = new Thread(() -> { Log.e(TAG, "MIN_PRIORITY start..."); }); thread1.setPriority(Thread.MIN_PRIORITY); Thread thread2 = new Thread(() -> { Log.e(TAG, "NORM_PRIORITY start..."); }); thread2.setPriority(Thread.NORM_PRIORITY); Thread thread3 = new Thread(() -> { Log.e(TAG, "MAX_PRIORITY start..."); }); thread3.setPriority(Thread.MAX_PRIORITY); thread1.start(); thread2.start(); thread3.start(); break; default: break; } } private static Runnable getRunnable(final String name) { return new Runnable() { @Override public void run() { Integer priority = getPriority(this); if (priority != null) { Log.d(TAG, "==[execute start]== " + name + ", priority=" + priority); } else { Log.d(TAG, "==[execute start]== " + name); } // 模拟复杂处理任务 // Simulate processing tasks int a = 0; for (int i = 0; i < 100000000; i++) { a += 1; } if (priority != null) { Log.d(TAG, "execute end " + name + ", priority=" + priority); } else { Log.d(TAG, "execute end " + name); } } }; } private static Integer getPriority(Object o) { if (o instanceof IPriority) { return ((IPriority) o).priority(); } return null; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/UseCaseFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtaskdemo.core.BaseContainerFragment; import com.xuexiang.xtaskdemo.fragment.usecase.AppStartFragment; import com.xuexiang.xtaskdemo.fragment.usecase.ComplexBusinessFragment; import com.xuexiang.xtaskdemo.fragment.usecase.ConcurrentProcessFragment; /** * @author xuexiang * @since 2/23/22 12:21 AM */ @Page(name = "应用场景\n列举常用的使用案例") public class UseCaseFragment extends BaseContainerFragment { @Override protected Class[] getPagesClasses() { return new Class[]{ AppStartFragment.class, ComplexBusinessFragment.class, ConcurrentProcessFragment.class }; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/XTaskStepFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment; import android.util.Log; import androidx.annotation.NonNull; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.api.TaskChainEngine; import com.xuexiang.xtask.api.step.ConcurrentGroupTaskStep; import com.xuexiang.xtask.api.step.SerialGroupTaskStep; import com.xuexiang.xtask.api.step.XTaskStep; import com.xuexiang.xtask.core.ITaskChainEngine; import com.xuexiang.xtask.core.ThreadType; import com.xuexiang.xtask.core.param.ITaskParam; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtask.core.param.impl.TaskParam; import com.xuexiang.xtask.core.param.impl.TaskResult; import com.xuexiang.xtask.core.step.impl.TaskChainCallbackAdapter; import com.xuexiang.xtask.core.step.impl.TaskCommand; import com.xuexiang.xtask.thread.pool.cancel.ICanceller; import com.xuexiang.xtaskdemo.core.BaseSimpleListFragment; import com.xuexiang.xutil.common.ObjectUtils; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * 使用XTaskStep可以让你使用起来更方便 * * @author xuexiang * @since 1/20/22 12:24 AM */ @Page(name = "XTaskStep任务使用\n通过XTaskStep进行简化使用") public class XTaskStepFragment extends BaseSimpleListFragment { private static final String TAG = "TaskFragment"; private Set mPool = new HashSet<>(); @Override protected List initSimpleData(List lists) { lists.add("简单任务链使用"); lists.add("任务链参数传递"); lists.add("任务线程控制"); lists.add("简单的串行任务组使用"); lists.add("简单的并行任务组使用"); lists.add("任务执行失败"); lists.add("任务执行发生异常"); return lists; } @Override protected void onItemClick(int position) { switch (position) { case 0: doSimpleTaskChain(); break; case 1: doParamTaskChain(); break; case 2: doThreadTypeTaskChain(); break; case 3: doSerialGroupTaskChain(); break; case 4: doConcurrentGroupTaskChain(); break; case 5: doTaskFailed(); break; case 6: doTaskException(); break; default: break; } } /** * 简单任务链的使用 */ private void doSimpleTaskChain() { final TaskChainEngine engine = XTask.getTaskChain(); for (int i = 0; i < 5; i++) { engine.addTask(XTask.getTask(new SimpleTaskCommand(1000 * i))); } ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public boolean isCallBackOnMainThread() { return false; } @Override public void onTaskChainStart(@NonNull ITaskChainEngine engine) { Log.e(TAG, "task chain start"); } @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); engine.destroy(); } @Override public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain error"); } }).start(); addCanceller(canceller); } /** * 任务链参数传递,上一个任务的参数可传递至下一个任务 */ private void doParamTaskChain() { final TaskChainEngine engine = XTask.getTaskChain(); engine.setTaskParam(TaskParam.get("chainName", engine.getName())); TaskParam taskParam = TaskParam.get("param1", 100) .put("param2", true); XTaskStep taskStep = XTask.getTask(new TaskCommand() { @Override public void run() { ITaskParam param = getTaskParam(); Log.e(TAG, getName() + " start, param1:" + param.get("param1") + ", chainName:" + param.get("chainName")); param.put("param1", 200); param.put("param3", "this is param3!"); } }, taskParam); engine.addTask(taskStep) .addTask(XTask.getTask(new TaskCommand() { @Override public void run() { ITaskParam param = getTaskParam(); Log.e(TAG, getName() + " start, param1:" + param.get("param1") + ", param3:" + param.get("param3")); param.put("param2", false); } })); ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); Map data = result.getDataStore().getData(); for (Map.Entry entry : data.entrySet()) { Log.e(TAG, "key:" + entry.getKey() + ", value:" + entry.getValue()); } } }).start(); addCanceller(canceller); } /** * 任务线程控制 */ private void doThreadTypeTaskChain() { final TaskChainEngine engine = XTask.getTaskChain(); ICanceller canceller = engine.addTask(XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC)) .addTask(XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_EMERGENT)) .addTask(XTask.getTask(new SimpleTaskCommand(200), ThreadType.MAIN)) .addTask(XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_IO)) .addTask(XTask.getTask(new SimpleTaskCommand(1000), ThreadType.SYNC)) .addTask(XTask.getTask(new SimpleTaskCommand(1000), ThreadType.ASYNC_BACKGROUND)) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainStart(@NonNull ITaskChainEngine engine) { Log.e(TAG, "task chain start, thread:" + Thread.currentThread().getName()); } @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); } }).start(); addCanceller(canceller); } /** * 简单的串行任务组使用 */ private void doSerialGroupTaskChain() { final TaskChainEngine engine = XTask.getTaskChain(); SerialGroupTaskStep group1 = XTask.getSerialGroupTask("group1"); for (int i = 0; i < 5; i++) { group1.addTask(XTask.getTask(new SimpleTaskCommand(500))); } SerialGroupTaskStep group2 = XTask.getSerialGroupTask("group2"); for (int i = 0; i < 5; i++) { group2.addTask(XTask.getTask(new SimpleTaskCommand(1000))); } ICanceller canceller = engine.addTask(group1) .addTask(group2) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, path:" + result.getPath()); } }) .start(); addCanceller(canceller); } /** * 简单的并行任务组使用 */ private void doConcurrentGroupTaskChain() { final TaskChainEngine engine = XTask.getTaskChain(); ConcurrentGroupTaskStep group1 = XTask.getConcurrentGroupTask("group1"); for (int i = 0; i < 5; i++) { group1.addTask(XTask.getTask(new SimpleTaskCommand(100 * (i + 1)))); } ConcurrentGroupTaskStep group2 = XTask.getConcurrentGroupTask("group2"); for (int i = 0; i < 5; i++) { group2.addTask(XTask.getTask(new SimpleTaskCommand(200 * (i + 1)))); } ICanceller canceller = engine.addTask(group1) .addTask(group2) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, path:" + result.getPath()); } }) .start(); addCanceller(canceller); } /** * 任务执行失败,整个链路都停止工作 */ private void doTaskFailed() { final TaskChainEngine engine = XTask.getTaskChain(); for (int i = 0; i < 5; i++) { int finalI = i; engine.addTask(XTask.getTask(new TaskCommand() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (finalI == 2) { notifyTaskFailed(404, "任务执行失败!"); } else { notifyTaskSucceed(TaskResult.succeed()); } } }, false)); } ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); } @Override public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain error, " + result.getDetailMessage()); } }).start(); addCanceller(canceller); } private void doTaskException() { final TaskChainEngine engine = XTask.getTaskChain(); for (int i = 0; i < 5; i++) { int finalI = i; engine.addTask(XTask.getTask(new TaskCommand() { @Override public void run() throws Exception { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (finalI == 2) { throw new Exception("执行出现异常!"); } } })); } ICanceller canceller = engine.setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain completed, thread:" + Thread.currentThread().getName()); } @Override public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { Log.e(TAG, "task chain error, " + result.getDetailMessage()); } }).start(); addCanceller(canceller); } private void addCanceller(ICanceller canceller) { if (canceller != null) { mPool.add(canceller.getName()); } } private static class SimpleTaskCommand extends TaskCommand { private long mDuring; private SimpleTaskCommand(long during) { mDuring = during; } @Override public void run() { Log.e(TAG, getName() + " start...thread:" + Thread.currentThread().getName()); try { Thread.sleep(mDuring); } catch (InterruptedException e) { e.printStackTrace(); } Log.e(TAG, getName() + " end..."); } } @Override public void onDestroyView() { if (!ObjectUtils.isEmpty(mPool)) { XTask.cancelTaskChain(mPool); } super.onDestroyView(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/XTaskThreadFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment; import android.util.Log; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.thread.pool.cancel.ICancelable; import com.xuexiang.xtask.thread.utils.CancelUtils; import com.xuexiang.xtaskdemo.core.BaseSimpleListFragment; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * 使用XTask进行线程池操作 * * @author xuexiang * @since 3/20/22 2:22 AM */ @Page(name = "XTask线程池使用\nXTask拥有丰富的线程调度方法") public class XTaskThreadFragment extends BaseSimpleListFragment { private static final String TAG = "XTaskThreadFragment"; private List mCancelableList = new ArrayList<>(); @Override protected List initSimpleData(List lists) { lists.add("普通异步执行"); lists.add("紧急异步执行"); lists.add("后台异步执行"); lists.add("优先级异步执行"); lists.add("主线程延迟执行"); lists.add("子线程延迟执行"); lists.add("周期执行(固定间期)"); lists.add("周期执行(固定延期)"); return lists; } @Override protected void onItemClick(int position) { switch (position) { case 0: doNormalAsync(); break; case 1: doEmergentAsync(); break; case 2: doBackgroundAsync(); break; case 3: doPriorityAsync(); break; case 4: doDelayOnMainThread(); break; case 5: doDelay(); break; case 6: doPollingFixedRate(); break; case 7: doPollingFixedDelay(); break; default: break; } } private void doNormalAsync() { Log.e(TAG, "doNormalAsync start..."); ICancelable cancelable = XTask.submit(() -> { Log.e(TAG, "Async task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }); mCancelableList.add(cancelable); } private void doEmergentAsync() { Log.e(TAG, "doEmergentAsync start..."); ICancelable cancelable = XTask.emergentSubmit(() -> { Log.e(TAG, "Emergent task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }); mCancelableList.add(cancelable); } private void doBackgroundAsync() { Log.e(TAG, "doBackgroundAsync start..."); ICancelable cancelable = XTask.backgroundSubmit(() -> { Log.e(TAG, "Background task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }); mCancelableList.add(cancelable); } private void doPriorityAsync() { Log.e(TAG, "doPriorityAsync start..."); ICancelable cancelable = XTask.submit(() -> { Log.e(TAG, "Priority task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }, 10); mCancelableList.add(cancelable); } private void doDelayOnMainThread() { Log.e(TAG, "doDelayOnMainThread start..."); ICancelable cancelable = XTask.postToMainDelay(() -> { Log.e(TAG, "Delay task start, thread:" + Thread.currentThread().getName()); }, 2000); mCancelableList.add(cancelable); } private void doDelay() { Log.e(TAG, "doDelay start..."); ICancelable cancelable = XTask.schedule(() -> { Log.e(TAG, "Delay task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }, 2, TimeUnit.SECONDS); mCancelableList.add(cancelable); } private void doPollingFixedRate() { Log.e(TAG, "doPollingFixedRate start..."); ICancelable cancelable = XTask.scheduleAtFixedRate(() -> { Log.e(TAG, "FixedRate task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }, 0, 2, TimeUnit.SECONDS); mCancelableList.add(cancelable); } private void doPollingFixedDelay() { Log.e(TAG, "doPollingFixedDelay start..."); ICancelable cancelable = XTask.scheduleWithFixedDelay(() -> { Log.e(TAG, "FixedDelay task start, thread:" + Thread.currentThread().getName()); mockProcess(2000); }, 0, 2, TimeUnit.SECONDS); mCancelableList.add(cancelable); } /** * 模拟执行 * * @param time 模拟执行所需要的时间 */ public void mockProcess(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void onDestroyView() { CancelUtils.cancel(mCancelableList); super.onDestroyView(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/task/Job.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.task; import android.util.Log; import androidx.annotation.NonNull; import com.xuexiang.xtask.core.param.ITaskParam; import com.xuexiang.xtask.core.param.impl.TaskResult; import com.xuexiang.xtask.core.step.ITaskStepController; import com.xuexiang.xtaskdemo.fragment.CustomTaskFragment; /** * 模拟复杂的任务 * * @author xuexiang * @since 2/9/22 11:22 PM */ public class Job implements Runnable { public static final String KEY_JOB_TARGET = "key_job_target"; private ITaskStepController mController; public Job(@NonNull ITaskStepController controller) { mController = controller; } @Override public void run() { ITaskParam taskParam = mController.getTaskParam(); int target = taskParam.getInt(KEY_JOB_TARGET); Log.e(CustomTaskFragment.TAG, "Job is running..., target:" + target); // 模拟耗时 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } taskParam.put(KEY_JOB_TARGET, target + 10); mController.notifyTaskSucceed(TaskResult.succeed()); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/task/JobTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.task; import com.xuexiang.xtask.api.step.SimpleTaskStep; import java.util.concurrent.atomic.AtomicInteger; /** * 模拟复杂任务 * * @author xuexiang * @since 2/9/22 11:22 PM */ public class JobTask extends SimpleTaskStep { private static final AtomicInteger TASK_NUMBER = new AtomicInteger(1); /** * 构造方法 */ public JobTask() { super("CallBackTask-" + TASK_NUMBER.getAndIncrement()); } @Override public void doTask() throws Exception { new Job(this).run(); } @Override protected boolean isAutoNotify() { return false; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/task/StepATask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.task; import android.util.Log; import com.xuexiang.xtask.core.param.impl.TaskResult; import com.xuexiang.xtask.core.step.impl.AbstractTaskStep; import com.xuexiang.xtaskdemo.fragment.CustomTaskFragment; /** * 步骤A任务 * * @author xuexiang * @since 2/9/22 1:37 AM */ public class StepATask extends AbstractTaskStep { public static final String KEY_TOTAL = "total"; @Override public void doTask() throws Exception { Log.e(CustomTaskFragment.TAG, "doing StepA task!"); // 增加参数 getTaskParam().put(KEY_TOTAL, 10); // 通知任务链任务完成 notifyTaskSucceed(TaskResult.succeed()); } @Override public String getName() { return "StepATask"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/task/StepBTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.task; import android.util.Log; import com.xuexiang.xtask.core.param.impl.TaskResult; import com.xuexiang.xtask.core.step.impl.AbstractTaskStep; import com.xuexiang.xtaskdemo.fragment.CustomTaskFragment; /** * 步骤B任务 * * @author xuexiang * @since 2/9/22 1:42 AM */ public class StepBTask extends AbstractTaskStep { @Override public void doTask() throws Exception { Log.e(CustomTaskFragment.TAG, "doing StepB task!"); // 获取参数进行处理 int total = getTaskParam().getInt(StepATask.KEY_TOTAL) + 20; Log.e(CustomTaskFragment.TAG, "total:" + total); // 更新结果 getTaskParam().put(StepATask.KEY_TOTAL, total); notifyTaskSucceed(TaskResult.succeed()); } @Override public String getName() { return "StepBTask"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/AppStartFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase; import android.view.View; import androidx.annotation.NonNull; import com.xuexiang.xaop.annotation.SingleClick; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.api.step.ConcurrentGroupTaskStep; import com.xuexiang.xtask.core.ITaskChainEngine; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtask.core.step.impl.TaskChainCallbackAdapter; import com.xuexiang.xtask.core.step.impl.TaskCommand; import com.xuexiang.xtaskdemo.R; import com.xuexiang.xtaskdemo.core.BaseFragment; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.job.LongTimeJob; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.job.SingleJob; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.job.TopPriorityJob; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.task.AsyncInitTask; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.task.MainInitTask; import com.xuexiang.xui.widget.textview.LoggerTextView; import butterknife.BindView; import butterknife.OnClick; /** * 应用冷启动优化 *

* 所谓的应用冷启动优化,最主要的就是减少应用在初始化应用在主线程中所占用的时间。 * 针对一些耗时的操作,我们常用的优化策略是: * 1.同步主线程执行 -> 异步子线程执行 * 2.串行操作 -> 并行操作 * 3.预加载 -> 懒加载 * 4.业务流程优化(按业务的优先级调整执行顺序) * 5.数据结构优化(减少加载时间) * * @author xuexiang * @since 2/23/22 12:26 AM */ @Page(name = "应用冷启动优化") public class AppStartFragment extends BaseFragment { @BindView(R.id.logger) LoggerTextView logger; @Override protected int getLayoutId() { return R.layout.fragment_usecase_template; } @Override protected void initViews() { } @SingleClick @OnClick({R.id.btn_before_improve, R.id.btn_after_improve}) public void onViewClicked(View view) { clearLog(); log("开始执行任务..."); final long startTime = System.currentTimeMillis(); switch (view.getId()) { case R.id.btn_before_improve: doJobBeforeImprove(startTime); break; case R.id.btn_after_improve: doJobAfterImprove(startTime); break; default: break; } } /** * 优化前的写法, 这里仅是演示模拟,实际的可能更复杂 */ private void doJobBeforeImprove(long startTime) { new TopPriorityJob(logger).doJob(); for (int i = 0; i < 4; i++) { new SingleJob((i + 1), logger).doJob(); } new LongTimeJob(logger).doJob(); log("任务执行完毕,总共耗时:" + (System.currentTimeMillis() - startTime) + "ms"); } /** * 优化后的写法, 这里仅是演示模拟,实际的可能更复杂 */ private void doJobAfterImprove(final long startTime) { ConcurrentGroupTaskStep groupTaskStep = XTask.getConcurrentGroupTask(); for (int i = 0; i < 4; i++) { int finalI = i; groupTaskStep.addTask(XTask.getTask(new TaskCommand() { @Override public void run() throws Exception { new SingleJob((finalI + 1), logger).doJob(); } })); } XTask.getTaskChain() .addTask(new MainInitTask(logger)) .addTask(groupTaskStep) .addTask(new AsyncInitTask(logger)) .setTaskChainCallback(new TaskChainCallbackAdapter() { @Override public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) { log("任务完全执行完毕,总共耗时:" + (System.currentTimeMillis() - startTime) + "ms"); } }).start(); log("主线程任务执行完毕,总共耗时:" + (System.currentTimeMillis() - startTime) + "ms"); } public void log(String logContent) { if (logger != null) { logger.logSuccess(logContent); } } public void clearLog() { if (logger != null) { logger.clearLog(); } } @Override public void onDestroyView() { super.onDestroyView(); XTask.cancelAllTaskChain(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/ComplexBusinessFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase; import android.util.Pair; import android.view.View; import androidx.annotation.NonNull; import com.xuexiang.xaop.annotation.SingleClick; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.core.ITaskChainEngine; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtask.core.param.impl.TaskParam; import com.xuexiang.xtask.core.step.impl.TaskChainCallbackAdapter; import com.xuexiang.xtaskdemo.R; import com.xuexiang.xtaskdemo.core.BaseFragment; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductFactory; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductInfo; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.GetProductInfoProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.GivePriceProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.PublicProductProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.SearchFactoryProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.GetProductInfoTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.GivePriceTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.PublicProductTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.SearchFactoryTask; import com.xuexiang.xui.widget.textview.LoggerTextView; import com.xuexiang.xutil.system.AppExecutors; import butterknife.BindView; import butterknife.OnClick; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; /** * 复杂业务流程处理 * * 案例[高仿网红产品]的流程如下: * 1.获取产品信息 -> 2.查询可生产的工厂 -> 3.联系工厂生产产品 -> 4.送去市场部门评估售价 -> 5.产品上市 * * @author xuexiang * @since 3/18/22 11:28 PM */ @Page(name = "复杂业务流程处理") public class ComplexBusinessFragment extends BaseFragment { @BindView(R.id.logger) LoggerTextView logger; private String productId = "123456"; private Disposable disposable; @Override protected int getLayoutId() { return R.layout.fragment_usecase_compare; } @Override protected void initViews() { } @SingleClick @OnClick({R.id.btn_normal, R.id.btn_rxjava, R.id.btn_xtask}) public void onViewClicked(View view) { clearLog(); log("开始仿冒生产网红产品..."); final long startTime = System.currentTimeMillis(); switch (view.getId()) { case R.id.btn_normal: doBusinessNormal(startTime); break; case R.id.btn_rxjava: doBusinessRxJava(startTime); break; case R.id.btn_xtask: doBusinessXTask(startTime); break; default: break; } } /** * 普通的接口回调写法, 这里仅是演示模拟,实际的可能更复杂 * 流程如下: * 1.获取产品信息 -> 2.查询可生产的工厂 -> 3.联系工厂生产产品 -> 4.送去市场部门评估售价 -> 5.产品上市 */ private void doBusinessNormal(final long startTime) { AppExecutors.get().singleIO().execute(() -> { // 1.获取产品信息 new GetProductInfoProcessor(logger, productId).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter() { @Override public void onSuccess(final ProductInfo productInfo) { // 2.查询可生产的工厂 new SearchFactoryProcessor(logger, productInfo).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter() { @Override public void onSuccess(final ProductFactory factory) { // 3.联系工厂生产产品 log("开始生产产品..."); Product product = factory.produce(productInfo); // 4.送去市场部门评估售价 new GivePriceProcessor(logger, product).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter() { @Override public void onSuccess(Product product) { // 5.产品上市 PublicProductProcessor publicProductProcessor = new PublicProductProcessor(logger, product); publicProductProcessor.setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter() { @Override public void onSuccess(Product product) { log("总共耗时:" + (System.currentTimeMillis() - startTime) + "ms"); log("仿冒生产网红产品完成, " + product); } }).process(); } }).process(); } }).process(); } }).process(); }); } /** * RxJava写法, 这里仅是演示模拟,实际的可能更复杂 * 流程如下: * 1.获取产品信息 -> 2.查询可生产的工厂 -> 3.联系工厂生产产品 -> 4.送去市场部门评估售价 -> 5.产品上市 */ private void doBusinessRxJava(final long startTime) { 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写法, 这里仅是演示模拟,实际的可能更复杂 * 流程如下: * 1.获取产品信息 -> 2.查询可生产的工厂 -> 3.联系工厂生产产品 -> 4.送去市场部门评估售价 -> 5.产品上市 */ private void doBusinessXTask(final long startTime) { 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(); } public void log(String logContent) { if (logger != null) { logger.logSuccess(logContent); } } public void clearLog() { if (logger != null) { logger.clearLog(); } } @Override public void onDestroyView() { super.onDestroyView(); if (disposable != null && !disposable.isDisposed()) { disposable.dispose(); } XTask.cancelAllTaskChain(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/ConcurrentProcessFragment.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase; import android.view.View; import androidx.annotation.NonNull; import com.xuexiang.xaop.annotation.SingleClick; import com.xuexiang.xpage.annotation.Page; import com.xuexiang.xtask.XTask; import com.xuexiang.xtask.core.ITaskChainEngine; import com.xuexiang.xtask.core.ThreadType; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtask.core.param.impl.TaskParam; import com.xuexiang.xtask.core.step.impl.TaskChainCallbackAdapter; import com.xuexiang.xtaskdemo.R; import com.xuexiang.xtaskdemo.core.BaseFragment; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.BriefInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.FactoryInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.PriceInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.PromotionInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.RichInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetBriefInfoProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetFactoryInfoProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetPriceInfoProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetPromotionInfoProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetRichInfoProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task.GetBriefInfoTask; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task.GetFactoryInfoTask; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task.GetPriceInfoTask; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task.GetPromotionInfoTask; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task.GetRichInfoTask; import com.xuexiang.xui.widget.textview.LoggerTextView; import com.xuexiang.xutil.system.AppExecutors; import java.util.concurrent.CountDownLatch; import butterknife.BindView; import butterknife.OnClick; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Observable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; /** * 复杂并发任务处理 *

* 案例[展示商品详细信息]的流程如下: *

* 1.根据商品的唯一号ID获取商品简要信息 * 2.获取商品的详细信息: * 2.1 获取商品的生产信息 * 2.2 获取商品的价格信息 * 2.3 获取商品的促销信息 * 2.4 获取商品的富文本信息 * 3.进行商品信息的展示 * * @author xuexiang * @since 3/21/22 10:57 PM */ @Page(name = "复杂并发任务处理") public class ConcurrentProcessFragment extends BaseFragment { @BindView(R.id.logger) LoggerTextView logger; private String productId = "123456"; private Disposable disposable; @Override protected int getLayoutId() { return R.layout.fragment_usecase_compare; } @Override protected void initViews() { } @SingleClick @OnClick({R.id.btn_normal, R.id.btn_rxjava, R.id.btn_xtask}) public void onViewClicked(View view) { clearLog(); log("开始查询商品信息..."); final long startTime = System.currentTimeMillis(); switch (view.getId()) { case R.id.btn_normal: queryInfoNormal(startTime, productId); break; case R.id.btn_rxjava: queryInfoRxJava(startTime, productId); break; case R.id.btn_xtask: queryInfoXTask(startTime, productId); break; default: break; } } /** * 普通的接口回调写法, 这里仅是演示模拟,实际的可能更复杂 */ private void queryInfoNormal(final long startTime, String productId) { AppExecutors.get().singleIO().execute(() -> { new GetBriefInfoProcessor(logger, productId).setProcessorCallback(new AbstractProcessor.ProcessorCallbackAdapter() { @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() { @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() { @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() { @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() { @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写法, 这里仅是演示模拟,实际的可能更复杂 */ private void queryInfoRxJava(final long startTime, String productId) { 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写法, 这里仅是演示模拟,实际的可能更复杂 */ private void queryInfoXTask(final long startTime, String productId) { 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(); } public void log(String logContent) { if (logger != null) { logger.logSuccess(logContent); } } public void clearLog() { if (logger != null) { logger.clearLog(); } } @Override public void onDestroyView() { super.onDestroyView(); if (disposable != null && !disposable.isDisposed()) { disposable.dispose(); } XTask.cancelAllTaskChain(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/appstart/job/AbstractMockJob.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.appstart.job; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 模拟任务 * * @author xuexiang * @since 2/23/22 12:47 AM */ public abstract class AbstractMockJob { private LoggerTextView mLogger; public AbstractMockJob(LoggerTextView logger) { mLogger = logger; } /** * 模拟执行任务 */ public abstract void doJob(); public void log(String logContent) { if (mLogger != null) { mLogger.logNormal(logContent); } } /** * 模拟执行 * * @param time 模拟执行所需要的时间 */ public void mockProcess(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/appstart/job/LongTimeJob.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.appstart.job; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 耗时任务,比如第三方依赖库的初始化、大数据的预加载、磁盘读写操作等 * * @author xuexiang * @since 2/23/22 1:02 AM */ public class LongTimeJob extends AbstractMockJob { public LongTimeJob(LoggerTextView logger) { super(logger); } @Override public void doJob() { log("[耗时任务]开始执行..."); mockProcess(1000); log("[耗时任务]执行完毕!"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/appstart/job/SingleJob.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.appstart.job; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 单独的任务,没有执行上的先后顺序. 例如:非核心数据的加载。 * * @author xuexiang * @since 2/23/22 1:16 AM */ public class SingleJob extends AbstractMockJob { private int mIndex; public SingleJob(int index, LoggerTextView logger) { super(logger); mIndex = index; } @Override public void doJob() { log("[单独的任务" + mIndex + "]开始执行..."); mockProcess(200); log("[单独的任务" + mIndex + "]执行完毕!"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/appstart/job/TopPriorityJob.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.appstart.job; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 最高优先级的事务,比如核心框架的初始化,关键数据的初始化等 * * @author xuexiang * @since 2/23/22 12:51 AM */ public class TopPriorityJob extends AbstractMockJob { public TopPriorityJob(LoggerTextView logger) { super(logger); } @Override public void doJob() { log("[最高优先级的任务]开始执行..."); mockProcess(50); log("[最高优先级的任务]执行完毕!"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/appstart/task/AsyncInitTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.appstart.task; import androidx.annotation.NonNull; import com.xuexiang.xtask.api.step.SimpleTaskStep; import com.xuexiang.xtask.core.ThreadType; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.job.LongTimeJob; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 异步初始化任务 * 放一些优先级不是很高的、耗时的初始化任务 * * @author xuexiang * @since 2/23/22 1:42 AM */ public class AsyncInitTask extends SimpleTaskStep { private LoggerTextView mLogger; public AsyncInitTask(LoggerTextView logger) { mLogger = logger; } @Override public void doTask() throws Exception { // 执行耗时任务 new LongTimeJob(mLogger).doJob(); } @Override public String getName() { return "AsyncInitTask"; } @NonNull @Override public ThreadType getThreadType() { // 任务的优先级不高,使用异步子线程执行 return ThreadType.ASYNC; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/appstart/task/MainInitTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.appstart.task; import androidx.annotation.NonNull; import com.xuexiang.xtask.api.step.SimpleTaskStep; import com.xuexiang.xtask.core.ThreadType; import com.xuexiang.xtaskdemo.fragment.usecase.appstart.job.TopPriorityJob; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 主要初始化任务,放在第一位执行, 执行最高优先级的事务 * * @author xuexiang * @since 2/23/22 1:34 AM */ public class MainInitTask extends SimpleTaskStep { private LoggerTextView mLogger; public MainInitTask(LoggerTextView logger) { mLogger = logger; } @Override public void doTask() throws Exception { // 执行最高优先级的事务 new TopPriorityJob(mLogger).doJob(); } @Override public String getName() { return "MainInitTask"; } @NonNull @Override public ThreadType getThreadType() { // 任务优先级较高,执行有前后依赖,因此将任务放在第一位使用同步主线程执行 return ThreadType.SYNC; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/entity/Product.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.entity; /** * 产品 * * @author xuexiang * @since 2/25/22 1:17 AM */ public class Product { private ProductInfo info; private String address; private String price; private String publicTime; public Product(ProductInfo info, String address) { this.info = info; this.address = address; } public ProductInfo getInfo() { return info; } public Product setInfo(ProductInfo info) { this.info = info; return this; } public String getAddress() { return address; } public Product setAddress(String address) { this.address = address; return this; } public String getPrice() { return price; } public Product setPrice(String price) { this.price = price; return this; } public String getPublicTime() { return publicTime; } public Product setPublicTime(String publicTime) { this.publicTime = publicTime; return this; } @Override public String toString() { return "产品信息:" + info + ", 产地:" + address + ", 价格:" + price + ", 上市日前:" + publicTime; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/entity/ProductFactory.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.entity; /** * 产品工厂 * * @author xuexiang * @since 2/25/22 1:15 AM */ public class ProductFactory { private String id; private String address; public ProductFactory(String id, String address) { this.id = id; this.address = address; } public Product produce(ProductInfo info) { return new Product(info, address); } public String getId() { return id; } public ProductFactory setId(String id) { this.id = id; return this; } public String getAddress() { return address; } public ProductFactory setAddress(String address) { this.address = address; return this; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/entity/ProductInfo.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.entity; /** * 产品信息 * * @author xuexiang * @since 2/25/22 12:40 AM */ public class ProductInfo { /** * 编号 */ private String id; /** * 品牌 */ private String brand; /** * 质量 */ private String quality; public ProductInfo(String id) { this.id = id; } public ProductInfo(String id, String brand, String quality) { this.id = id; this.brand = brand; this.quality = quality; } public String getId() { return id; } public ProductInfo setId(String id) { this.id = id; return this; } public String getBrand() { return brand; } public ProductInfo setBrand(String brand) { this.brand = brand; return this; } public String getQuality() { return quality; } public ProductInfo setQuality(String quality) { this.quality = quality; return this; } @Override public String toString() { return "id:" + id + ", 品牌:" + brand + ", 品质:" + quality; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/processor/AbstractProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.processor; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 抽象处理器 * * @author xuexiang * @since 2/25/22 12:28 AM */ public abstract class AbstractProcessor { private LoggerTextView mLogger; private IProcessorCallback mCallback; public AbstractProcessor(LoggerTextView logger) { mLogger = logger; } public AbstractProcessor setProcessorCallback(IProcessorCallback callback) { mCallback = callback; return this; } /** * 处理任务 * * @return 返回的结果 */ public abstract T process(); public void log(String logContent) { if (mLogger != null) { mLogger.logNormal(logContent); } } public void onProcessSuccess(T t) { if (mCallback != null) { mCallback.onSuccess(t); } } public void onProcessFailed(String error) { if (mCallback != null) { mCallback.onFailed(error); } } /** * 模拟执行 * * @param time 模拟执行所需要的时间 */ public void mockProcess(long time) { try { Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } public abstract static class ProcessorCallbackAdapter implements IProcessorCallback { @Override public void onFailed(String error) { } } public interface IProcessorCallback { /** * 处理成功 * * @param result 结果 */ void onSuccess(T result); /** * 处理失败 * * @param error 错误信息 */ void onFailed(String error); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/processor/GetProductInfoProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 1.获取产品信息 * * @author xuexiang * @since 2/25/22 12:34 AM */ public class GetProductInfoProcessor extends AbstractProcessor { private String id; public GetProductInfoProcessor(LoggerTextView logger, String id) { super(logger); this.id = id; } @Override public ProductInfo process() { log("[获取产品信息]开始执行..."); ProductInfo info = getProductInfoById(id); log("[获取产品信息]执行完毕!"); onProcessSuccess(info); return info; } private ProductInfo getProductInfoById(String id) { // 模拟耗费的时间 mockProcess(500); return new ProductInfo(id, "品牌A", "高品质"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/processor/GivePriceProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.Product; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 3.评估产品,给出价格 * @author xuexiang * @since 2/25/22 1:36 AM */ public class GivePriceProcessor extends AbstractProcessor { private Product product; public GivePriceProcessor(LoggerTextView logger, Product product) { super(logger); this.product = product; } @Override public Product process() { log("[评估产品价格]开始执行..."); String price = givePrice(product); product.setPrice(price); log("[评估产品价格]执行完毕!"); onProcessSuccess(product); return product; } private String givePrice(Product product) { // 模拟耗费的时间 mockProcess(100); return "45¥"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/processor/PublicProductProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductFactory; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 4.产品发布 * * @author xuexiang * @since 2/25/22 1:50 AM */ public class PublicProductProcessor extends AbstractProcessor { private Product product; public PublicProductProcessor(LoggerTextView logger, Product product) { super(logger); this.product = product; } public PublicProductProcessor(LoggerTextView logger) { super(logger); } @Override public Product process() { log("[产品发布]开始执行..."); String time = getPublicPlan(product); product.setPublicTime(time); log("[产品发布]执行完毕!"); onProcessSuccess(product); return product; } private String getPublicPlan(Product product) { // 模拟耗费的时间 mockProcess(400); return "2022年2月22日22时22分22秒"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/processor/SearchFactoryProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductFactory; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 2.查找相关的工厂 * * @author xuexiang * @since 2/25/22 1:13 AM */ public class SearchFactoryProcessor extends AbstractProcessor { private ProductInfo info; public SearchFactoryProcessor(LoggerTextView logger, ProductInfo info) { super(logger); this.info = info; } @Override public ProductFactory process() { log("[查找相关工厂]开始执行..."); ProductFactory factory = searchFactoryByBrand(info.getBrand()); log("[查找相关工厂]执行完毕!"); onProcessSuccess(factory); return factory; } private ProductFactory searchFactoryByBrand(String brand) { // 模拟耗费的时间 mockProcess(300); return new ProductFactory(brand, "南京市江宁区秣陵街道xxx街区"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/task/AbstractTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.task; import com.xuexiang.xtask.api.step.SimpleTaskStep; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 基础抽象任务 * * @author xuexiang * @since 2/25/22 2:18 AM */ public abstract class AbstractTask extends SimpleTaskStep { protected LoggerTextView mLogger; public AbstractTask(LoggerTextView logger) { mLogger = logger; } public void log(String logContent) { if (mLogger != null) { mLogger.logSuccess(logContent); } } @Override protected boolean isAutoNotify() { // 这里进行手动控制 return false; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/task/GetProductInfoTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductInfo; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.GetProductInfoProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 1.获取产品信息 * * @author xuexiang * @since 2/25/22 2:07 AM */ public class GetProductInfoTask extends AbstractTask { public GetProductInfoTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { String productId = getTaskParam().getString(ProductTaskConstants.KEY_PRODUCT_ID); new GetProductInfoProcessor(mLogger, productId) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(ProductInfo info) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT_INFO, info); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "1.获取产品信息"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/task/GivePriceTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.GivePriceProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 3.评估产品,给出价格 * * @author xuexiang * @since 2/25/22 2:27 AM */ public class GivePriceTask extends AbstractTask { public GivePriceTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { Product product = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class); new GivePriceProcessor(mLogger, product).setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(Product result) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, result); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }).process(); } @Override public String getName() { return "3.评估产品,给出价格"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/task/ProductTaskConstants.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.task; /** * @author xuexiang * @since 2/25/22 2:14 AM */ public final class ProductTaskConstants { public static final String KEY_PRODUCT_ID = "key_product_id"; public static final String KEY_PRODUCT_INFO = "key_product_info"; public static final String KEY_PRODUCT = "key_product"; } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/task/PublicProductTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.PublicProductProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 4.产品发布 * * @author xuexiang * @since 2/25/22 2:31 AM */ public class PublicProductTask extends AbstractTask { public PublicProductTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { Product product = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class); new PublicProductProcessor(mLogger, product).setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(Product result) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, result); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }).process(); } @Override public String getName() { return "4.产品发布"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/business/task/SearchFactoryTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.business.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductFactory; import com.xuexiang.xtaskdemo.fragment.usecase.business.entity.ProductInfo; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.SearchFactoryProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 2.查找相关的工厂并生产 * * @author xuexiang * @since 2/25/22 2:17 AM */ public class SearchFactoryTask extends AbstractTask { public SearchFactoryTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { ProductInfo productInfo = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT_INFO, ProductInfo.class); new SearchFactoryProcessor(mLogger, productInfo) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(ProductFactory factory) { log("开始生产产品..."); Product product = factory.produce(productInfo); getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, product); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "2.查找相关的工厂并生产"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/entity/BriefInfo.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity; import androidx.annotation.NonNull; /** * 产品简要信息 * * @author xuexiang * @since 3/22/22 1:08 AM */ public class BriefInfo { private String id; protected String name; private String factoryId; private String priceId; private String promotionId; private String richId; public BriefInfo(String id) { this.id = id; } public BriefInfo(@NonNull BriefInfo briefInfo) { this(briefInfo.id, briefInfo.name, briefInfo.factoryId, briefInfo.priceId, briefInfo.promotionId, briefInfo.richId); } public BriefInfo(String id, String name, String factoryId, String priceId, String promotionId, String richId) { this.id = id; this.name = name; this.factoryId = factoryId; this.priceId = priceId; this.promotionId = promotionId; this.richId = richId; } public String getId() { return id; } public BriefInfo setId(String id) { this.id = id; return this; } public String getName() { return name; } public BriefInfo setName(String name) { this.name = name; return this; } public String getFactoryId() { return factoryId; } public BriefInfo setFactoryId(String factoryId) { this.factoryId = factoryId; return this; } public String getPriceId() { return priceId; } public BriefInfo setPriceId(String priceId) { this.priceId = priceId; return this; } public String getPromotionId() { return promotionId; } public BriefInfo setPromotionId(String promotionId) { this.promotionId = promotionId; return this; } public String getRichId() { return richId; } public BriefInfo setRichId(String richId) { this.richId = richId; return this; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/entity/FactoryInfo.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity; import androidx.annotation.NonNull; /** * 工厂生产信息 * * @author xuexiang * @since 3/22/22 1:11 AM */ public class FactoryInfo { private String id; /** * 生产地址 */ private String address; /** * 生产日期 */ private String productDate; /** * 过期日期 */ private String expirationDate; public FactoryInfo(String id) { this.id = id; } public String getId() { return id; } public FactoryInfo setId(String id) { this.id = id; return this; } public String getAddress() { return address; } public FactoryInfo setAddress(String address) { this.address = address; return this; } public String getProductDate() { return productDate; } public FactoryInfo setProductDate(String productDate) { this.productDate = productDate; return this; } public String getExpirationDate() { return expirationDate; } public FactoryInfo setExpirationDate(String expirationDate) { this.expirationDate = expirationDate; return this; } @NonNull @Override public String toString() { return "生产编号:" + id + ", 生产地址:" + address + ", 生产日期:" + productDate + ", 过期日期:" + expirationDate; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/entity/PriceInfo.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity; import androidx.annotation.NonNull; /** * 价格信息 * * @author xuexiang * @since 3/22/22 1:10 AM */ public class PriceInfo { private String id; /** * 出厂价 */ private float factoryPrice; /** * 批发价 */ private float wholesalePrice; /** * 零售价 */ private float retailPrice; public PriceInfo(String id) { this.id = id; } public String getId() { return id; } public PriceInfo setId(String id) { this.id = id; return this; } public float getFactoryPrice() { return factoryPrice; } public PriceInfo setFactoryPrice(float factoryPrice) { this.factoryPrice = factoryPrice; return this; } public float getWholesalePrice() { return wholesalePrice; } public PriceInfo setWholesalePrice(float wholesalePrice) { this.wholesalePrice = wholesalePrice; return this; } public float getRetailPrice() { return retailPrice; } public PriceInfo setRetailPrice(float retailPrice) { this.retailPrice = retailPrice; return this; } @NonNull @Override public String toString() { return "价格编号:" + id + ", 出厂价:" + factoryPrice + ", 批发价:" + wholesalePrice + ", 零售价:" + retailPrice; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/entity/Product.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity; import androidx.annotation.NonNull; /** * 产品 * * @author xuexiang * @since 3/22/22 1:07 AM */ public class Product extends BriefInfo { /** * 生产信息 */ private FactoryInfo factory; /** * 价格信息 */ private PriceInfo price; /** * 促销信息 */ private PromotionInfo promotion; /** * 富文本信息 */ private RichInfo rich; public Product(@NonNull BriefInfo briefInfo) { super(briefInfo); } public FactoryInfo getFactory() { return factory; } public Product setFactory(FactoryInfo factory) { this.factory = factory; return this; } public PriceInfo getPrice() { return price; } public Product setPrice(PriceInfo price) { this.price = price; return this; } public PromotionInfo getPromotion() { return promotion; } public Product setPromotion(PromotionInfo promotion) { this.promotion = promotion; return this; } public RichInfo getRich() { return rich; } public Product setRich(RichInfo rich) { this.rich = rich; return this; } @NonNull @Override public String toString() { return "产品信息: " + name + "\n产地信息: " + factory + "\n价格信息: " + price + "\n促销信息: " + promotion + "\n富文本信息: " + rich; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/entity/PromotionInfo.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity; import androidx.annotation.NonNull; /** * 产品促销信息 * * @author xuexiang * @since 3/22/22 1:12 AM */ public class PromotionInfo { private String id; /** * 促销类型 */ private int type; /** * 促销内容 */ private String content; /** * 生效日期 */ private String effectiveDate; /** * 失效日期 */ private String expirationDate; public PromotionInfo(String id) { this.id = id; } public String getId() { return id; } public PromotionInfo setId(String id) { this.id = id; return this; } public int getType() { return type; } public PromotionInfo setType(int type) { this.type = type; return this; } public String getContent() { return content; } public PromotionInfo setContent(String content) { this.content = content; return this; } public String getEffectiveDate() { return effectiveDate; } public PromotionInfo setEffectiveDate(String effectiveDate) { this.effectiveDate = effectiveDate; return this; } public String getExpirationDate() { return expirationDate; } public PromotionInfo setExpirationDate(String expirationDate) { this.expirationDate = expirationDate; return this; } @NonNull @Override public String toString() { return "促销编号:" + id + ", 促销类型:" + type + ", 促销内容:" + content + ", 生效日期:" + effectiveDate + ", 失效日期:" + expirationDate; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/entity/RichInfo.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity; import androidx.annotation.NonNull; /** * 富文本信息 * * @author xuexiang * @since 3/22/22 1:13 AM */ public class RichInfo { private String id; /** * 描述信息 */ private String description; /** * 图片链接 */ private String imgUrl; /** * 视频链接 */ private String videoUrl; public RichInfo(String id) { this.id = id; } public String getId() { return id; } public RichInfo setId(String id) { this.id = id; return this; } public String getDescription() { return description; } public RichInfo setDescription(String description) { this.description = description; return this; } public String getImgUrl() { return imgUrl; } public RichInfo setImgUrl(String imgUrl) { this.imgUrl = imgUrl; return this; } public String getVideoUrl() { return videoUrl; } public RichInfo setVideoUrl(String videoUrl) { this.videoUrl = videoUrl; return this; } @NonNull @Override public String toString() { return "富文本编号:" + id + ", 描述信息:" + description + ", 图片链接:" + imgUrl + ", 视频链接:" + videoUrl; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/processor/GetBriefInfoProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.BriefInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 1 获取商品简要信息 * * @author xuexiang * @since 3/22/22 1:18 AM */ public class GetBriefInfoProcessor extends AbstractProcessor { private String productId; public GetBriefInfoProcessor(LoggerTextView logger, String productId) { super(logger); this.productId = productId; } @Override public BriefInfo process() { log("[简要信息查询]开始执行..."); BriefInfo info = getBriefInfoById(productId); log("[简要信息查询]执行完毕!"); onProcessSuccess(info); return info; } private BriefInfo getBriefInfoById(String id) { // 模拟耗费的时间 mockProcess(500); return new BriefInfo(id) .setName("统一老坛酸菜牛肉面") .setFactoryId("fa234632-1234-4567") .setPriceId("pr432359-3745-9426") .setPromotionId("pt235123-9654-2942") .setRichId("ri735294-2346-1048"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/processor/GetFactoryInfoProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.FactoryInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 2.1 获取商品的生产信息 * * @author xuexiang * @since 3/22/22 1:22 AM */ public class GetFactoryInfoProcessor extends AbstractProcessor { private String factoryId; public GetFactoryInfoProcessor(LoggerTextView logger, String factoryId) { super(logger); this.factoryId = factoryId; } @Override public FactoryInfo process() { log("[生产信息查询]开始执行..."); FactoryInfo info = getFactoryInfoById(factoryId); log("[生产信息查询]执行完毕!"); onProcessSuccess(info); return info; } private FactoryInfo getFactoryInfoById(String id) { // 模拟耗费的时间 mockProcess(200); return new FactoryInfo(id) .setAddress("长沙市开福区金霞经济开发区中青路1301号长沙统一企业有限公司") .setProductDate("2022年3月15日") .setExpirationDate("2030年3月15日"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/processor/GetPriceInfoProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.PriceInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 2.2 获取商品的价格信息 * * @author xuexiang * @since 3/22/22 1:20 AM */ public class GetPriceInfoProcessor extends AbstractProcessor { private String priceId; public GetPriceInfoProcessor(LoggerTextView logger, String priceId) { super(logger); this.priceId = priceId; } @Override public PriceInfo process() { log("[价格信息查询]开始执行..."); PriceInfo info = getPriceInfoById(priceId); log("[价格信息查询]执行完毕!"); onProcessSuccess(info); return info; } private PriceInfo getPriceInfoById(String id) { // 模拟耗费的时间 mockProcess(300); return new PriceInfo(id) .setFactoryPrice(1.5F) .setWholesalePrice(2.5F) .setRetailPrice(4.5F); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/processor/GetPromotionInfoProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.PromotionInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 2.3 获取商品的促销信息 * * @author xuexiang * @since 3/22/22 1:23 AM */ public class GetPromotionInfoProcessor extends AbstractProcessor { private String promotionId; public GetPromotionInfoProcessor(LoggerTextView logger, String promotionId) { super(logger); this.promotionId = promotionId; } @Override public PromotionInfo process() { log("[促销信息查询]开始执行..."); PromotionInfo info = getPromotionInfoById(promotionId); log("[促销信息查询]执行完毕!"); onProcessSuccess(info); return info; } private PromotionInfo getPromotionInfoById(String id) { // 模拟耗费的时间 mockProcess(150); return new PromotionInfo(id) .setType(5) .setContent("买一送一") .setEffectiveDate("2022年3月15日") .setExpirationDate("2022年4月15日"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/processor/GetRichInfoProcessor.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.RichInfo; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 2.4 获取商品的富文本信息 * * @author xuexiang * @since 3/22/22 1:21 AM */ public class GetRichInfoProcessor extends AbstractProcessor { private String richId; public GetRichInfoProcessor(LoggerTextView logger, String richId) { super(logger); this.richId = richId; } @Override public RichInfo process() { log("[富文本信息查询]开始执行..."); RichInfo info = getRichInfoById(richId); log("[富文本信息查询]执行完毕!"); onProcessSuccess(info); return info; } private RichInfo getRichInfoById(String id) { // 模拟耗费的时间 mockProcess(380); return new RichInfo(id) .setDescription("精选湖南插旗菜业古法土坑腌制,取上乘泡脚酸菜,让你尽享人间美味!") .setImgUrl("http://inews.gtimg.com/newsapp_bt/0/14650183855/641") .setVideoUrl("https://haokan.baidu.com/v?pd=wisenatural&vid=8687875510146074163"); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/task/GetBriefInfoTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.AbstractTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.BriefInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetBriefInfoProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; /** * 获取商品简要信息 * * @author xuexiang * @since 3/22/22 11:52 PM */ public class GetBriefInfoTask extends AbstractTask { public GetBriefInfoTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { String productId = getTaskParam().getString(ProductTaskConstants.KEY_PRODUCT_ID); new GetBriefInfoProcessor(mLogger, productId) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(BriefInfo briefInfo) { Product product = new Product(briefInfo); getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, product); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "1.获取商品简要信息"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/task/GetFactoryInfoTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.AbstractTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.FactoryInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetFactoryInfoProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; import com.xuexiang.xutil.common.StringUtils; /** * 2.1 获取商品的生产信息 * * @author xuexiang * @since 3/23/22 12:18 AM */ public class GetFactoryInfoTask extends AbstractTask { public GetFactoryInfoTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { final Product product = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class); if (product == null || StringUtils.isEmpty(product.getFactoryId())) { notifyTaskFailed(ITaskResult.ERROR, "product is null or factoryId is empty!"); return; } new GetFactoryInfoProcessor(mLogger, product.getFactoryId()) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(FactoryInfo factoryInfo) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, product.setFactory(factoryInfo)); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "2.1 获取商品的生产信息"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/task/GetPriceInfoTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.AbstractTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.PriceInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetPriceInfoProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; import com.xuexiang.xutil.common.StringUtils; /** * 2.2 获取商品的价格信息 * * @author xuexiang * @since 3/23/22 12:18 AM */ public class GetPriceInfoTask extends AbstractTask { public GetPriceInfoTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { final Product product = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class); if (product == null || StringUtils.isEmpty(product.getPriceId())) { notifyTaskFailed(ITaskResult.ERROR, "product is null or priceId is empty!"); return; } new GetPriceInfoProcessor(mLogger, product.getPriceId()) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(PriceInfo priceInfo) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, product.setPrice(priceInfo)); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "2.2 获取商品的价格信息"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/task/GetPromotionInfoTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.AbstractTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.PromotionInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetPromotionInfoProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; import com.xuexiang.xutil.common.StringUtils; /** * 2.3 获取商品的促销信息 * * @author xuexiang * @since 3/23/22 12:18 AM */ public class GetPromotionInfoTask extends AbstractTask { public GetPromotionInfoTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { final Product product = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class); if (product == null || StringUtils.isEmpty(product.getPromotionId())) { notifyTaskFailed(ITaskResult.ERROR, "product is null or promotionId is empty!"); return; } new GetPromotionInfoProcessor(mLogger, product.getPromotionId()) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(PromotionInfo promotionInfo) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, product.setPromotion(promotionInfo)); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "2.3 获取商品的促销信息"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/fragment/usecase/concurrent/task/GetRichInfoTask.java ================================================ /* * Copyright (C) 2022 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.fragment.usecase.concurrent.task; import com.xuexiang.xtask.core.param.ITaskResult; import com.xuexiang.xtaskdemo.fragment.usecase.business.processor.AbstractProcessor; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.AbstractTask; import com.xuexiang.xtaskdemo.fragment.usecase.business.task.ProductTaskConstants; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.Product; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.entity.RichInfo; import com.xuexiang.xtaskdemo.fragment.usecase.concurrent.processor.GetRichInfoProcessor; import com.xuexiang.xui.widget.textview.LoggerTextView; import com.xuexiang.xutil.common.StringUtils; /** * 2.4 获取商品的富文本信息 * * @author xuexiang * @since 3/23/22 12:18 AM */ public class GetRichInfoTask extends AbstractTask { public GetRichInfoTask(LoggerTextView logger) { super(logger); } @Override public void doTask() throws Exception { final Product product = getTaskParam().getObject(ProductTaskConstants.KEY_PRODUCT, Product.class); if (product == null || StringUtils.isEmpty(product.getRichId())) { notifyTaskFailed(ITaskResult.ERROR, "product is null or richId is empty!"); return; } new GetRichInfoProcessor(mLogger, product.getRichId()) .setProcessorCallback(new AbstractProcessor.IProcessorCallback() { @Override public void onSuccess(RichInfo richInfo) { getTaskParam().put(ProductTaskConstants.KEY_PRODUCT, product.setRich(richInfo)); notifyTaskSucceed(); } @Override public void onFailed(String error) { notifyTaskFailed(ITaskResult.ERROR, error); } }) .process(); } @Override public String getName() { return "2.4 获取商品的富文本信息"; } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/utils/XToastUtils.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.utils; import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.StringRes; import com.xuexiang.xui.XUI; import com.xuexiang.xui.widget.toast.XToast; /** * xtoast 工具类 * * @author xuexiang * @since 2019-06-30 19:04 */ public final class XToastUtils { private XToastUtils() { throw new UnsupportedOperationException("u can't instantiate me..."); } static { XToast.Config.get() .setAlpha(200) .allowQueue(false); } //======普通土司=======// @MainThread public static void toast(@NonNull CharSequence message) { XToast.normal(XUI.getContext(), message).show(); } @MainThread public static void toast(@StringRes int message) { XToast.normal(XUI.getContext(), message).show(); } @MainThread public static void toast(@NonNull CharSequence message, int duration) { XToast.normal(XUI.getContext(), message, duration).show(); } @MainThread public static void toast(@StringRes int message, int duration) { XToast.normal(XUI.getContext(), message, duration).show(); } //======错误【红色】=======// @MainThread public static void error(@NonNull CharSequence message) { XToast.error(XUI.getContext(), message).show(); } @MainThread public static void error(@StringRes int message) { XToast.error(XUI.getContext(), message).show(); } @MainThread public static void error(@NonNull CharSequence message, int duration) { XToast.error(XUI.getContext(), message, duration).show(); } @MainThread public static void error(@StringRes int message, int duration) { XToast.error(XUI.getContext(), message, duration).show(); } //======成功【绿色】=======// @MainThread public static void success(@NonNull CharSequence message) { XToast.success(XUI.getContext(), message).show(); } @MainThread public static void success(@StringRes int message) { XToast.success(XUI.getContext(), message).show(); } @MainThread public static void success(@NonNull CharSequence message, int duration) { XToast.success(XUI.getContext(), message, duration).show(); } @MainThread public static void success(@StringRes int message, int duration) { XToast.success(XUI.getContext(), message, duration).show(); } //======信息【蓝色】=======// @MainThread public static void info(@NonNull CharSequence message) { XToast.info(XUI.getContext(), message).show(); } @MainThread public static void info(@StringRes int message) { XToast.info(XUI.getContext(), message).show(); } @MainThread public static void info(@NonNull CharSequence message, int duration) { XToast.info(XUI.getContext(), message, duration).show(); } @MainThread public static void info(@StringRes int message, int duration) { XToast.info(XUI.getContext(), message, duration).show(); } //=======警告【黄色】======// @MainThread public static void warning(@NonNull CharSequence message) { XToast.warning(XUI.getContext(), message).show(); } @MainThread public static void warning(@StringRes int message) { XToast.warning(XUI.getContext(), message).show(); } @MainThread public static void warning(@NonNull CharSequence message, int duration) { XToast.warning(XUI.getContext(), message, duration).show(); } @MainThread public static void warning(@StringRes int message, int duration) { XToast.warning(XUI.getContext(), message, duration).show(); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/utils/sdkinit/UMengInit.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.utils.sdkinit; import android.app.Application; import android.content.Context; import androidx.annotation.NonNull; import com.meituan.android.walle.WalleChannelReader; import com.umeng.analytics.MobclickAgent; import com.umeng.commonsdk.UMConfigure; import com.xuexiang.xtaskdemo.BuildConfig; import com.xuexiang.xtaskdemo.MyApp; /** * UMeng 统计 SDK初始化 * * @author xuexiang * @since 2019-06-18 15:49 */ public final class UMengInit { private UMengInit() { throw new UnsupportedOperationException("u can't instantiate me..."); } private static String DEFAULT_CHANNEL_ID = "github"; /** * 初始化SDK,合规指南【先进行预初始化,如果用户隐私同意后可以初始化UmengSDK进行信息上报】 */ public static void init(@NonNull Context context) { Context appContext = context.getApplicationContext(); if (appContext instanceof Application) { init((Application) appContext); } } /** * 初始化SDK,合规指南【先进行预初始化,如果用户隐私同意后可以初始化UmengSDK进行信息上报】 */ public static void init(Application application) { // 运营统计数据调试运行时不初始化 if (MyApp.isDebug()) { return; } UMConfigure.setLogEnabled(false); UMConfigure.preInit(application, BuildConfig.APP_ID_UMENG, getChannel(application)); // 用户同意了隐私协议 if (isAgreePrivacy()) { realInit(application); } } /** * @return 用户是否同意了隐私协议 */ private static boolean isAgreePrivacy() { // TODO: 2021/5/11 隐私协议设置 return true; } /** * 真实的初始化UmengSDK【进行设备信息的统计上报,必须在获得用户隐私同意后方可调用】 */ private static void realInit(Application application) { // 运营统计数据调试运行时不初始化 if (MyApp.isDebug()) { return; } //初始化组件化基础库, 注意: 即使您已经在AndroidManifest.xml中配置过appkey和channel值,也需要在App代码中调用初始化接口(如需要使用AndroidManifest.xml中配置好的appkey和channel值,UMConfigure.init调用中appkey和channel参数请置为null)。 //第二个参数是appkey,最后一个参数是pushSecret //这里BuildConfig.APP_ID_UMENG是根据local.properties中定义的APP_ID_UMENG生成的,只是运行看效果的话,可以不初始化该SDK UMConfigure.init(application, BuildConfig.APP_ID_UMENG, getChannel(application), UMConfigure.DEVICE_TYPE_PHONE, ""); //统计SDK是否支持采集在子进程中打点的自定义事件,默认不支持 //支持多进程打点 UMConfigure.setProcessEvent(true); MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO); } /** * 获取渠道信息 */ private static String getChannel(final Context context) { return WalleChannelReader.getChannel(context, DEFAULT_CHANNEL_ID); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/utils/sdkinit/XBasicLibInit.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.utils.sdkinit; import android.app.Application; import com.xuexiang.xtaskdemo.MyApp; import com.xuexiang.xtaskdemo.core.BaseActivity; import com.xuexiang.xtaskdemo.utils.XToastUtils; import com.xuexiang.xaop.XAOP; import com.xuexiang.xpage.PageConfig; import com.xuexiang.xrouter.launcher.XRouter; import com.xuexiang.xui.XUI; import com.xuexiang.xutil.XUtil; import com.xuexiang.xutil.common.StringUtils; /** * X系列基础库初始化 * * @author xuexiang * @since 2019-06-30 23:54 */ public final class XBasicLibInit { private XBasicLibInit() { throw new UnsupportedOperationException("u can't instantiate me..."); } /** * 初始化基础库SDK */ public static void init(Application application) { //工具类 initXUtil(application); //页面框架 initXPage(application); //切片框架 initXAOP(application); //UI框架 initXUI(application); //路由框架 initRouter(application); } /** * 初始化XUtil工具类 */ private static void initXUtil(Application application) { XUtil.init(application); XUtil.debug(MyApp.isDebug()); } /** * 初始化XPage页面框架 */ private static void initXPage(Application application) { PageConfig.getInstance() .debug(MyApp.isDebug() ? "PageLog" : null) .setContainActivityClazz(BaseActivity.class) .init(application); } /** * 初始化XAOP */ private static void initXAOP(Application application) { XAOP.init(application); XAOP.debug(MyApp.isDebug()); //设置动态申请权限切片 申请权限被拒绝的事件响应监听 XAOP.setOnPermissionDeniedListener(permissionsDenied -> XToastUtils.error("权限申请被拒绝:" + StringUtils.listToString(permissionsDenied, ","))); } /** * 初始化XUI框架 */ private static void initXUI(Application application) { XUI.init(application); XUI.debug(MyApp.isDebug()); } /** * 初始化路由框架 */ private static void initRouter(Application application) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 if (MyApp.isDebug()) { XRouter.openLog(); // 打印日志 XRouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) } XRouter.init(application); } } ================================================ FILE: app/src/main/java/com/xuexiang/xtaskdemo/utils/service/JsonSerializationService.java ================================================ /* * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.xuexiang.xtaskdemo.utils.service; import android.content.Context; import com.xuexiang.xrouter.annotation.Router; import com.xuexiang.xrouter.facade.service.SerializationService; import com.xuexiang.xutil.net.JsonUtil; import java.lang.reflect.Type; /** * @author XUE * @since 2019/3/27 16:39 */ @Router(path = "/service/json") public class JsonSerializationService implements SerializationService { /** * 对象序列化为json * * @param instance obj * @return json string */ @Override public String object2Json(Object instance) { return JsonUtil.toJson(instance); } /** * json反序列化为对象 * * @param input json string * @param clazz object type * @return instance of object */ @Override public T parseObject(String input, Type clazz) { return JsonUtil.fromJson(input, clazz); } /** * 进程初始化的方法 * * @param context 上下文 */ @Override public void init(Context context) { } } ================================================ FILE: app/src/main/res/drawable/ic_launcher_background.xml ================================================ ================================================ FILE: app/src/main/res/drawable-v24/ic_launcher_foreground.xml ================================================ ================================================ FILE: app/src/main/res/layout/adapter_item_simple_list_2.xml ================================================ ================================================ FILE: app/src/main/res/layout/fragment_usecase_compare.xml ================================================