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
================================================
================================================
FILE: app/src/main/res/layout/fragment_usecase_template.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: app/src/main/res/values/colors.xml
================================================
#299EE3
#299EE3
#299EE3
@color/xui_config_color_white
@color/xui_config_color_red
@color/colorAccent
#388E3C
@color/xui_config_color_waring
#353A3E
================================================
FILE: app/src/main/res/values/strings.xml
================================================
XTaskDemo
================================================
FILE: app/src/main/res/values/styles.xml
================================================
================================================
FILE: app/src/main/res/values/styles_widget.xml
================================================
================================================
FILE: app/src/test/java/com/xuexiang/xtaskdemo/ExampleUnitTest.java
================================================
package com.xuexiang.xtaskdemo;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see Testing documentation
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}
================================================
FILE: app/x-library-simple.gradle
================================================
//引用XAOP插件
apply plugin: 'com.xuexiang.xaop'
//引用XRouter-plugin插件实现自动注册
apply plugin: 'com.xuexiang.xrouter'
//引用aspectjx插件
apply plugin: 'android-aspectjx'
//自动添加依赖
project.configurations.each { configuration ->
def dependencies = getProject().dependencies
if (configuration.name == "implementation") {
//为Project加入X-Library依赖
//XUI框架
configuration.dependencies.add(dependencies.create(deps.xlibrary.xui))
configuration.dependencies.add(dependencies.create(deps.androidx.appcompat))
configuration.dependencies.add(dependencies.create(deps.androidx.recyclerview))
configuration.dependencies.add(dependencies.create(deps.androidx.design))
configuration.dependencies.add(dependencies.create(deps.glide))
//XUtil工具类
configuration.dependencies.add(dependencies.create(deps.xlibrary.xutil_core))
//XAOP切片
configuration.dependencies.add(dependencies.create(deps.xlibrary.xaop_runtime))
//XPage
configuration.dependencies.add(dependencies.create(deps.xlibrary.xpage_lib))
configuration.dependencies.add(dependencies.create(deps.butterknife.runtime))
//XRouter
configuration.dependencies.add(dependencies.create(deps.xlibrary.xrouter_runtime))
}
if (configuration.name == "annotationProcessor") {
//XPage
configuration.dependencies.add(dependencies.create(deps.xlibrary.xpage_compiler))
configuration.dependencies.add(dependencies.create(deps.butterknife.compiler))
//页面路由
configuration.dependencies.add(dependencies.create(deps.xlibrary.xrouter_compiler))
}
if (configuration.name == "debugImplementation") {
//内存泄漏监测leak
configuration.dependencies.add(dependencies.create(deps.leakcanary))
}
}
aspectjx {
include 'com.xuexiang.xtaskdemo'
}
================================================
FILE: art/xtask.mdj
================================================
{
"_type": "Project",
"_id": "AAAAAAFF+h6SjaM2Hec=",
"name": "Untitled",
"ownedElements": [
{
"_type": "UMLModel",
"_id": "AAAAAAFF+qBWK6M3Z8Y=",
"_parent": {
"$ref": "AAAAAAFF+h6SjaM2Hec="
},
"name": "Model",
"ownedElements": [
{
"_type": "UMLClassDiagram",
"_id": "AAAAAAFF+qBtyKM79qY=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "Main",
"defaultDiagram": true,
"ownedViews": [
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/oB4+v237OY=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oB4+/2436k=",
"_parent": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"model": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oB4+/25mFE=",
"_parent": {
"$ref": "AAAAAAF+/oB4+/2436k="
},
"visible": false,
"font": "Arial;13;0",
"left": 128,
"top": 16,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oB4+/26qTc=",
"_parent": {
"$ref": "AAAAAAF+/oB4+/2436k="
},
"font": "Arial;13;1",
"left": 213,
"top": 423,
"width": 111.72509765625,
"height": 13,
"text": "TaskChainEngine"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oB4+/27ttU=",
"_parent": {
"$ref": "AAAAAAF+/oB4+/2436k="
},
"visible": false,
"font": "Arial;13;0",
"left": 128,
"top": 16,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oB4+/28A9A=",
"_parent": {
"$ref": "AAAAAAF+/oB4+/2436k="
},
"visible": false,
"font": "Arial;13;0",
"left": 128,
"top": 16,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 208,
"top": 416,
"width": 121.72509765625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oB4+/25mFE="
},
"nameLabel": {
"$ref": "AAAAAAF+/oB4+/26qTc="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oB4+/27ttU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oB4+/28A9A="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oB4+/293p0=",
"_parent": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"model": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"font": "Arial;13;0",
"left": 208,
"top": 441,
"width": 121.72509765625,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oB4+/2+/10=",
"_parent": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"model": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"font": "Arial;13;0",
"left": 208,
"top": 451,
"width": 121.72509765625,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oB4+/2/nPA=",
"_parent": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"model": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"visible": false,
"font": "Arial;13;0",
"left": 64,
"top": 8,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oB4+/3AZuo=",
"_parent": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"model": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"visible": false,
"font": "Arial;13;0",
"left": 64,
"top": 8,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 208,
"top": 416,
"width": 121.72509765625,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/oB4+/2436k="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/oB4+/293p0="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oB4+/2+/10="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oB4+/2/nPA="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oB4+/3AZuo="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oDrxP3jEnk=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oDrxP3kV6w=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"model": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oDrxP3lX10=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3kV6w="
},
"visible": false,
"font": "Arial;13;0",
"left": -128,
"top": -336,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oDrxf3md0M=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3kV6w="
},
"font": "Arial;13;1",
"left": 213,
"top": 342,
"width": 111.72509765625,
"height": 13,
"text": "ITaskChainEngine"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oDrxf3ncKk=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3kV6w="
},
"visible": false,
"font": "Arial;13;0",
"left": -128,
"top": -336,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oDrxf3oR5g=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3kV6w="
},
"visible": false,
"font": "Arial;13;0",
"left": -128,
"top": -336,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 208,
"top": 335,
"width": 121.72509765625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oDrxP3lX10="
},
"nameLabel": {
"$ref": "AAAAAAF+/oDrxf3md0M="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oDrxf3ncKk="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oDrxf3oR5g="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oDrxf3pgEU=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"model": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"visible": false,
"font": "Arial;13;0",
"left": -64,
"top": -168,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oDrxf3qlZQ=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"model": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"visible": false,
"font": "Arial;13;0",
"left": -64,
"top": -168,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oDrxf3rOcM=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"model": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"visible": false,
"font": "Arial;13;0",
"left": -64,
"top": -168,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oDrxf3sbgc=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"model": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"visible": false,
"font": "Arial;13;0",
"left": -64,
"top": -168,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 208,
"top": 312,
"width": 121.72509765625,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oDrxP3kV6w="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oDrxf3pgEU="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oDrxf3qlZQ="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oDrxf3rOcM="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oDrxf3sbgc="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oEl+P4P5Ko=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oEl+P4Q9jY=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4P5Ko="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oEl+P4Rcb8=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4Q9jY="
},
"visible": false,
"font": "Arial;13;0",
"left": -240,
"top": -736,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oEl+P4S4Jw=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4Q9jY="
},
"font": "Arial;13;1",
"left": 37,
"top": 198,
"width": 116.09228515625,
"height": 13,
"text": "ITaskStepLifecycle"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oEl+P4TTvo=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4Q9jY="
},
"visible": false,
"font": "Arial;13;0",
"left": -240,
"top": -736,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oEl+P4Uv2o=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4Q9jY="
},
"visible": false,
"font": "Arial;13;0",
"left": -240,
"top": -736,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 32,
"top": 191,
"width": 126.09228515625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oEl+P4Rcb8="
},
"nameLabel": {
"$ref": "AAAAAAF+/oEl+P4S4Jw="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oEl+P4TTvo="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oEl+P4Uv2o="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oEl+P4VgnY=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4P5Ko="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": -120,
"top": -368,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oEl+P4WViE=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4P5Ko="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": -120,
"top": -368,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oEl+P4Xwx4=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4P5Ko="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": -120,
"top": -368,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oEl+P4YpRw=",
"_parent": {
"$ref": "AAAAAAF+/oEl+P4P5Ko="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": -120,
"top": -368,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 32,
"top": 168,
"width": 126.09228515625,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oEl+P4Q9jY="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oEl+P4VgnY="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oEl+P4WViE="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oEl+P4Xwx4="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oEl+P4YpRw="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oFcZv45QQE=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oFcZv46lfA=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv45QQE="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oFcZv47z7I=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv46lfA="
},
"visible": false,
"font": "Arial;13;0",
"left": -496,
"top": -864,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oFcZv48dXA=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv46lfA="
},
"font": "Arial;13;1",
"left": 221,
"top": 174,
"width": 99.4423828125,
"height": 13,
"text": "IGroupTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oFcZv49tL8=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv46lfA="
},
"visible": false,
"font": "Arial;13;0",
"left": -496,
"top": -864,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oFcZv4+arw=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv46lfA="
},
"visible": false,
"font": "Arial;13;0",
"left": -496,
"top": -864,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 216,
"top": 167,
"width": 109.4423828125,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oFcZv47z7I="
},
"nameLabel": {
"$ref": "AAAAAAF+/oFcZv48dXA="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oFcZv49tL8="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oFcZv4+arw="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oFcZv4/0lw=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv45QQE="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": -248,
"top": -432,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oFcZv5ACvQ=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv45QQE="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": -248,
"top": -432,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oFcZv5BKZA=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv45QQE="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": -248,
"top": -432,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oFcZv5CjKc=",
"_parent": {
"$ref": "AAAAAAF+/oFcZv45QQE="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": -248,
"top": -432,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 216,
"top": 144,
"width": 109.4423828125,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oFcZv46lfA="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oFcZv4/0lw="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oFcZv5ACvQ="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oFcZv5BKZA="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oFcZv5CjKc="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oF4Mv5jM5A=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oF4Mv5kDeg=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"model": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oF4Mv5lotA=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5kDeg="
},
"visible": false,
"font": "Arial;13;0",
"left": 176,
"top": -848,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oF4Mv5mPtk=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5kDeg="
},
"font": "Arial;13;1",
"left": 413,
"top": 206,
"width": 62.1435546875,
"height": 13,
"text": "ICanceller"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oF4Mv5nAew=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5kDeg="
},
"visible": false,
"font": "Arial;13;0",
"left": 176,
"top": -848,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oF4Mv5ojqM=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5kDeg="
},
"visible": false,
"font": "Arial;13;0",
"left": 176,
"top": -848,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 408,
"top": 199,
"width": 72.1435546875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oF4Mv5lotA="
},
"nameLabel": {
"$ref": "AAAAAAF+/oF4Mv5mPtk="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oF4Mv5nAew="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oF4Mv5ojqM="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oF4Mv5pTzQ=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"model": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"visible": false,
"font": "Arial;13;0",
"left": 88,
"top": -424,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oF4Mv5qoZk=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"model": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"visible": false,
"font": "Arial;13;0",
"left": 88,
"top": -424,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oF4Mv5rRNw=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"model": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"visible": false,
"font": "Arial;13;0",
"left": 88,
"top": -424,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oF4Mv5sX1A=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"model": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"visible": false,
"font": "Arial;13;0",
"left": 88,
"top": -424,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 408,
"top": 176,
"width": 72.1435546875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oF4Mv5kDeg="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oF4Mv5pTzQ="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oF4Mv5qoZk="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oF4Mv5rRNw="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oF4Mv5sX1A="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oIYZv6Y+i8=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oIYZf6W7vU="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oIYZ/6ZwPo=",
"_parent": {
"$ref": "AAAAAAF+/oIYZv6Y+i8="
},
"model": {
"$ref": "AAAAAAF+/oIYZf6W7vU="
},
"visible": false,
"font": "Arial;13;0",
"left": 171,
"top": 269,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oIYZv6Y+i8="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oIYZ/6a/q8=",
"_parent": {
"$ref": "AAAAAAF+/oIYZv6Y+i8="
},
"model": {
"$ref": "AAAAAAF+/oIYZf6W7vU="
},
"visible": null,
"font": "Arial;13;0",
"left": 161,
"top": 281,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oIYZv6Y+i8="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oIYZ/6btmc=",
"_parent": {
"$ref": "AAAAAAF+/oIYZv6Y+i8="
},
"model": {
"$ref": "AAAAAAF+/oIYZf6W7vU="
},
"visible": false,
"font": "Arial;13;0",
"left": 190,
"top": 246,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oIYZv6Y+i8="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oEl+P4P5Ko="
},
"tail": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"lineStyle": 1,
"points": "238:311;124:217",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oIYZ/6ZwPo="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oIYZ/6a/q8="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oIYZ/6btmc="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oIuff6py/M=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oIuff6ny+c="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oIuff6qeqM=",
"_parent": {
"$ref": "AAAAAAF+/oIuff6py/M="
},
"model": {
"$ref": "AAAAAAF+/oIuff6ny+c="
},
"visible": false,
"font": "Arial;13;0",
"left": 254,
"top": 245,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oIuff6py/M="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oIuff6rBjM=",
"_parent": {
"$ref": "AAAAAAF+/oIuff6py/M="
},
"model": {
"$ref": "AAAAAAF+/oIuff6ny+c="
},
"visible": null,
"font": "Arial;13;0",
"left": 239,
"top": 245,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oIuff6py/M="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oIuff6svG8=",
"_parent": {
"$ref": "AAAAAAF+/oIuff6py/M="
},
"model": {
"$ref": "AAAAAAF+/oIuff6ny+c="
},
"visible": false,
"font": "Arial;13;0",
"left": 283,
"top": 246,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oIuff6py/M="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oFcZv45QQE="
},
"tail": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"lineStyle": 1,
"points": "268:311;270:193",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oIuff6qeqM="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oIuff6rBjM="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oIuff6svG8="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oJAIf66bOw=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oJAIf64xJQ="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oJAIf67sGQ=",
"_parent": {
"$ref": "AAAAAAF+/oJAIf66bOw="
},
"model": {
"$ref": "AAAAAAF+/oJAIf64xJQ="
},
"visible": false,
"font": "Arial;13;0",
"left": 345,
"top": 250,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oJAIf66bOw="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oJAIf68e+M=",
"_parent": {
"$ref": "AAAAAAF+/oJAIf66bOw="
},
"model": {
"$ref": "AAAAAAF+/oJAIf64xJQ="
},
"visible": null,
"font": "Arial;13;0",
"left": 336,
"top": 238,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oJAIf66bOw="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oJAIf695Jk=",
"_parent": {
"$ref": "AAAAAAF+/oJAIf66bOw="
},
"model": {
"$ref": "AAAAAAF+/oJAIf64xJQ="
},
"visible": false,
"font": "Arial;13;0",
"left": 364,
"top": 273,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oJAIf66bOw="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"tail": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"lineStyle": 1,
"points": "300:311;411:225",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oJAIf67sGQ="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oJAIf68e+M="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oJAIf695Jk="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/oKdTP7Qy2M=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oKdTP7PY/4="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oKdTP7RVbE=",
"_parent": {
"$ref": "AAAAAAF+/oKdTP7Qy2M="
},
"model": {
"$ref": "AAAAAAF+/oKdTP7PY/4="
},
"visible": false,
"font": "Arial;13;0",
"left": 253,
"top": 368,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oKdTP7Qy2M="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oKdTP7ShG4=",
"_parent": {
"$ref": "AAAAAAF+/oKdTP7Qy2M="
},
"model": {
"$ref": "AAAAAAF+/oKdTP7PY/4="
},
"visible": null,
"font": "Arial;13;0",
"left": 238,
"top": 368,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oKdTP7Qy2M="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oKdTP7TOVA=",
"_parent": {
"$ref": "AAAAAAF+/oKdTP7Qy2M="
},
"model": {
"$ref": "AAAAAAF+/oKdTP7PY/4="
},
"visible": false,
"font": "Arial;13;0",
"left": 282,
"top": 369,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oKdTP7Qy2M="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oDrxP3jEnk="
},
"tail": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"lineStyle": 1,
"points": "268:415;268:335",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oKdTP7RVbE="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oKdTP7ShG4="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oKdTP7TOVA="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oMJbv7mDqg=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oMJbv7nb64=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"model": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oMJbv7oytA=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7nb64="
},
"visible": false,
"font": "Arial;13;0",
"left": 752,
"top": -432,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oMJbv7pHRg=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7nb64="
},
"font": "Arial;13;1",
"left": 629,
"top": 342,
"width": 61.419921875,
"height": 13,
"text": "ITaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oMJbv7qtPE=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7nb64="
},
"visible": false,
"font": "Arial;13;0",
"left": 752,
"top": -432,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oMJbv7ri60=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7nb64="
},
"visible": false,
"font": "Arial;13;0",
"left": 752,
"top": -432,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 624,
"top": 335,
"width": 71.419921875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oMJbv7oytA="
},
"nameLabel": {
"$ref": "AAAAAAF+/oMJbv7pHRg="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oMJbv7qtPE="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oMJbv7ri60="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oMJbv7sXjY=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"model": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"visible": false,
"font": "Arial;13;0",
"left": 376,
"top": -216,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oMJbv7tVVM=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"model": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"visible": false,
"font": "Arial;13;0",
"left": 376,
"top": -216,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oMJbv7uN74=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"model": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"visible": false,
"font": "Arial;13;0",
"left": 376,
"top": -216,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oMJbv7vMAk=",
"_parent": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"model": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"visible": false,
"font": "Arial;13;0",
"left": 376,
"top": -216,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 624,
"top": 312,
"width": 71.419921875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oMJbv7nb64="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oMJbv7sXjY="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oMJbv7tVVM="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oMJbv7uN74="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oMJbv7vMAk="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oN48v8Qo+U=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oN48v8O7tk="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oN48v8RdDE=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"model": {
"$ref": "AAAAAAF+/oN48v8O7tk="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oN48v8Srzk=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8RdDE="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oN48v8THqs=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8RdDE="
},
"font": "Arial;13;1",
"left": 469,
"top": 102,
"width": 72.25537109375,
"height": 13,
"text": "ICancelable"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oN48v8UzP0=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8RdDE="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oN48v8VRCk=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8RdDE="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 464,
"top": 95,
"width": 82.25537109375,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oN48v8Srzk="
},
"nameLabel": {
"$ref": "AAAAAAF+/oN48v8THqs="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oN48v8UzP0="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oN48v8VRCk="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oN48v8WqcM=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"model": {
"$ref": "AAAAAAF+/oN48v8O7tk="
},
"visible": false,
"font": "Arial;13;0",
"left": -144,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oN48/8X8FU=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"model": {
"$ref": "AAAAAAF+/oN48v8O7tk="
},
"visible": false,
"font": "Arial;13;0",
"left": -144,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oN48/8Yhis=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"model": {
"$ref": "AAAAAAF+/oN48v8O7tk="
},
"visible": false,
"font": "Arial;13;0",
"left": -144,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oN48/8ZxBY=",
"_parent": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"model": {
"$ref": "AAAAAAF+/oN48v8O7tk="
},
"visible": false,
"font": "Arial;13;0",
"left": -144,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 464,
"top": 72,
"width": 82.25537109375,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oN48v8RdDE="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oN48v8WqcM="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oN48/8X8FU="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oN48/8Yhis="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oN48/8ZxBY="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oOe/P87GbQ=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oOe+/85LIw="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oOe/f88fgY=",
"_parent": {
"$ref": "AAAAAAF+/oOe/P87GbQ="
},
"model": {
"$ref": "AAAAAAF+/oOe+/85LIw="
},
"visible": false,
"font": "Arial;13;0",
"left": 460,
"top": 134,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oOe/P87GbQ="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oOe/f89lPI=",
"_parent": {
"$ref": "AAAAAAF+/oOe/P87GbQ="
},
"model": {
"$ref": "AAAAAAF+/oOe+/85LIw="
},
"visible": null,
"font": "Arial;13;0",
"left": 447,
"top": 126,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oOe/P87GbQ="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oOe/f8+wgA=",
"_parent": {
"$ref": "AAAAAAF+/oOe/P87GbQ="
},
"model": {
"$ref": "AAAAAAF+/oOe+/85LIw="
},
"visible": false,
"font": "Arial;13;0",
"left": 485,
"top": 149,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oOe/P87GbQ="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"tail": {
"$ref": "AAAAAAF+/oF4Mv5jM5A="
},
"lineStyle": 1,
"points": "458:175;489:121",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oOe/f88fgY="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oOe/f89lPI="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oOe/f8+wgA="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oQKQ/9PN2k=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oQKQ/9NvDs="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oQKQ/9Qk4s=",
"_parent": {
"$ref": "AAAAAAF+/oQKQ/9PN2k="
},
"model": {
"$ref": "AAAAAAF+/oQKQ/9NvDs="
},
"visible": false,
"font": "Arial;13;0",
"left": 568,
"top": 218,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oQKQ/9PN2k="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oQKQ/9RxXg=",
"_parent": {
"$ref": "AAAAAAF+/oQKQ/9PN2k="
},
"model": {
"$ref": "AAAAAAF+/oQKQ/9NvDs="
},
"visible": null,
"font": "Arial;13;0",
"left": 555,
"top": 226,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oQKQ/9PN2k="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oQKQ/9S9u4=",
"_parent": {
"$ref": "AAAAAAF+/oQKQ/9PN2k="
},
"model": {
"$ref": "AAAAAAF+/oQKQ/9NvDs="
},
"visible": false,
"font": "Arial;13;0",
"left": 593,
"top": 201,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oQKQ/9PN2k="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oN48v8Qo+U="
},
"tail": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"lineStyle": 1,
"points": "643:311;520:121",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oQKQ/9Qk4s="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oQKQ/9RxXg="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oQKQ/9S9u4="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oQybv9gcdI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oQybv9h1Ug=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9gcdI="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oQybv9iy2A=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9h1Ug="
},
"visible": false,
"font": "Arial;13;0",
"left": -400,
"top": 112,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oQybv9jJxs=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9h1Ug="
},
"font": "Arial;13;1",
"left": 605,
"top": 166,
"width": 122.560546875,
"height": 13,
"text": "ITaskStepController"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oQybv9kWek=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9h1Ug="
},
"visible": false,
"font": "Arial;13;0",
"left": -400,
"top": 112,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oQybv9la2g=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9h1Ug="
},
"visible": false,
"font": "Arial;13;0",
"left": -400,
"top": 112,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 600,
"top": 159,
"width": 132.560546875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oQybv9iy2A="
},
"nameLabel": {
"$ref": "AAAAAAF+/oQybv9jJxs="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oQybv9kWek="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oQybv9la2g="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oQybv9m5JU=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9gcdI="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -200,
"top": 56,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oQybv9npHY=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9gcdI="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -200,
"top": 56,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oQybv9oHm4=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9gcdI="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -200,
"top": 56,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oQybv9p5Cc=",
"_parent": {
"$ref": "AAAAAAF+/oQybv9gcdI="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -200,
"top": 56,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 600,
"top": 136,
"width": 132.560546875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oQybv9h1Ug="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oQybv9m5JU="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oQybv9npHY="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oQybv9oHm4="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oQybv9p5Cc="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oRXKv+K4yc=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oRXKv+Ij8g="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oRXKv+LD5M=",
"_parent": {
"$ref": "AAAAAAF+/oRXKv+K4yc="
},
"model": {
"$ref": "AAAAAAF+/oRXKv+Ij8g="
},
"visible": false,
"font": "Arial;13;0",
"left": 647,
"top": 241,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oRXKv+K4yc="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oRXKv+MJCw=",
"_parent": {
"$ref": "AAAAAAF+/oRXKv+K4yc="
},
"model": {
"$ref": "AAAAAAF+/oRXKv+Ij8g="
},
"visible": null,
"font": "Arial;13;0",
"left": 632,
"top": 241,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oRXKv+K4yc="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oRXKv+ND5M=",
"_parent": {
"$ref": "AAAAAAF+/oRXKv+K4yc="
},
"model": {
"$ref": "AAAAAAF+/oRXKv+Ij8g="
},
"visible": false,
"font": "Arial;13;0",
"left": 676,
"top": 242,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oRXKv+K4yc="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oQybv9gcdI="
},
"tail": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"lineStyle": 1,
"points": "660:311;664:185",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oRXKv+LD5M="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oRXKv+MJCw="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oRXKv+ND5M="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oSvGv+dGTE=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oSvGv+e0Rc=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+dGTE="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oSvGv+fGD8=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+e0Rc="
},
"visible": false,
"font": "Arial;13;0",
"left": -336,
"top": 144,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oSvGv+gHaI=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+e0Rc="
},
"font": "Arial;13;1",
"left": 765,
"top": 174,
"width": 61.419921875,
"height": 13,
"text": "Runnable"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oSvGv+hXNE=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+e0Rc="
},
"visible": false,
"font": "Arial;13;0",
"left": -336,
"top": 144,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oSvGv+iZ8E=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+e0Rc="
},
"visible": false,
"font": "Arial;13;0",
"left": -336,
"top": 144,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 760,
"top": 167,
"width": 71.419921875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oSvGv+fGD8="
},
"nameLabel": {
"$ref": "AAAAAAF+/oSvGv+gHaI="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oSvGv+hXNE="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oSvGv+iZ8E="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oSvGv+j17A=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+dGTE="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -168,
"top": 72,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oSvGv+k+VU=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+dGTE="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -168,
"top": 72,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oSvGv+lz4k=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+dGTE="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -168,
"top": 72,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oSvGv+mw8k=",
"_parent": {
"$ref": "AAAAAAF+/oSvGv+dGTE="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -168,
"top": 72,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 760,
"top": 144,
"width": 71.419921875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oSvGv+e0Rc="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oSvGv+j17A="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oSvGv+k+VU="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oSvGv+lz4k="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oSvGv+mw8k="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oT/Ff/I3EI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oT/FP/Gwcg="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oT/Ff/JQuY=",
"_parent": {
"$ref": "AAAAAAF+/oT/Ff/I3EI="
},
"model": {
"$ref": "AAAAAAF+/oT/FP/Gwcg="
},
"visible": false,
"font": "Arial;13;0",
"left": 715,
"top": 236,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oT/Ff/I3EI="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oT/Ff/KaZo=",
"_parent": {
"$ref": "AAAAAAF+/oT/Ff/I3EI="
},
"model": {
"$ref": "AAAAAAF+/oT/FP/Gwcg="
},
"visible": null,
"font": "Arial;13;0",
"left": 703,
"top": 227,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oT/Ff/I3EI="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oT/Ff/LUGw=",
"_parent": {
"$ref": "AAAAAAF+/oT/Ff/I3EI="
},
"model": {
"$ref": "AAAAAAF+/oT/FP/Gwcg="
},
"visible": false,
"font": "Arial;13;0",
"left": 738,
"top": 255,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oT/Ff/I3EI="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oSvGv+dGTE="
},
"tail": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"lineStyle": 1,
"points": "679:311;775:193",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oT/Ff/JQuY="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oT/Ff/KaZo="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oT/Ff/LUGw="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/oVQfv/bUgI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oVQfv/c+MQ=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"model": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oVQfv/dQHE=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/c+MQ="
},
"visible": false,
"font": "Arial;13;0",
"top": 48,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oVQfv/eH6w=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/c+MQ="
},
"font": "Arial;13;1",
"left": 605,
"top": 471,
"width": 109.5732421875,
"height": 13,
"text": "AbstractTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oVQfv/fBds=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/c+MQ="
},
"visible": false,
"font": "Arial;13;0",
"top": 48,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oVQfv/gJVg=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/c+MQ="
},
"visible": false,
"font": "Arial;13;0",
"top": 48,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 600,
"top": 464,
"width": 119.5732421875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oVQfv/dQHE="
},
"nameLabel": {
"$ref": "AAAAAAF+/oVQfv/eH6w="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oVQfv/fBds="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oVQfv/gJVg="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oVQfv/hHrY=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"model": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"font": "Arial;13;0",
"left": 600,
"top": 489,
"width": 119.5732421875,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oVQfv/iFiQ=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"model": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"font": "Arial;13;0",
"left": 600,
"top": 499,
"width": 119.5732421875,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oVQfv/jaUM=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"model": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"visible": false,
"font": "Arial;13;0",
"top": 24,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oVQfv/kILc=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"model": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"visible": false,
"font": "Arial;13;0",
"top": 24,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 600,
"top": 464,
"width": 119.5732421875,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/oVQfv/c+MQ="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/oVQfv/hHrY="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oVQfv/iFiQ="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oVQfv/jaUM="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oVQfv/kILc="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/oVy3wAFTrw=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oVy3wAER5E="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oVy3wAG9c8=",
"_parent": {
"$ref": "AAAAAAF+/oVy3wAFTrw="
},
"model": {
"$ref": "AAAAAAF+/oVy3wAER5E="
},
"visible": false,
"font": "Arial;13;0",
"left": 644,
"top": 392,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oVy3wAFTrw="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oVy3wAHRJU=",
"_parent": {
"$ref": "AAAAAAF+/oVy3wAFTrw="
},
"model": {
"$ref": "AAAAAAF+/oVy3wAER5E="
},
"visible": null,
"font": "Arial;13;0",
"left": 629,
"top": 392,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oVy3wAFTrw="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oVy3wAIz+0=",
"_parent": {
"$ref": "AAAAAAF+/oVy3wAFTrw="
},
"model": {
"$ref": "AAAAAAF+/oVy3wAER5E="
},
"visible": false,
"font": "Arial;13;0",
"left": 673,
"top": 393,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oVy3wAFTrw="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"tail": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"lineStyle": 1,
"points": "659:463;659:335",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oVy3wAG9c8="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oVy3wAHRJU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oVy3wAIz+0="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/oXK5AAY5hk=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oXK5AAZrQw=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAY5hk="
},
"model": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oXK5AAaV1E=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAZrQw="
},
"visible": false,
"font": "Arial;13;0",
"left": 288,
"top": 224,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oXK5AAbNfc=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAZrQw="
},
"font": "Arial;13;1",
"left": 709,
"top": 623,
"width": 99.46142578125,
"height": 13,
"text": "SimpleTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oXK5AAclbU=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAZrQw="
},
"visible": false,
"font": "Arial;13;0",
"left": 288,
"top": 224,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oXK5AAdEQU=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAZrQw="
},
"visible": false,
"font": "Arial;13;0",
"left": 288,
"top": 224,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 704,
"top": 616,
"width": 109.46142578125,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oXK5AAaV1E="
},
"nameLabel": {
"$ref": "AAAAAAF+/oXK5AAbNfc="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oXK5AAclbU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oXK5AAdEQU="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oXK5AAeq0Q=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAY5hk="
},
"model": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"font": "Arial;13;0",
"left": 704,
"top": 641,
"width": 109.46142578125,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oXK5AAffno=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAY5hk="
},
"model": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"font": "Arial;13;0",
"left": 704,
"top": 651,
"width": 109.46142578125,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oXK5AAgHVA=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAY5hk="
},
"model": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"visible": false,
"font": "Arial;13;0",
"left": 144,
"top": 112,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oXK5AAhS08=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAY5hk="
},
"model": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"visible": false,
"font": "Arial;13;0",
"left": 144,
"top": 112,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 704,
"top": 616,
"width": 109.46142578125,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/oXK5AAZrQw="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/oXK5AAeq0Q="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oXK5AAffno="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oXK5AAgHVA="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oXK5AAhS08="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/oYK4wBDOcU=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oYK4wBEb+Q=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"model": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oYK4wBFYc4=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBEb+Q="
},
"visible": false,
"font": "Arial;13;0",
"left": -256,
"top": 240,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oYK4wBGZQo=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBEb+Q="
},
"font": "Arial;13;1",
"left": 589,
"top": 623,
"width": 65.5078125,
"height": 13,
"text": "XTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oYK4wBHsFA=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBEb+Q="
},
"visible": false,
"font": "Arial;13;0",
"left": -256,
"top": 240,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oYK4wBIZ+Y=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBEb+Q="
},
"visible": false,
"font": "Arial;13;0",
"left": -256,
"top": 240,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 584,
"top": 616,
"width": 75.5078125,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oYK4wBFYc4="
},
"nameLabel": {
"$ref": "AAAAAAF+/oYK4wBGZQo="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oYK4wBHsFA="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oYK4wBIZ+Y="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oYK4wBJ65o=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"model": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"font": "Arial;13;0",
"left": 584,
"top": 641,
"width": 75.5078125,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oYK4wBKVkw=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"model": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"font": "Arial;13;0",
"left": 584,
"top": 651,
"width": 75.5078125,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oYK4wBLunc=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"model": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"visible": false,
"font": "Arial;13;0",
"left": -128,
"top": 120,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oYK5ABM3jQ=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"model": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"visible": false,
"font": "Arial;13;0",
"left": -128,
"top": 120,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 584,
"top": 616,
"width": 75.5078125,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/oYK4wBEb+Q="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/oYK4wBJ65o="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oYK4wBKVkw="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oYK4wBLunc="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oYK5ABM3jQ="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oY38QBxcIk=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oY38QBvDLM="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oY38QBy+KQ=",
"_parent": {
"$ref": "AAAAAAF+/oY38QBxcIk="
},
"model": {
"$ref": "AAAAAAF+/oY38QBvDLM="
},
"visible": false,
"font": "Arial;13;0",
"left": 625,
"top": 552,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oY38QBxcIk="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oY38gBzhIE=",
"_parent": {
"$ref": "AAAAAAF+/oY38QBxcIk="
},
"model": {
"$ref": "AAAAAAF+/oY38QBvDLM="
},
"visible": null,
"font": "Arial;13;0",
"left": 610,
"top": 548,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oY38QBxcIk="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oY38gB0htU=",
"_parent": {
"$ref": "AAAAAAF+/oY38QBxcIk="
},
"model": {
"$ref": "AAAAAAF+/oY38QBvDLM="
},
"visible": false,
"font": "Arial;13;0",
"left": 654,
"top": 559,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oY38QBxcIk="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"tail": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"lineStyle": 1,
"points": "627:615;653:509",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oY38QBy+KQ="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oY38gBzhIE="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oY38gB0htU="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oZEGACCCzk=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oZEGACAn0w="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oZEGACDv+I=",
"_parent": {
"$ref": "AAAAAAF+/oZEGACCCzk="
},
"model": {
"$ref": "AAAAAAF+/oZEGACAn0w="
},
"visible": false,
"font": "Arial;13;0",
"left": 695,
"top": 564,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oZEGACCCzk="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oZEGACELsA=",
"_parent": {
"$ref": "AAAAAAF+/oZEGACCCzk="
},
"model": {
"$ref": "AAAAAAF+/oZEGACAn0w="
},
"visible": null,
"font": "Arial;13;0",
"left": 682,
"top": 572,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oZEGACCCzk="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oZEGACFCnQ=",
"_parent": {
"$ref": "AAAAAAF+/oZEGACCCzk="
},
"model": {
"$ref": "AAAAAAF+/oZEGACAn0w="
},
"visible": false,
"font": "Arial;13;0",
"left": 720,
"top": 547,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oZEGACCCzk="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"tail": {
"$ref": "AAAAAAF+/oXK5AAY5hk="
},
"lineStyle": 1,
"points": "743:615;674:509",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oZEGACDv+I="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oZEGACELsA="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oZEGACFCnQ="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/obCKwCVu1U=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/obCKgCR2Jk="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACWSEs=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKgCR2Jk="
},
"font": "Arial;13;0",
"left": 441,
"top": 307,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"edgePosition": 1,
"text": "+n"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACXkZ0=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKgCR2Jk="
},
"visible": null,
"font": "Arial;13;0",
"left": 449,
"top": 292,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACYaow=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKgCR2Jk="
},
"visible": false,
"font": "Arial;13;0",
"left": 447,
"top": 336,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACZQPY=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCSX2E="
},
"visible": false,
"font": "Arial;13;0",
"left": 320,
"top": 382,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACavIo=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCSX2E="
},
"visible": false,
"font": "Arial;13;0",
"left": 315,
"top": 370,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACbAfE=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCSX2E="
},
"visible": false,
"font": "Arial;13;0",
"left": 330,
"top": 408,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACcDfA=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCTC3k="
},
"visible": false,
"font": "Arial;13;0",
"left": 597,
"top": 312,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACdsyw=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCTC3k="
},
"visible": false,
"font": "Arial;13;0",
"left": 595,
"top": 299,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/obCLACegVc=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCTC3k="
},
"visible": false,
"font": "Arial;13;0",
"left": 600,
"top": 340,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/obCKwCVu1U="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/obCLACf6xk=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCSX2E="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/obCLACgyTI=",
"_parent": {
"$ref": "AAAAAAF+/obCKwCVu1U="
},
"model": {
"$ref": "AAAAAAF+/obCKwCTC3k="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"tail": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"lineStyle": 1,
"points": "306:415;448:328;623:335",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/obCLACWSEs="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/obCLACXkZ0="
},
"propertyLabel": {
"$ref": "AAAAAAF+/obCLACYaow="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/obCLACZQPY="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/obCLACavIo="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/obCLACbAfE="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/obCLACcDfA="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/obCLACdsyw="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/obCLACegVc="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/obCLACf6xk="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/obCLACgyTI="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oejFwDmxXI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oejFwDkL34="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oejFwDnjsk=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"model": {
"$ref": "AAAAAAF+/oejFwDkL34="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oejFwDojFA=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDnjsk="
},
"visible": false,
"font": "Arial;13;0",
"left": 112,
"top": -256,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oejFwDpLvs=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDnjsk="
},
"font": "Arial;13;1",
"left": 381,
"top": 526,
"width": 65.01904296875,
"height": 13,
"text": "IDataStore"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oejFwDqviU=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDnjsk="
},
"visible": false,
"font": "Arial;13;0",
"left": 112,
"top": -256,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oejGADrg/w=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDnjsk="
},
"visible": false,
"font": "Arial;13;0",
"left": 112,
"top": -256,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 376,
"top": 519,
"width": 75.01904296875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oejFwDojFA="
},
"nameLabel": {
"$ref": "AAAAAAF+/oejFwDpLvs="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oejFwDqviU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oejGADrg/w="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oejGADsTs0=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"model": {
"$ref": "AAAAAAF+/oejFwDkL34="
},
"visible": false,
"font": "Arial;13;0",
"left": 56,
"top": -128,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oejGADts/g=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"model": {
"$ref": "AAAAAAF+/oejFwDkL34="
},
"visible": false,
"font": "Arial;13;0",
"left": 56,
"top": -128,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oejGADu4N0=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"model": {
"$ref": "AAAAAAF+/oejFwDkL34="
},
"visible": false,
"font": "Arial;13;0",
"left": 56,
"top": -128,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oejGADv4nU=",
"_parent": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"model": {
"$ref": "AAAAAAF+/oejFwDkL34="
},
"visible": false,
"font": "Arial;13;0",
"left": 56,
"top": -128,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 376,
"top": 496,
"width": 75.01904296875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oejFwDnjsk="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oejGADsTs0="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oejGADts/g="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oejGADu4N0="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oejGADv4nU="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/ofZUwElJVk=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/ofZUwEm+g4=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"model": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/ofZUwEn0HA=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwEm+g4="
},
"visible": false,
"font": "Arial;13;0",
"left": 32,
"top": -224,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ofZUwEoA6o=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwEm+g4="
},
"font": "Arial;13;1",
"left": 373,
"top": 646,
"width": 72.02685546875,
"height": 13,
"text": "ITaskParam"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ofZUwEpL30=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwEm+g4="
},
"visible": false,
"font": "Arial;13;0",
"left": 32,
"top": -224,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ofZUwEqSQA=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwEm+g4="
},
"visible": false,
"font": "Arial;13;0",
"left": 32,
"top": -224,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 368,
"top": 639,
"width": 82.02685546875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/ofZUwEn0HA="
},
"nameLabel": {
"$ref": "AAAAAAF+/ofZUwEoA6o="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/ofZUwEpL30="
},
"propertyLabel": {
"$ref": "AAAAAAF+/ofZUwEqSQA="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/ofZUwErdO4=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"model": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"visible": false,
"font": "Arial;13;0",
"left": 16,
"top": -112,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/ofZUwEs0Pg=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"model": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"visible": false,
"font": "Arial;13;0",
"left": 16,
"top": -112,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/ofZUwEtvqo=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"model": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"visible": false,
"font": "Arial;13;0",
"left": 16,
"top": -112,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/ofZUwEu2C0=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"model": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"visible": false,
"font": "Arial;13;0",
"left": 16,
"top": -112,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 368,
"top": 616,
"width": 82.02685546875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/ofZUwEm+g4="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/ofZUwErdO4="
},
"operationCompartment": {
"$ref": "AAAAAAF+/ofZUwEs0Pg="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/ofZUwEtvqo="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/ofZUwEu2C0="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/of2wAFux2o=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/of2wAFsd14="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/of2wAFvWWo=",
"_parent": {
"$ref": "AAAAAAF+/of2wAFux2o="
},
"model": {
"$ref": "AAAAAAF+/of2wAFsd14="
},
"visible": false,
"font": "Arial;13;0",
"left": 395,
"top": 573,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/of2wAFux2o="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/of2wAFwUeE=",
"_parent": {
"$ref": "AAAAAAF+/of2wAFux2o="
},
"model": {
"$ref": "AAAAAAF+/of2wAFsd14="
},
"visible": null,
"font": "Arial;13;0",
"left": 380,
"top": 572,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/of2wAFux2o="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/of2wAFxA6c=",
"_parent": {
"$ref": "AAAAAAF+/of2wAFux2o="
},
"model": {
"$ref": "AAAAAAF+/of2wAFsd14="
},
"visible": false,
"font": "Arial;13;0",
"left": 424,
"top": 574,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/of2wAFux2o="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"tail": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"lineStyle": 1,
"points": "409:615;412:545",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/of2wAFvWWo="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/of2wAFwUeE="
},
"propertyLabel": {
"$ref": "AAAAAAF+/of2wAFxA6c="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/ogt1AGIzZI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/ogt1AGJSOI=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"model": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/ogt1AGKyJw=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGJSOI="
},
"visible": false,
"font": "Arial;13;0",
"left": 96,
"top": -192,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ogt1AGLfhA=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGJSOI="
},
"font": "Arial;13;1",
"left": 373,
"top": 735,
"width": 68.4150390625,
"height": 13,
"text": "TaskParam"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ogt1AGMuus=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGJSOI="
},
"visible": false,
"font": "Arial;13;0",
"left": 96,
"top": -192,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ogt1AGNW0U=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGJSOI="
},
"visible": false,
"font": "Arial;13;0",
"left": 96,
"top": -192,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 368,
"top": 728,
"width": 78.4150390625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/ogt1AGKyJw="
},
"nameLabel": {
"$ref": "AAAAAAF+/ogt1AGLfhA="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/ogt1AGMuus="
},
"propertyLabel": {
"$ref": "AAAAAAF+/ogt1AGNW0U="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/ogt1AGOZ4E=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"model": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"font": "Arial;13;0",
"left": 368,
"top": 753,
"width": 78.4150390625,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/ogt1AGPKYw=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"model": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"font": "Arial;13;0",
"left": 368,
"top": 763,
"width": 78.4150390625,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/ogt1QGQHFE=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"model": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"visible": false,
"font": "Arial;13;0",
"left": 48,
"top": -96,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/ogt1QGRJCQ=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"model": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"visible": false,
"font": "Arial;13;0",
"left": 48,
"top": -96,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 368,
"top": 728,
"width": 78.4150390625,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/ogt1AGJSOI="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/ogt1AGOZ4E="
},
"operationCompartment": {
"$ref": "AAAAAAF+/ogt1AGPKYw="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/ogt1QGQHFE="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/ogt1QGRJCQ="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/ohUsAHQ0vA=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/ohUsAHPXn8="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ohUsQHRAlk=",
"_parent": {
"$ref": "AAAAAAF+/ohUsAHQ0vA="
},
"model": {
"$ref": "AAAAAAF+/ohUsAHPXn8="
},
"visible": false,
"font": "Arial;13;0",
"left": 392,
"top": 676,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/ohUsAHQ0vA="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ohUsQHSJIg=",
"_parent": {
"$ref": "AAAAAAF+/ohUsAHQ0vA="
},
"model": {
"$ref": "AAAAAAF+/ohUsAHPXn8="
},
"visible": null,
"font": "Arial;13;0",
"left": 377,
"top": 676,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/ohUsAHQ0vA="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ohUsQHTQSs=",
"_parent": {
"$ref": "AAAAAAF+/ohUsAHQ0vA="
},
"model": {
"$ref": "AAAAAAF+/ohUsAHPXn8="
},
"visible": false,
"font": "Arial;13;0",
"left": 421,
"top": 677,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/ohUsAHQ0vA="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"tail": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"lineStyle": 1,
"points": "406:727;408:639",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/ohUsQHRAlk="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/ohUsQHSJIg="
},
"propertyLabel": {
"$ref": "AAAAAAF+/ohUsQHTQSs="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/oiH/gH+hsU=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oiH/gH/gy0=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"model": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oiH/gIAQa0=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH/gy0="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": -208,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oiH/gIB+LM=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH/gy0="
},
"font": "Arial;13;1",
"left": 317,
"top": 831,
"width": 68.39599609375,
"height": 13,
"text": "TaskResult"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oiH/gIC0Kg=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH/gy0="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": -208,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oiH/gIDXTQ=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH/gy0="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": -208,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 312,
"top": 824,
"width": 78.39599609375,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oiH/gIAQa0="
},
"nameLabel": {
"$ref": "AAAAAAF+/oiH/gIB+LM="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oiH/gIC0Kg="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oiH/gIDXTQ="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oiH/gIEMgs=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"model": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"font": "Arial;13;0",
"left": 312,
"top": 849,
"width": 78.39599609375,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oiH/wIFnxE=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"model": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"font": "Arial;13;0",
"left": 312,
"top": 859,
"width": 78.39599609375,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oiH/wIGSwE=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"model": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"visible": false,
"font": "Arial;13;0",
"left": -136,
"top": -104,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oiH/wIHbiM=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"model": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"visible": false,
"font": "Arial;13;0",
"left": -136,
"top": -104,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 312,
"top": 824,
"width": 78.39599609375,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/oiH/gH/gy0="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/oiH/gIEMgs="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oiH/wIFnxE="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oiH/wIGSwE="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oiH/wIHbiM="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/oifbQJAE14=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oifbQI+idY="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oifbgJBPQQ=",
"_parent": {
"$ref": "AAAAAAF+/oifbQJAE14="
},
"model": {
"$ref": "AAAAAAF+/oifbQI+idY="
},
"visible": false,
"font": "Arial;13;0",
"left": 365,
"top": 784,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oifbQJAE14="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oifbgJCGBM=",
"_parent": {
"$ref": "AAAAAAF+/oifbQJAE14="
},
"model": {
"$ref": "AAAAAAF+/oifbQI+idY="
},
"visible": null,
"font": "Arial;13;0",
"left": 352,
"top": 776,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/oifbQJAE14="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/oifbgJDOU0=",
"_parent": {
"$ref": "AAAAAAF+/oifbQJAE14="
},
"model": {
"$ref": "AAAAAAF+/oifbQI+idY="
},
"visible": false,
"font": "Arial;13;0",
"left": 390,
"top": 799,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/oifbQJAE14="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/ogt1AGIzZI="
},
"tail": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"lineStyle": 1,
"points": "363:823;393:773",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/oifbgJBPQQ="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/oifbgJCGBM="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oifbgJDOU0="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/oixvAJgiuk=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/oixvAJhWBE=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"model": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/oixvAJiWPo=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJhWBE="
},
"visible": false,
"font": "Arial;13;0",
"left": -608,
"top": -192,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oixvAJjIfo=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJhWBE="
},
"font": "Arial;13;1",
"left": 229,
"top": 758,
"width": 72.0078125,
"height": 13,
"text": "ITaskResult"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oixvAJk6xo=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJhWBE="
},
"visible": false,
"font": "Arial;13;0",
"left": -608,
"top": -192,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/oixvAJlFU0=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJhWBE="
},
"visible": false,
"font": "Arial;13;0",
"left": -608,
"top": -192,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 224,
"top": 751,
"width": 82.0078125,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/oixvAJiWPo="
},
"nameLabel": {
"$ref": "AAAAAAF+/oixvAJjIfo="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/oixvAJk6xo="
},
"propertyLabel": {
"$ref": "AAAAAAF+/oixvAJlFU0="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/oixvAJmej0=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"model": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"visible": false,
"font": "Arial;13;0",
"left": -304,
"top": -96,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/oixvAJnpqQ=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"model": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"visible": false,
"font": "Arial;13;0",
"left": -304,
"top": -96,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/oixvAJontY=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"model": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"visible": false,
"font": "Arial;13;0",
"left": -304,
"top": -96,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/oixvQJpeQY=",
"_parent": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"model": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"visible": false,
"font": "Arial;13;0",
"left": -304,
"top": -96,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 224,
"top": 728,
"width": 82.0078125,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/oixvAJhWBE="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/oixvAJmej0="
},
"operationCompartment": {
"$ref": "AAAAAAF+/oixvAJnpqQ="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/oixvAJontY="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/oixvQJpeQY="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/ojnTwKbVPM=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/ojnTwKa348="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ojnTwKcXyE=",
"_parent": {
"$ref": "AAAAAAF+/ojnTwKbVPM="
},
"model": {
"$ref": "AAAAAAF+/ojnTwKa348="
},
"visible": false,
"font": "Arial;13;0",
"left": 288,
"top": 790,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/ojnTwKbVPM="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ojnTwKd0GA=",
"_parent": {
"$ref": "AAAAAAF+/ojnTwKbVPM="
},
"model": {
"$ref": "AAAAAAF+/ojnTwKa348="
},
"visible": null,
"font": "Arial;13;0",
"left": 276,
"top": 799,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/ojnTwKbVPM="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ojnTwKe1X0=",
"_parent": {
"$ref": "AAAAAAF+/ojnTwKbVPM="
},
"model": {
"$ref": "AAAAAAF+/ojnTwKa348="
},
"visible": false,
"font": "Arial;13;0",
"left": 311,
"top": 771,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/ojnTwKbVPM="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"tail": {
"$ref": "AAAAAAF+/oiH/gH+hsU="
},
"lineStyle": 1,
"points": "329:823;271.2631578947368:751",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/ojnTwKcXyE="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/ojnTwKd0GA="
},
"propertyLabel": {
"$ref": "AAAAAAF+/ojnTwKe1X0="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/onnsAPk0AI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/onnrwPgaSQ="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPlgvA=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPgaSQ="
},
"font": "Arial;13;0",
"left": 273,
"top": 588,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"edgePosition": 1,
"text": "+1"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPmaGY=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPgaSQ="
},
"visible": null,
"font": "Arial;13;0",
"left": 295,
"top": 588,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPn39Y=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPgaSQ="
},
"visible": false,
"font": "Arial;13;0",
"left": 251,
"top": 587,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPosNE=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPhtDg="
},
"visible": false,
"font": "Arial;13;0",
"left": 282,
"top": 481,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPpkI4=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPhtDg="
},
"visible": false,
"font": "Arial;13;0",
"left": 295,
"top": 483,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPqxqU=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPhtDg="
},
"visible": false,
"font": "Arial;13;0",
"left": 255,
"top": 476,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPrEOs=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnsAPiR/4="
},
"visible": false,
"font": "Arial;13;0",
"left": 279,
"top": 695,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPs8R8=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnsAPiR/4="
},
"visible": false,
"font": "Arial;13;0",
"left": 292,
"top": 693,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/onnsAPtY94=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnsAPiR/4="
},
"visible": false,
"font": "Arial;13;0",
"left": 251,
"top": 699,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/onnsAPk0AI="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/onnsAPuqss=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnrwPhtDg="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/onnsAPvb5w=",
"_parent": {
"$ref": "AAAAAAF+/onnsAPk0AI="
},
"model": {
"$ref": "AAAAAAF+/onnsAPiR/4="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oixvAJgiuk="
},
"tail": {
"$ref": "AAAAAAF+/oB4+v237OY="
},
"lineStyle": 1,
"points": "268:461;264:727",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/onnsAPlgvA="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/onnsAPmaGY="
},
"propertyLabel": {
"$ref": "AAAAAAF+/onnsAPn39Y="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/onnsAPosNE="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/onnsAPpkI4="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/onnsAPqxqU="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/onnsAPrEOs="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/onnsAPs8R8="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/onnsAPtY94="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/onnsAPuqss="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/onnsAPvb5w="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/ooh1ARuIJ4=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRqA8w="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1ARv8h8=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRqA8w="
},
"font": "Arial;13;0",
"left": 535,
"top": 568,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"edgePosition": 1,
"text": "+1"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1ARwubs=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRqA8w="
},
"visible": null,
"font": "Arial;13;0",
"left": 550,
"top": 581,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1ARxVrg=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRqA8w="
},
"visible": false,
"font": "Arial;13;0",
"left": 527,
"top": 543,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1ARydW0=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRr2tM="
},
"visible": false,
"font": "Arial;13;0",
"left": 607,
"top": 529,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1ARzp6U=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRr2tM="
},
"visible": false,
"font": "Arial;13;0",
"left": 612,
"top": 541,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1AR0x84=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRr2tM="
},
"visible": false,
"font": "Arial;13;0",
"left": 597,
"top": 503,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1AR1Dz8=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRssuw="
},
"visible": false,
"font": "Arial;13;0",
"left": 478,
"top": 608,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1AR2/wM=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRssuw="
},
"visible": false,
"font": "Arial;13;0",
"left": 487,
"top": 618,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/ooh1AR3BHU=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRssuw="
},
"visible": false,
"font": "Arial;13;0",
"left": 460,
"top": 587,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/ooh1AR4ocw=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRr2tM="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/ooh1AR5c+I=",
"_parent": {
"$ref": "AAAAAAF+/ooh1ARuIJ4="
},
"model": {
"$ref": "AAAAAAF+/ooh0wRssuw="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/ofZUwElJVk="
},
"tail": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"lineStyle": 1,
"points": "622:509;449:615",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/ooh1ARv8h8="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/ooh1ARwubs="
},
"propertyLabel": {
"$ref": "AAAAAAF+/ooh1ARxVrg="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/ooh1ARydW0="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/ooh1ARzp6U="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/ooh1AR0x84="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/ooh1AR1Dz8="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/ooh1AR2/wM="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/ooh1AR3BHU="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/ooh1AR4ocw="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/ooh1AR5c+I="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/osTOwbHp3s=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/osTOwbIwu0=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbHp3s="
},
"model": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/osTOwbJsZI=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbIwu0="
},
"visible": false,
"font": "Arial;13;0",
"top": -16,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/osTOwbK91w=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbIwu0="
},
"font": "Arial;13;1",
"left": 453,
"top": 439,
"width": 87.4072265625,
"height": 13,
"text": "MapDataStore"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/osTOwbLwpk=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbIwu0="
},
"visible": false,
"font": "Arial;13;0",
"top": -16,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/osTOwbMYic=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbIwu0="
},
"visible": false,
"font": "Arial;13;0",
"top": -16,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 448,
"top": 432,
"width": 97.4072265625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/osTOwbJsZI="
},
"nameLabel": {
"$ref": "AAAAAAF+/osTOwbK91w="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/osTOwbLwpk="
},
"propertyLabel": {
"$ref": "AAAAAAF+/osTOwbMYic="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/osTOwbNLLs=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbHp3s="
},
"model": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"font": "Arial;13;0",
"left": 448,
"top": 457,
"width": 97.4072265625,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/osTPAbOk1w=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbHp3s="
},
"model": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"font": "Arial;13;0",
"left": 448,
"top": 467,
"width": 97.4072265625,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/osTPAbPctc=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbHp3s="
},
"model": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"visible": false,
"font": "Arial;13;0",
"top": -8,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/osTPAbQwxM=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbHp3s="
},
"model": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"visible": false,
"font": "Arial;13;0",
"top": -8,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 448,
"top": 432,
"width": 97.4072265625,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/osTOwbIwu0="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/osTOwbNLLs="
},
"operationCompartment": {
"$ref": "AAAAAAF+/osTPAbOk1w="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/osTPAbPctc="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/osTPAbQwxM="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/otLdgdLI8c=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/otLdgdKMcA="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/otLdgdMgew=",
"_parent": {
"$ref": "AAAAAAF+/otLdgdLI8c="
},
"model": {
"$ref": "AAAAAAF+/otLdgdKMcA="
},
"visible": false,
"font": "Arial;13;0",
"left": 453,
"top": 495,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/otLdgdLI8c="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/otLdgdNRAU=",
"_parent": {
"$ref": "AAAAAAF+/otLdgdLI8c="
},
"model": {
"$ref": "AAAAAAF+/otLdgdKMcA="
},
"visible": null,
"font": "Arial;13;0",
"left": 461,
"top": 508,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/otLdgdLI8c="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/otLdgdObK0=",
"_parent": {
"$ref": "AAAAAAF+/otLdgdLI8c="
},
"model": {
"$ref": "AAAAAAF+/otLdgdKMcA="
},
"visible": false,
"font": "Arial;13;0",
"left": 436,
"top": 470,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/otLdgdLI8c="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oejFwDmxXI="
},
"tail": {
"$ref": "AAAAAAF+/osTOwbHp3s="
},
"lineStyle": 1,
"points": "467:477;424.509521484375:502.5447013608871",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/otLdgdMgew="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/otLdgdNRAU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/otLdgdObK0="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/ovrcwgK9Oc=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/ovrcwgLg8M=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"model": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/ovrcwgMBn8=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgLg8M="
},
"visible": false,
"font": "Arial;13;0",
"left": 96,
"top": -64,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ovrcwgN/vY=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgLg8M="
},
"font": "Arial;13;1",
"left": 909,
"top": 471,
"width": 148.56689453125,
"height": 13,
"text": "AbstractGroupTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ovrcwgOotk=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgLg8M="
},
"visible": false,
"font": "Arial;13;0",
"left": 96,
"top": -64,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/ovrcwgPbno=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgLg8M="
},
"visible": false,
"font": "Arial;13;0",
"left": 96,
"top": -64,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 904,
"top": 464,
"width": 158.56689453125,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/ovrcwgMBn8="
},
"nameLabel": {
"$ref": "AAAAAAF+/ovrcwgN/vY="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/ovrcwgOotk="
},
"propertyLabel": {
"$ref": "AAAAAAF+/ovrcwgPbno="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/ovrcwgQ0Fc=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"model": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"font": "Arial;13;0",
"left": 904,
"top": 489,
"width": 158.56689453125,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/ovrcwgR5vs=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"model": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"font": "Arial;13;0",
"left": 904,
"top": 499,
"width": 158.56689453125,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/ovrcwgSzjA=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"model": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"visible": false,
"font": "Arial;13;0",
"left": 48,
"top": -32,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/ovrcwgTBI8=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"model": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"visible": false,
"font": "Arial;13;0",
"left": 48,
"top": -32,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 904,
"top": 464,
"width": 158.56689453125,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/ovrcwgLg8M="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/ovrcwgQ0Fc="
},
"operationCompartment": {
"$ref": "AAAAAAF+/ovrcwgR5vs="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/ovrcwgSzjA="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/ovrcwgTBI8="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/owCvQhqKbI=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/owCvQholDw="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/owCvQhrLPs=",
"_parent": {
"$ref": "AAAAAAF+/owCvQhqKbI="
},
"model": {
"$ref": "AAAAAAF+/owCvQholDw="
},
"visible": false,
"font": "Arial;13;0",
"left": 810,
"top": 495,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/owCvQhqKbI="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/owCvQhsrOs=",
"_parent": {
"$ref": "AAAAAAF+/owCvQhqKbI="
},
"model": {
"$ref": "AAAAAAF+/owCvQholDw="
},
"visible": null,
"font": "Arial;13;0",
"left": 810,
"top": 510,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/owCvQhqKbI="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/owCvQhtfJg=",
"_parent": {
"$ref": "AAAAAAF+/owCvQhqKbI="
},
"model": {
"$ref": "AAAAAAF+/owCvQholDw="
},
"visible": false,
"font": "Arial;13;0",
"left": 811,
"top": 465,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/owCvQhqKbI="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"tail": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"lineStyle": 1,
"points": "903:486;720:486",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/owCvQhrLPs="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/owCvQhsrOs="
},
"propertyLabel": {
"$ref": "AAAAAAF+/owCvQhtfJg="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/o4/zwrhcRw=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/o4/zwriyQA=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwrhcRw="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/o4/zwrj31E=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwriyQA="
},
"visible": false,
"font": "Arial;13;0",
"left": 664,
"top": -697,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o4/zwrk9BA=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwriyQA="
},
"font": "Arial;13;1",
"left": 893,
"top": 334,
"width": 116.09228515625,
"height": 13,
"text": "ITaskStepLifecycle"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o4/zwrlgyM=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwriyQA="
},
"visible": false,
"font": "Arial;13;0",
"left": 664,
"top": -697,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o4/zwrmzCY=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwriyQA="
},
"visible": false,
"font": "Arial;13;0",
"left": 664,
"top": -697,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 888,
"top": 327,
"width": 126.09228515625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/o4/zwrj31E="
},
"nameLabel": {
"$ref": "AAAAAAF+/o4/zwrk9BA="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/o4/zwrlgyM="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o4/zwrmzCY="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/o4/zwrnZ+0=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwrhcRw="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": 736,
"top": -232,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/o4/zwroZwQ=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwrhcRw="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": 736,
"top": -232,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/o4/zwrpyI4=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwrhcRw="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": 736,
"top": -232,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/o4/zwrqGyE=",
"_parent": {
"$ref": "AAAAAAF+/o4/zwrhcRw="
},
"model": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
},
"visible": false,
"font": "Arial;13;0",
"left": 736,
"top": -232,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 888,
"top": 304,
"width": 126.09228515625,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/o4/zwriyQA="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/o4/zwrnZ+0="
},
"operationCompartment": {
"$ref": "AAAAAAF+/o4/zwroZwQ="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/o4/zwrpyI4="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/o4/zwrqGyE="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/o5UWQtsemA=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/o5UWQttdC0=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQtsemA="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/o5UWgtuMro=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQttdC0="
},
"visible": false,
"font": "Arial;13;0",
"left": 485,
"top": -769,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o5UWgtv9H0=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQttdC0="
},
"font": "Arial;13;1",
"left": 989,
"top": 350,
"width": 99.4423828125,
"height": 13,
"text": "IGroupTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o5UWgtwDv0=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQttdC0="
},
"visible": false,
"font": "Arial;13;0",
"left": 485,
"top": -769,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o5UWgtxFqQ=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQttdC0="
},
"visible": false,
"font": "Arial;13;0",
"left": 485,
"top": -769,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 984,
"top": 343,
"width": 109.4423828125,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/o5UWgtuMro="
},
"nameLabel": {
"$ref": "AAAAAAF+/o5UWgtv9H0="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/o5UWgtwDv0="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o5UWgtxFqQ="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/o5UWgtyCAw=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQtsemA="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": 520,
"top": -256,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/o5UWgtzqbw=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQtsemA="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": 520,
"top": -256,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/o5UWgt0amQ=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQtsemA="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": 520,
"top": -256,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/o5UWgt1wqg=",
"_parent": {
"$ref": "AAAAAAF+/o5UWQtsemA="
},
"model": {
"$ref": "AAAAAAF+/oFcZf43uUM="
},
"visible": false,
"font": "Arial;13;0",
"left": 520,
"top": -256,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 984,
"top": 320,
"width": 109.4423828125,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/o5UWQttdC0="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/o5UWgtyCAw="
},
"operationCompartment": {
"$ref": "AAAAAAF+/o5UWgtzqbw="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/o5UWgt0amQ="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/o5UWgt1wqg="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/o6Ykwu96Uc=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o6Ykwu8maw="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o6Ykwu+Iys=",
"_parent": {
"$ref": "AAAAAAF+/o6Ykwu96Uc="
},
"model": {
"$ref": "AAAAAAF+/o6Ykwu8maw="
},
"visible": false,
"font": "Arial;13;0",
"left": 997,
"top": 391,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o6Ykwu96Uc="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o6Ykwu//aU=",
"_parent": {
"$ref": "AAAAAAF+/o6Ykwu96Uc="
},
"model": {
"$ref": "AAAAAAF+/o6Ykwu8maw="
},
"visible": null,
"font": "Arial;13;0",
"left": 983,
"top": 386,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/o6Ykwu96Uc="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o6YkwvAtHQ=",
"_parent": {
"$ref": "AAAAAAF+/o6Ykwu96Uc="
},
"model": {
"$ref": "AAAAAAF+/o6Ykwu8maw="
},
"visible": false,
"font": "Arial;13;0",
"left": 1026,
"top": 402,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o6Ykwu96Uc="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/o5UWQtsemA="
},
"tail": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"lineStyle": 1,
"points": "991:463;1034.842105263158:343",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/o6Ykwu+Iys="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/o6Ykwu//aU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o6YkwvAtHQ="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/o6nTQvpgzs=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o6nTQvoIC4="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o6nTQvqKb0=",
"_parent": {
"$ref": "AAAAAAF+/o6nTQvpgzs="
},
"model": {
"$ref": "AAAAAAF+/o6nTQvoIC4="
},
"visible": false,
"font": "Arial;13;0",
"left": 949,
"top": 391,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o6nTQvpgzs="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o6nTQvr5g0=",
"_parent": {
"$ref": "AAAAAAF+/o6nTQvpgzs="
},
"model": {
"$ref": "AAAAAAF+/o6nTQvoIC4="
},
"visible": null,
"font": "Arial;13;0",
"left": 934,
"top": 394,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/o6nTQvpgzs="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o6nTQvsx6s=",
"_parent": {
"$ref": "AAAAAAF+/o6nTQvpgzs="
},
"model": {
"$ref": "AAAAAAF+/o6nTQvoIC4="
},
"visible": false,
"font": "Arial;13;0",
"left": 978,
"top": 386,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o6nTQvpgzs="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/o4/zwrhcRw="
},
"tail": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"lineStyle": 1,
"points": "977:463;951.578947368421:327",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/o6nTQvqKb0="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/o6nTQvr5g0="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o6nTQvsx6s="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/o7UzwxNC+g=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/o7UzwxOHNE=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxNC+g="
},
"model": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/o7UzwxPRgk=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxOHNE="
},
"visible": false,
"font": "Arial;13;0",
"left": 224,
"top": 48,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o7UzwxQuaE=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxOHNE="
},
"font": "Arial;13;1",
"left": 1061,
"top": 615,
"width": 165.8896484375,
"height": 13,
"text": "ConcurrentGroupTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o7UzwxRt/I=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxOHNE="
},
"visible": false,
"font": "Arial;13;0",
"left": 224,
"top": 48,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o7UzwxSMfI=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxOHNE="
},
"visible": false,
"font": "Arial;13;0",
"left": 224,
"top": 48,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 1056,
"top": 608,
"width": 175.8896484375,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/o7UzwxPRgk="
},
"nameLabel": {
"$ref": "AAAAAAF+/o7UzwxQuaE="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/o7UzwxRt/I="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o7UzwxSMfI="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/o7UzwxTjwc=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxNC+g="
},
"model": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"font": "Arial;13;0",
"left": 1056,
"top": 633,
"width": 175.8896484375,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/o7UzwxUoq8=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxNC+g="
},
"model": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"font": "Arial;13;0",
"left": 1056,
"top": 643,
"width": 175.8896484375,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/o7UzwxVe3A=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxNC+g="
},
"model": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"visible": false,
"font": "Arial;13;0",
"left": 112,
"top": 24,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/o7UzwxWQ1g=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxNC+g="
},
"model": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"visible": false,
"font": "Arial;13;0",
"left": 112,
"top": 24,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 1056,
"top": 608,
"width": 175.8896484375,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/o7UzwxOHNE="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/o7UzwxTjwc="
},
"operationCompartment": {
"$ref": "AAAAAAF+/o7UzwxUoq8="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/o7UzwxVe3A="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/o7UzwxWQ1g="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/o7qpQytXQ0=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o7qpQyr9Lw="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o7qpgyuVwo=",
"_parent": {
"$ref": "AAAAAAF+/o7qpQytXQ0="
},
"model": {
"$ref": "AAAAAAF+/o7qpQyr9Lw="
},
"visible": false,
"font": "Arial;13;0",
"left": 1051,
"top": 563,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o7qpQytXQ0="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o7qpgyvEx8=",
"_parent": {
"$ref": "AAAAAAF+/o7qpQytXQ0="
},
"model": {
"$ref": "AAAAAAF+/o7qpQyr9Lw="
},
"visible": null,
"font": "Arial;13;0",
"left": 1041,
"top": 574,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/o7qpQytXQ0="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o7qpgywI2k=",
"_parent": {
"$ref": "AAAAAAF+/o7qpQytXQ0="
},
"model": {
"$ref": "AAAAAAF+/o7qpQyr9Lw="
},
"visible": false,
"font": "Arial;13;0",
"left": 1072,
"top": 540,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o7qpQytXQ0="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"tail": {
"$ref": "AAAAAAF+/o7UzwxNC+g="
},
"lineStyle": 1,
"points": "1117:607;1008:509",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/o7qpgyuVwo="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/o7qpgyvEx8="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o7qpgywI2k="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/o8e9g0QghE=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/o8e9g0RT/k=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0QghE="
},
"model": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/o8e9w0SMpo=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0RT/k="
},
"visible": false,
"font": "Arial;13;0",
"left": -576,
"top": -48,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o8e9w0Tq40=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0RT/k="
},
"font": "Arial;13;1",
"left": 869,
"top": 615,
"width": 131.244140625,
"height": 13,
"text": "SerialGroupTaskStep"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o8e9w0Uy4E=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0RT/k="
},
"visible": false,
"font": "Arial;13;0",
"left": -576,
"top": -48,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o8e9w0VKjs=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0RT/k="
},
"visible": false,
"font": "Arial;13;0",
"left": -576,
"top": -48,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 864,
"top": 608,
"width": 141.244140625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/o8e9w0SMpo="
},
"nameLabel": {
"$ref": "AAAAAAF+/o8e9w0Tq40="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/o8e9w0Uy4E="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o8e9w0VKjs="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/o8e9w0WwuU=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0QghE="
},
"model": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"font": "Arial;13;0",
"left": 864,
"top": 633,
"width": 141.244140625,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/o8e9w0Xu1Q=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0QghE="
},
"model": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"font": "Arial;13;0",
"left": 864,
"top": 643,
"width": 141.244140625,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/o8e9w0YdOM=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0QghE="
},
"model": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"top": -24,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/o8e9w0Z4C8=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0QghE="
},
"model": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"top": -24,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 864,
"top": 608,
"width": 141.244140625,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/o8e9g0RT/k="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/o8e9w0WwuU="
},
"operationCompartment": {
"$ref": "AAAAAAF+/o8e9w0Xu1Q="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/o8e9w0YdOM="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/o8e9w0Z4C8="
}
},
{
"_type": "UMLGeneralizationView",
"_id": "AAAAAAF+/o9+Jw6qyhU=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o9+Jw6o42Q="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o9+Jw6rmuc=",
"_parent": {
"$ref": "AAAAAAF+/o9+Jw6qyhU="
},
"model": {
"$ref": "AAAAAAF+/o9+Jw6o42Q="
},
"visible": false,
"font": "Arial;13;0",
"left": 943,
"top": 547,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o9+Jw6qyhU="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o9+Jw6sC4E=",
"_parent": {
"$ref": "AAAAAAF+/o9+Jw6qyhU="
},
"model": {
"$ref": "AAAAAAF+/o9+Jw6o42Q="
},
"visible": null,
"font": "Arial;13;0",
"left": 929,
"top": 542,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/o9+Jw6qyhU="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/o9+Jw6tryI=",
"_parent": {
"$ref": "AAAAAAF+/o9+Jw6qyhU="
},
"model": {
"$ref": "AAAAAAF+/o9+Jw6o42Q="
},
"visible": false,
"font": "Arial;13;0",
"left": 972,
"top": 556,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/o9+Jw6qyhU="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"tail": {
"$ref": "AAAAAAF+/o8e9g0QghE="
},
"lineStyle": 1,
"points": "942:607;974:509",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/o9+Jw6rmuc="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/o9+Jw6sC4E="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o9+Jw6tryI="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/o/u+g7oUTw=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/o/u+g7py1s=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7oUTw="
},
"model": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/o/u+g7qxd8=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7py1s="
},
"visible": false,
"font": "Arial;13;0",
"left": -544,
"top": 80,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o/u+w7rCKk=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7py1s="
},
"font": "Arial;13;1",
"left": 493,
"top": 367,
"width": 72.71875,
"height": 13,
"text": "ThreadType"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o/u+w7sYYU=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7py1s="
},
"visible": false,
"font": "Arial;13;0",
"left": -544,
"top": 80,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/o/u+w7tcsc=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7py1s="
},
"visible": false,
"font": "Arial;13;0",
"left": -544,
"top": 80,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 488,
"top": 360,
"width": 82.71875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/o/u+g7qxd8="
},
"nameLabel": {
"$ref": "AAAAAAF+/o/u+w7rCKk="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/o/u+w7sYYU="
},
"propertyLabel": {
"$ref": "AAAAAAF+/o/u+w7tcsc="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/o/u+w7uHgA=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7oUTw="
},
"model": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"font": "Arial;13;0",
"left": 488,
"top": 385,
"width": 82.71875,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/o/u+w7vxVY=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7oUTw="
},
"model": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"font": "Arial;13;0",
"left": 488,
"top": 395,
"width": 82.71875,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/o/u+w7whHk=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7oUTw="
},
"model": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": 40,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/o/u+w7xU1s=",
"_parent": {
"$ref": "AAAAAAF+/o/u+g7oUTw="
},
"model": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": 40,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 488,
"top": 360,
"width": 82.71875,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/o/u+g7py1s="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/o/u+w7uHgA="
},
"operationCompartment": {
"$ref": "AAAAAAF+/o/u+w7vxVY="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/o/u+w7whHk="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/o/u+w7xU1s="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/pAqzw+mGA4=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+iYqM="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAqzw+naqM=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+iYqM="
},
"font": "Arial;13;0",
"left": 576,
"top": 439,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"edgePosition": 1,
"text": "+1"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+oq/E=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+iYqM="
},
"visible": null,
"font": "Arial;13;0",
"left": 574,
"top": 451,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+p/Sw=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+iYqM="
},
"visible": false,
"font": "Arial;13;0",
"left": 602,
"top": 416,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+qj0g=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+jasg="
},
"visible": false,
"font": "Arial;13;0",
"left": 600,
"top": 452,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+rqhQ=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+jasg="
},
"visible": false,
"font": "Arial;13;0",
"left": 590,
"top": 461,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+sGfQ=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+jasg="
},
"visible": false,
"font": "Arial;13;0",
"left": 620,
"top": 433,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+tHeo=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+kIaI="
},
"visible": false,
"font": "Arial;13;0",
"left": 568,
"top": 426,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+uleM=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+kIaI="
},
"visible": false,
"font": "Arial;13;0",
"left": 561,
"top": 438,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pAq0A+vpmE=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+kIaI="
},
"visible": false,
"font": "Arial;13;0",
"left": 581,
"top": 402,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pAq0A+wvJw=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+jasg="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pAq0A+xA80=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+mGA4="
},
"model": {
"$ref": "AAAAAAF+/pAqzw+kIaI="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/o/u+g7oUTw="
},
"tail": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"lineStyle": 1,
"points": "630:463;557:405",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/pAqzw+naqM="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/pAq0A+oq/E="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pAq0A+p/Sw="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/pAq0A+qj0g="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/pAq0A+rqhQ="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/pAq0A+sGfQ="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/pAq0A+tHeo="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/pAq0A+uleM="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/pAq0A+vpmE="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/pAq0A+wvJw="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/pAq0A+xA80="
}
},
{
"_type": "UMLClassView",
"_id": "AAAAAAF+/pFMLxBEWe0=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/pFMLxBFMek=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"model": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/pFMLxBGxco=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBFMek="
},
"visible": false,
"font": "Arial;13;0",
"left": -112,
"top": -112,
"height": 13
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pFMLxBHMlg=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBFMek="
},
"font": "Arial;13;1",
"left": 573,
"top": 735,
"width": 92.22509765625,
"height": 13,
"text": "TaskCommand"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pFMLxBIlzw=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBFMek="
},
"visible": false,
"font": "Arial;13;0",
"left": -112,
"top": -112,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pFMLxBJ1KE=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBFMek="
},
"visible": false,
"font": "Arial;13;0",
"left": -112,
"top": -112,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 568,
"top": 728,
"width": 102.22509765625,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/pFMLxBGxco="
},
"nameLabel": {
"$ref": "AAAAAAF+/pFMLxBHMlg="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/pFMLxBIlzw="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pFMLxBJ1KE="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/pFMLxBKQac=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"model": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"font": "Arial;13;0",
"left": 568,
"top": 753,
"width": 102.22509765625,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/pFMLxBLX6U=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"model": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"font": "Arial;13;0",
"left": 568,
"top": 763,
"width": 102.22509765625,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/pFMLxBM25E=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"model": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"visible": false,
"font": "Arial;13;0",
"left": -56,
"top": -56,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/pFMLxBNLOw=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"model": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"visible": false,
"font": "Arial;13;0",
"left": -56,
"top": -56,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 568,
"top": 728,
"width": 102.22509765625,
"height": 45,
"nameCompartment": {
"$ref": "AAAAAAF+/pFMLxBFMek="
},
"attributeCompartment": {
"$ref": "AAAAAAF+/pFMLxBKQac="
},
"operationCompartment": {
"$ref": "AAAAAAF+/pFMLxBLX6U="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/pFMLxBM25E="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/pFMLxBNLOw="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/pI9SxI7C0Q=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/pI9SxI8Grc=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI7C0Q="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/pI9SxI92k4=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI8Grc="
},
"visible": false,
"font": "Arial;13;0",
"left": -609,
"top": 992,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pI9SxI+A44=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI8Grc="
},
"font": "Arial;13;1",
"left": 517,
"top": 870,
"width": 122.560546875,
"height": 13,
"text": "ITaskStepController"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pI9SxI/t/M=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI8Grc="
},
"visible": false,
"font": "Arial;13;0",
"left": -609,
"top": 992,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pI9SxJApCA=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI8Grc="
},
"visible": false,
"font": "Arial;13;0",
"left": -609,
"top": 992,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 512,
"top": 863,
"width": 132.560546875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/pI9SxI92k4="
},
"nameLabel": {
"$ref": "AAAAAAF+/pI9SxI+A44="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/pI9SxI/t/M="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pI9SxJApCA="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/pI9SxJBDXA=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI7C0Q="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"top": 760,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/pI9SxJC5q8=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI7C0Q="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"top": 760,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/pI9SxJD3FE=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI7C0Q="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"top": 760,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/pI9SxJEKfk=",
"_parent": {
"$ref": "AAAAAAF+/pI9SxI7C0Q="
},
"model": {
"$ref": "AAAAAAF+/oQybv9etN0="
},
"visible": false,
"font": "Arial;13;0",
"left": -288,
"top": 760,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 512,
"top": 840,
"width": 132.560546875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/pI9SxI8Grc="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/pI9SxJBDXA="
},
"operationCompartment": {
"$ref": "AAAAAAF+/pI9SxJC5q8="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/pI9SxJD3FE="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/pI9SxJEKfk="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/pJcFxKbGgQ=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pJcFxKaHaA="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pJcFxKcKec=",
"_parent": {
"$ref": "AAAAAAF+/pJcFxKbGgQ="
},
"model": {
"$ref": "AAAAAAF+/pJcFxKaHaA="
},
"visible": false,
"font": "Arial;13;0",
"left": 611,
"top": 804,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pJcFxKbGgQ="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pJcFxKdRq4=",
"_parent": {
"$ref": "AAAAAAF+/pJcFxKbGgQ="
},
"model": {
"$ref": "AAAAAAF+/pJcFxKaHaA="
},
"visible": null,
"font": "Arial;13;0",
"left": 625,
"top": 809,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pJcFxKbGgQ="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pJcFxKeQGA=",
"_parent": {
"$ref": "AAAAAAF+/pJcFxKbGgQ="
},
"model": {
"$ref": "AAAAAAF+/pJcFxKaHaA="
},
"visible": false,
"font": "Arial;13;0",
"left": 582,
"top": 795,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pJcFxKbGgQ="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/pI9SxI7C0Q="
},
"tail": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"lineStyle": 1,
"points": "610:773;585.25:840",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/pJcFxKcKec="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/pJcFxKdRq4="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pJcFxKeQGA="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/pKIDRMuTmA=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/pKIDRMvLMo=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMuTmA="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/pKIDRMwZ9U=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMvLMo="
},
"visible": false,
"font": "Arial;13;0",
"left": -447,
"top": 719,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pKIDRMxjQ4=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMvLMo="
},
"font": "Arial;13;1",
"left": 661,
"top": 862,
"width": 61.419921875,
"height": 13,
"text": "Runnable"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pKIDRMynTY=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMvLMo="
},
"visible": false,
"font": "Arial;13;0",
"left": -447,
"top": 719,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pKIDRMzSqY=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMvLMo="
},
"visible": false,
"font": "Arial;13;0",
"left": -447,
"top": 719,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 656,
"top": 855,
"width": 71.419921875,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/pKIDRMwZ9U="
},
"nameLabel": {
"$ref": "AAAAAAF+/pKIDRMxjQ4="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/pKIDRMynTY="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pKIDRMzSqY="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/pKIDRM0wt8=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMuTmA="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": 760,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/pKIDRM1RGE=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMuTmA="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": 760,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/pKIDRM2ua8=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMuTmA="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": 760,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/pKIDRM33VI=",
"_parent": {
"$ref": "AAAAAAF+/pKIDRMuTmA="
},
"model": {
"$ref": "AAAAAAF+/oSvGv+bods="
},
"visible": false,
"font": "Arial;13;0",
"left": -272,
"top": 760,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 656,
"top": 832,
"width": 71.419921875,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/pKIDRMvLMo="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/pKIDRM0wt8="
},
"operationCompartment": {
"$ref": "AAAAAAF+/pKIDRM1RGE="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/pKIDRM2ua8="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/pKIDRM33VI="
}
},
{
"_type": "UMLInterfaceRealizationView",
"_id": "AAAAAAF+/pKyUhOOsOc=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pKyUhON0V8="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pKyUhOP4WE=",
"_parent": {
"$ref": "AAAAAAF+/pKyUhOOsOc="
},
"model": {
"$ref": "AAAAAAF+/pKyUhON0V8="
},
"visible": false,
"font": "Arial;13;0",
"left": 667,
"top": 788,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pKyUhOOsOc="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pKyUhOQLrY=",
"_parent": {
"$ref": "AAAAAAF+/pKyUhOOsOc="
},
"model": {
"$ref": "AAAAAAF+/pKyUhON0V8="
},
"visible": null,
"font": "Arial;13;0",
"left": 679,
"top": 779,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pKyUhOOsOc="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pKyUhORW7M=",
"_parent": {
"$ref": "AAAAAAF+/pKyUhOOsOc="
},
"model": {
"$ref": "AAAAAAF+/pKyUhON0V8="
},
"visible": false,
"font": "Arial;13;0",
"left": 644,
"top": 807,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pKyUhOOsOc="
},
"edgePosition": 1
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/pKIDRMuTmA="
},
"tail": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"lineStyle": 1,
"points": "634:773;679.7099609375:835.0305606617648",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/pKyUhOP4WE="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/pKyUhOQLrY="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pKyUhORW7M="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/pLJ+RPe6eM=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPaK9w="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPfCAA=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPaK9w="
},
"font": "Arial;13;0",
"left": 626,
"top": 687,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"edgePosition": 1,
"text": "+1"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPgsAs=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPaK9w="
},
"visible": null,
"font": "Arial;13;0",
"left": 648,
"top": 687,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPhcZc=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPaK9w="
},
"visible": false,
"font": "Arial;13;0",
"left": 604,
"top": 688,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPioYk=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPbvO0="
},
"visible": false,
"font": "Arial;13;0",
"left": 634,
"top": 681,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPjMAE=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPbvO0="
},
"visible": false,
"font": "Arial;13;0",
"left": 647,
"top": 683,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPkpOo=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPbvO0="
},
"visible": false,
"font": "Arial;13;0",
"left": 607,
"top": 676,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPlFtU=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPc6fM="
},
"visible": false,
"font": "Arial;13;0",
"left": 634,
"top": 695,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPm/gg=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPc6fM="
},
"visible": false,
"font": "Arial;13;0",
"left": 647,
"top": 693,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pLJ+RPn/X0=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPc6fM="
},
"visible": false,
"font": "Arial;13;0",
"left": 606,
"top": 699,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pLJ+RPoWtk=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPbvO0="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pLJ+hPpDQ0=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPe6eM="
},
"model": {
"$ref": "AAAAAAF+/pLJ+RPc6fM="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/pFMLxBEWe0="
},
"tail": {
"$ref": "AAAAAAF+/oYK4wBDOcU="
},
"lineStyle": 1,
"points": "620:661;619:727",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/pLJ+RPfCAA="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/pLJ+RPgsAs="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pLJ+RPhcZc="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/pLJ+RPioYk="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/pLJ+RPjMAE="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/pLJ+RPkpOo="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/pLJ+RPlFtU="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/pLJ+RPm/gg="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/pLJ+RPn/X0="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/pLJ+RPoWtk="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/pLJ+hPpDQ0="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/pPGrhb+Ang=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb6+Zs="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhb/u9U=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb6+Zs="
},
"font": "Arial;13;0",
"left": 811,
"top": 336,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"edgePosition": 1,
"text": "+n"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcAZs0=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb6+Zs="
},
"visible": null,
"font": "Arial;13;0",
"left": 819,
"top": 351,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcBlWk=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb6+Zs="
},
"visible": false,
"font": "Arial;13;0",
"left": 817,
"top": 307,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcCOwk=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb7cpA="
},
"visible": false,
"font": "Arial;13;0",
"left": 928,
"top": 449,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcDV1A=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb7cpA="
},
"visible": false,
"font": "Arial;13;0",
"left": 918,
"top": 457,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcELwM=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb7cpA="
},
"visible": false,
"font": "Arial;13;0",
"left": 951,
"top": 432,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcFg5E=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb8i/o="
},
"visible": false,
"font": "Arial;13;0",
"left": 721,
"top": 341,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcGQ7s=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb8i/o="
},
"visible": false,
"font": "Arial;13;0",
"left": 724,
"top": 354,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pPGrhcHWDo=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb8i/o="
},
"visible": false,
"font": "Arial;13;0",
"left": 716,
"top": 314,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pPGrhcIZo4=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb7cpA="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pPGrhcJVBQ=",
"_parent": {
"$ref": "AAAAAAF+/pPGrhb+Ang="
},
"model": {
"$ref": "AAAAAAF+/pPGrRb8i/o="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/oMJbv7mDqg="
},
"tail": {
"$ref": "AAAAAAF+/ovrcwgK9Oc="
},
"lineStyle": 1,
"points": "958:463;818:328;695:334",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/pPGrhb/u9U="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/pPGrhcAZs0="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pPGrhcBlWk="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/pPGrhcCOwk="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/pPGrhcDV1A="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/pPGrhcELwM="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/pPGrhcFg5E="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/pPGrhcGQ7s="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/pPGrhcHWDo="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/pPGrhcIZo4="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/pPGrhcJVBQ="
}
},
{
"_type": "UMLInterfaceView",
"_id": "AAAAAAF+/pWxQBwZ9SA=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"subViews": [
{
"_type": "UMLNameCompartmentView",
"_id": "AAAAAAF+/pWxQBwaXH8=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwZ9SA="
},
"model": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"subViews": [
{
"_type": "LabelView",
"_id": "AAAAAAF+/pWxQBwbmEs=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwaXH8="
},
"visible": false,
"font": "Arial;13;0",
"top": -80,
"width": 64.32080078125,
"height": 13,
"text": "«interface»"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pWxQBwc+uA=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwaXH8="
},
"font": "Arial;13;1",
"left": 733,
"top": 398,
"width": 108.849609375,
"height": 13,
"text": "ITaskStepHandler"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pWxQBwdVFw=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwaXH8="
},
"visible": false,
"font": "Arial;13;0",
"top": -80,
"width": 73.67724609375,
"height": 13,
"text": "(from Model)"
},
{
"_type": "LabelView",
"_id": "AAAAAAF+/pWxQBweDKk=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwaXH8="
},
"visible": false,
"font": "Arial;13;0",
"top": -80,
"height": 13,
"horizontalAlignment": 1
}
],
"font": "Arial;13;0",
"left": 728,
"top": 391,
"width": 118.849609375,
"height": 25,
"stereotypeLabel": {
"$ref": "AAAAAAF+/pWxQBwbmEs="
},
"nameLabel": {
"$ref": "AAAAAAF+/pWxQBwc+uA="
},
"namespaceLabel": {
"$ref": "AAAAAAF+/pWxQBwdVFw="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pWxQBweDKk="
}
},
{
"_type": "UMLAttributeCompartmentView",
"_id": "AAAAAAF+/pWxQBwfkZU=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwZ9SA="
},
"model": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"visible": false,
"font": "Arial;13;0",
"top": -40,
"width": 10,
"height": 10
},
{
"_type": "UMLOperationCompartmentView",
"_id": "AAAAAAF+/pWxQBwgGj4=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwZ9SA="
},
"model": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"visible": false,
"font": "Arial;13;0",
"top": -40,
"width": 10,
"height": 10
},
{
"_type": "UMLReceptionCompartmentView",
"_id": "AAAAAAF+/pWxQBwh+tc=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwZ9SA="
},
"model": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"visible": false,
"font": "Arial;13;0",
"top": -40,
"width": 10,
"height": 10
},
{
"_type": "UMLTemplateParameterCompartmentView",
"_id": "AAAAAAF+/pWxQBwi+pg=",
"_parent": {
"$ref": "AAAAAAF+/pWxQBwZ9SA="
},
"model": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"visible": false,
"font": "Arial;13;0",
"top": -40,
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"containerChangeable": true,
"left": 728,
"top": 368,
"width": 118.849609375,
"height": 49,
"stereotypeDisplay": "icon",
"nameCompartment": {
"$ref": "AAAAAAF+/pWxQBwaXH8="
},
"suppressAttributes": true,
"suppressOperations": true,
"attributeCompartment": {
"$ref": "AAAAAAF+/pWxQBwfkZU="
},
"operationCompartment": {
"$ref": "AAAAAAF+/pWxQBwgGj4="
},
"receptionCompartment": {
"$ref": "AAAAAAF+/pWxQBwh+tc="
},
"templateParameterCompartment": {
"$ref": "AAAAAAF+/pWxQBwi+pg="
}
},
{
"_type": "UMLAssociationView",
"_id": "AAAAAAF+/pXTbx0euyo=",
"_parent": {
"$ref": "AAAAAAFF+qBtyKM79qY="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0a+7U="
},
"subViews": [
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTbx0ffrM=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0a+7U="
},
"font": "Arial;13;0",
"left": 705,
"top": 421,
"width": 14.82177734375,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"edgePosition": 1,
"text": "+1"
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTbx0gh5E=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0a+7U="
},
"visible": null,
"font": "Arial;13;0",
"left": 703,
"top": 409,
"height": 13,
"alpha": 1.5707963267948966,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTbx0hHJg=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0a+7U="
},
"visible": false,
"font": "Arial;13;0",
"left": 729,
"top": 446,
"height": 13,
"alpha": -1.5707963267948966,
"distance": 15,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"edgePosition": 1
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTbx0ijlg=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0b59I="
},
"visible": false,
"font": "Arial;13;0",
"left": 701,
"top": 429,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTbx0jxeI=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0b59I="
},
"visible": false,
"font": "Arial;13;0",
"left": 695,
"top": 417,
"height": 13,
"alpha": 0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTbx0k4QQ=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0b59I="
},
"visible": false,
"font": "Arial;13;0",
"left": 714,
"top": 454,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"edgePosition": 2
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTch0lDOw=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0coXQ="
},
"visible": false,
"font": "Arial;13;0",
"left": 722,
"top": 414,
"height": 13,
"alpha": -0.5235987755982988,
"distance": 30,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTch0m9lU=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0coXQ="
},
"visible": false,
"font": "Arial;13;0",
"left": 712,
"top": 405,
"height": 13,
"alpha": -0.7853981633974483,
"distance": 40,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
}
},
{
"_type": "EdgeLabelView",
"_id": "AAAAAAF+/pXTch0niJ0=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0coXQ="
},
"visible": false,
"font": "Arial;13;0",
"left": 742,
"top": 433,
"height": 13,
"alpha": 0.5235987755982988,
"distance": 25,
"hostEdge": {
"$ref": "AAAAAAF+/pXTbx0euyo="
}
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pXTch0oqeE=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0b59I="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
},
{
"_type": "UMLQualifierCompartmentView",
"_id": "AAAAAAF+/pXTcx0p9OI=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0euyo="
},
"model": {
"$ref": "AAAAAAF+/pXTbx0coXQ="
},
"visible": false,
"font": "Arial;13;0",
"width": 10,
"height": 10
}
],
"font": "Arial;13;0",
"head": {
"$ref": "AAAAAAF+/pWxQBwZ9SA="
},
"tail": {
"$ref": "AAAAAAF+/oVQfv/bUgI="
},
"lineStyle": 1,
"points": "690:463;752:417",
"showVisibility": true,
"nameLabel": {
"$ref": "AAAAAAF+/pXTbx0ffrM="
},
"stereotypeLabel": {
"$ref": "AAAAAAF+/pXTbx0gh5E="
},
"propertyLabel": {
"$ref": "AAAAAAF+/pXTbx0hHJg="
},
"tailRoleNameLabel": {
"$ref": "AAAAAAF+/pXTbx0ijlg="
},
"tailPropertyLabel": {
"$ref": "AAAAAAF+/pXTbx0jxeI="
},
"tailMultiplicityLabel": {
"$ref": "AAAAAAF+/pXTbx0k4QQ="
},
"headRoleNameLabel": {
"$ref": "AAAAAAF+/pXTch0lDOw="
},
"headPropertyLabel": {
"$ref": "AAAAAAF+/pXTch0m9lU="
},
"headMultiplicityLabel": {
"$ref": "AAAAAAF+/pXTch0niJ0="
},
"tailQualifiersCompartment": {
"$ref": "AAAAAAF+/pXTch0oqeE="
},
"headQualifiersCompartment": {
"$ref": "AAAAAAF+/pXTcx0p9OI="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/oB4+f21Hdo=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "TaskChainEngine",
"ownedElements": [
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/oKdTP7PY/4=",
"_parent": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"source": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"target": {
"$ref": "AAAAAAF+/oDrxP3hASY="
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/obCKgCR2Jk=",
"_parent": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"name": "n",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/obCKwCSX2E=",
"_parent": {
"$ref": "AAAAAAF+/obCKgCR2Jk="
},
"reference": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/obCKwCTC3k=",
"_parent": {
"$ref": "AAAAAAF+/obCKgCR2Jk="
},
"reference": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"aggregation": "shared"
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/onnrwPgaSQ=",
"_parent": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
},
"name": "1",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/onnrwPhtDg=",
"_parent": {
"$ref": "AAAAAAF+/onnrwPgaSQ="
},
"reference": {
"$ref": "AAAAAAF+/oB4+f21Hdo="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/onnsAPiR/4=",
"_parent": {
"$ref": "AAAAAAF+/onnrwPgaSQ="
},
"reference": {
"$ref": "AAAAAAF+/oixuwJenZM="
},
"aggregation": "shared"
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oDrxP3hASY=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskChainEngine",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oIYZf6W7vU=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"source": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"target": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
}
},
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oIuff6ny+c=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"source": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"target": {
"$ref": "AAAAAAF+/oFcZf43uUM="
}
},
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oJAIf64xJQ=",
"_parent": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"source": {
"$ref": "AAAAAAF+/oDrxP3hASY="
},
"target": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oEl+P4NGEs=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskStepLifecycle"
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oFcZf43uUM=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "IGroupTaskStep"
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oF4Mv5hqIo=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ICanceller",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oOe+/85LIw=",
"_parent": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"source": {
"$ref": "AAAAAAF+/oF4Mv5hqIo="
},
"target": {
"$ref": "AAAAAAF+/oN48v8O7tk="
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oMJbf7k7SE=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskStep",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oQKQ/9NvDs=",
"_parent": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"source": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"target": {
"$ref": "AAAAAAF+/oN48v8O7tk="
}
},
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oRXKv+Ij8g=",
"_parent": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"source": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"target": {
"$ref": "AAAAAAF+/oQybv9etN0="
}
},
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oT/FP/Gwcg=",
"_parent": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"source": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"target": {
"$ref": "AAAAAAF+/oSvGv+bods="
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oN48v8O7tk=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ICancelable"
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oQybv9etN0=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskStepController"
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oSvGv+bods=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "Runnable"
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/oVQfv/ZEC4=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "AbstractTaskStep",
"ownedElements": [
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/oVy3wAER5E=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"source": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"target": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/ooh0wRqA8w=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"name": "1",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/ooh0wRr2tM=",
"_parent": {
"$ref": "AAAAAAF+/ooh0wRqA8w="
},
"reference": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/ooh0wRssuw=",
"_parent": {
"$ref": "AAAAAAF+/ooh0wRqA8w="
},
"reference": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"aggregation": "shared"
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/pAqzw+iYqM=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"name": "1",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pAqzw+jasg=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+iYqM="
},
"reference": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pAqzw+kIaI=",
"_parent": {
"$ref": "AAAAAAF+/pAqzw+iYqM="
},
"reference": {
"$ref": "AAAAAAF+/o/u+g7mg4c="
},
"aggregation": "shared"
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/pXTbx0a+7U=",
"_parent": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
},
"name": "1",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pXTbx0b59I=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0a+7U="
},
"reference": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pXTbx0coXQ=",
"_parent": {
"$ref": "AAAAAAF+/pXTbx0a+7U="
},
"reference": {
"$ref": "AAAAAAF+/pWxQBwXB0Q="
},
"aggregation": "shared"
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/oXK5AAWQmM=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "SimpleTaskStep",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oZEGACAn0w=",
"_parent": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"source": {
"$ref": "AAAAAAF+/oXK5AAWQmM="
},
"target": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/oYK4wBBo88=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "XTaskStep",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oY38QBvDLM=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"source": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"target": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/pLJ+RPaK9w=",
"_parent": {
"$ref": "AAAAAAF+/oYK4wBBo88="
},
"name": "1",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pLJ+RPbvO0=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPaK9w="
},
"reference": {
"$ref": "AAAAAAF+/oYK4wBBo88="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pLJ+RPc6fM=",
"_parent": {
"$ref": "AAAAAAF+/pLJ+RPaK9w="
},
"reference": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"aggregation": "shared"
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oejFwDkL34=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "IDataStore"
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/ofZUwEjPSY=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskParam",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/of2wAFsd14=",
"_parent": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"source": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
},
"target": {
"$ref": "AAAAAAF+/oejFwDkL34="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/ogt1AGGwlc=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "TaskParam",
"ownedElements": [
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/ohUsAHPXn8=",
"_parent": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"source": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
},
"target": {
"$ref": "AAAAAAF+/ofZUwEjPSY="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/oiH/gH8TJg=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "TaskResult",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/oifbQI+idY=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"source": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"target": {
"$ref": "AAAAAAF+/ogt1AGGwlc="
}
},
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/ojnTwKa348=",
"_parent": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"source": {
"$ref": "AAAAAAF+/oiH/gH8TJg="
},
"target": {
"$ref": "AAAAAAF+/oixuwJenZM="
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/oixuwJenZM=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskResult"
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/osTOwbFpp8=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "MapDataStore",
"ownedElements": [
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/otLdgdKMcA=",
"_parent": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"source": {
"$ref": "AAAAAAF+/osTOwbFpp8="
},
"target": {
"$ref": "AAAAAAF+/oejFwDkL34="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/ovrcwgIMQU=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "AbstractGroupTaskStep",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/owCvQholDw=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"source": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"target": {
"$ref": "AAAAAAF+/oVQfv/ZEC4="
}
},
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/o6Ykwu8maw=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"source": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"target": {
"$ref": "AAAAAAF+/oFcZf43uUM="
}
},
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/o6nTQvoIC4=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"source": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"target": {
"$ref": "AAAAAAF+/oEl+P4NGEs="
}
},
{
"_type": "UMLAssociation",
"_id": "AAAAAAF+/pPGrRb6+Zs=",
"_parent": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
},
"name": "n",
"end1": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pPGrRb7cpA=",
"_parent": {
"$ref": "AAAAAAF+/pPGrRb6+Zs="
},
"reference": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
}
},
"end2": {
"_type": "UMLAssociationEnd",
"_id": "AAAAAAF+/pPGrRb8i/o=",
"_parent": {
"$ref": "AAAAAAF+/pPGrRb6+Zs="
},
"reference": {
"$ref": "AAAAAAF+/oMJbf7k7SE="
},
"aggregation": "shared"
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/o7UzwxLijE=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ConcurrentGroupTaskStep",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/o7qpQyr9Lw=",
"_parent": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"source": {
"$ref": "AAAAAAF+/o7UzwxLijE="
},
"target": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/o8e9g0OMEg=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "SerialGroupTaskStep",
"ownedElements": [
{
"_type": "UMLGeneralization",
"_id": "AAAAAAF+/o9+Jw6o42Q=",
"_parent": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"source": {
"$ref": "AAAAAAF+/o8e9g0OMEg="
},
"target": {
"$ref": "AAAAAAF+/ovrcwgIMQU="
}
}
]
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/o/u+g7mg4c=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ThreadType"
},
{
"_type": "UMLClass",
"_id": "AAAAAAF+/pFMLxBCPvo=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "TaskCommand",
"ownedElements": [
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/pJcFxKaHaA=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"source": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"target": {
"$ref": "AAAAAAF+/oQybv9etN0="
}
},
{
"_type": "UMLInterfaceRealization",
"_id": "AAAAAAF+/pKyUhON0V8=",
"_parent": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"source": {
"$ref": "AAAAAAF+/pFMLxBCPvo="
},
"target": {
"$ref": "AAAAAAF+/oSvGv+bods="
}
}
]
},
{
"_type": "UMLInterface",
"_id": "AAAAAAF+/pWxQBwXB0Q=",
"_parent": {
"$ref": "AAAAAAFF+qBWK6M3Z8Y="
},
"name": "ITaskStepHandler"
}
]
}
]
}
================================================
FILE: build.gradle
================================================
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
apply from: './versions.gradle'
addRepos(repositories) //增加代码仓库
dependencies {
classpath deps.android_gradle_plugin
classpath deps.android_maven_gradle_plugin
classpath 'com.chenenyu:img-optimizer:1.2.0' // 图片压缩
//美团多渠道打包
classpath 'com.meituan.android.walle:plugin:1.1.6'
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
//滴滴的质量优化框架
if (isNeedPackage.toBoolean() && isUseBooster.toBoolean()) {
classpath deps.booster.gradle_plugin
classpath deps.booster.task_processed_res
classpath deps.booster.task_resource_deredundancy
}
}
}
allprojects {
addRepos(repositories)
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 28 16:23:16 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
================================================
FILE: gradle.properties
================================================
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# 是否打包APK,打正式包时请设置为true,使用正式的签名
isNeedPackage=false
# 是否使用booster优化APK,这里需要注意gradle的版本,对于最新的gradle版本可能存在兼容问题
isUseBooster=false
android.precompileDependenciesResources=false
android.useAndroidX=true
android.enableJetifier=true
android.enableD8=true
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
================================================
FILE: gradlew.bat
================================================
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
================================================
FILE: settings.gradle
================================================
include ':app'
include ':xtask-thread'
include ':xtask-core'
================================================
FILE: versions.gradle
================================================
import java.util.regex.Matcher
import java.util.regex.Pattern
ext.deps = [:]
def versions = [:]
versions.android_gradle_plugin = "3.6.1"
versions.android_maven_gradle_plugin = "2.0"
versions.gradle_bintray_plugin = "1.8.0"
versions.booster = "3.1.0"
versions.booster_all = "1.1.1"
versions.support = "28.0.0"
versions.androidx = "1.1.0"
versions.junit = "4.12"
versions.espresso = "3.2.0"
versions.constraint_layout = "1.1.3"
versions.glide = "4.11.0"
versions.rxjava2 = "2.2.20"
versions.rxandroid = "2.1.1"
versions.rxbinding = "2.2.0"
versions.butterknife = "10.1.0"
versions.runner = "1.2.0"
versions.gson = "2.8.5"
versions.leakcanary = "2.6"
//========xlibrary start========//
versions.xui = "1.1.8"
versions.xupdate = "2.1.0"
versions.xaop = "1.1.0"
versions.xutil = "2.0.0"
versions.xhttp2 = "2.0.4"
versions.xpage = "3.2.0"
versions.xrouter = "1.0.1"
//========xlibrary end========//
def deps = [:]
def support = [:]
support.annotations = "com.android.support:support-annotations:$versions.support"
support.app_compat = "com.android.support:appcompat-v7:$versions.support"
support.recyclerview = "com.android.support:recyclerview-v7:$versions.support"
support.cardview = "com.android.support:cardview-v7:$versions.support"
support.design = "com.android.support:design:$versions.support"
support.v4 = "com.android.support:support-v4:$versions.support"
support.core_utils = "com.android.support:support-core-utils:$versions.support"
deps.support = support
def androidx = [:]
androidx.annotations = "androidx.annotation:annotation:$versions.androidx"
androidx.appcompat = "androidx.appcompat:appcompat:$versions.androidx"
androidx.recyclerview = "androidx.recyclerview:recyclerview:$versions.androidx"
androidx.design = "com.google.android.material:material:$versions.androidx"
androidx.multidex = 'androidx.multidex:multidex:2.0.1'
deps.androidx = androidx
def booster = [:]
booster.gradle_plugin = "com.didiglobal.booster:booster-gradle-plugin:$versions.booster"
booster.task_all = "com.didiglobal.booster:booster-task-all:$versions.booster_all"
booster.transform_all = "com.didiglobal.booster:booster-transform-all:$versions.booster_all"
//采用 cwebp 对资源进行压缩
booster.task_compression_cwebp = "com.didiglobal.booster:booster-task-compression-cwebp:$versions.booster"
//采用 pngquant 对资源进行压缩
booster.task_compression_pngquant = "com.didiglobal.booster:booster-task-compression-pngquant:$versions.booster"
//ap_ 文件压缩
booster.task_processed_res = "com.didiglobal.booster:booster-task-compression-processed-res:$versions.booster"
//去冗余资源
booster.task_resource_deredundancy = "com.didiglobal.booster:booster-task-resource-deredundancy:$versions.booster"
//检查 SNAPSHOT 版本
booster.task_check_snapshot = "com.didiglobal.booster:booster-task-check-snapshot:$versions.booster"
//性能瓶颈检测
booster.transform_lint = "com.didiglobal.booster:booster-transform-lint:$versions.booster"
//多线程优化
booster.transform_thread = "com.didiglobal.booster:booster-transform-thread:$versions.booster"
//资源索引内联
booster.transform_r_inline = "com.didiglobal.booster:booster-transform-r-inline:$versions.booster"
//WebView 预加载
booster.transform_webview = "com.didiglobal.booster:booster-transform-webview:$versions.booster"
//SharedPreferences 优化
booster.transform_shared_preferences = "com.didiglobal.booster:booster-transform-shared-preferences:$versions.booster"
//检查覆盖安装导致的 Resources 和 Assets 未加载的 Bug
booster.transform_res_check = "com.didiglobal.booster:booster-transform-res-check:$versions.booster"
//修复 Toast 在 Android 7.1 上的 Bug
booster.transform_toast = "com.didiglobal.booster:booster-transform-toast:$versions.booster"
//处理系统 Crash
booster.transform_activity_thread = "com.didiglobal.booster:booster-transform-activity-thread:$versions.booster"
deps.booster = booster
def butterknife = [:]
butterknife.runtime = "com.jakewharton:butterknife:$versions.butterknife"
butterknife.compiler = "com.jakewharton:butterknife-compiler:$versions.butterknife"
deps.butterknife = butterknife
def espresso = [:]
espresso.core = "androidx.test.espresso:espresso-core:$versions.espresso"
espresso.contrib = "androidx.test.espresso:espresso-contrib:$versions.espresso"
espresso.intents = "androidx.test.espresso:espresso-intents:$versions.espresso"
deps.espresso = espresso
deps.android_gradle_plugin = "com.android.tools.build:gradle:$versions.android_gradle_plugin"
deps.android_maven_gradle_plugin = "com.github.dcendents:android-maven-gradle-plugin:$versions.android_maven_gradle_plugin"
deps.gradle_bintray_plugin = "com.jfrog.bintray.gradle:gradle-bintray-plugin:$versions.gradle_bintray_plugin"
deps.glide = "com.github.bumptech.glide:glide:$versions.glide"
deps.constraint_layout = "androidx.constraint:constraint-layout:$versions.constraint_layout"
deps.junit = "junit:junit:$versions.junit"
deps.runner = "androidx.test:runner:$versions.runner"
deps.rxjava2 = "io.reactivex.rxjava2:rxjava:$versions.rxjava2"
deps.rxandroid = "io.reactivex.rxjava2:rxandroid:$versions.rxandroid"
deps.rxbinding = "com.jakewharton.rxbinding2:rxbinding:$versions.rxbinding"
deps.gson = "com.google.code.gson:gson:$versions.gson"
deps.leakcanary = "com.squareup.leakcanary:leakcanary-android:$versions.leakcanary"
//========xlibrary start=================//
def xlibrary = [:]
xlibrary.xui = "com.github.xuexiangjys:XUI:$versions.xui"
xlibrary.xupdate = "com.github.xuexiangjys:XUpdate:$versions.xupdate"
xlibrary.xaop_runtime = "com.github.xuexiangjys.XAOP:xaop-runtime:$versions.xaop"
xlibrary.xaop_plugin = "com.github.xuexiangjys.XAOP:xaop-plugin:$versions.xaop"
xlibrary.xutil_core = "com.github.xuexiangjys.XUtil:xutil-core:$versions.xutil"
xlibrary.xhttp2 = "com.github.xuexiangjys:XHttp2:$versions.xhttp2"
xlibrary.xpage_lib = "com.github.xuexiangjys.XPage:xpage-lib:$versions.xpage"
xlibrary.xpage_compiler = "com.github.xuexiangjys.XPage:xpage-compiler:$versions.xpage"
xlibrary.xrouter_runtime = "com.github.xuexiangjys.XRouter:xrouter-runtime:$versions.xrouter"
xlibrary.xrouter_compiler = "com.github.xuexiangjys.XRouter:xrouter-compiler:$versions.xrouter"
xlibrary.xrouter_plugin = "com.github.xuexiangjys.XRouter:xrouter-plugin:$versions.xrouter"
deps.xlibrary = xlibrary
//========xlibrary end=================//
ext.deps = deps
def build_versions = [:]
build_versions.min_sdk = 19
build_versions.target_sdk = 28
build_versions.build_tools = "28.0.3"
ext.build_versions = build_versions
def app_release = [:]
app_release.storeFile = "../keystores/android.keystore"
app_release.storePassword = "xuexiang"
app_release.keyAlias = "android.keystore"
app_release.keyPassword = "xuexiang"
ext.app_release = app_release
/**
* @return 是否为release
*/
def isRelease() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern
if (tskReqStr.contains("assemble")) {
println tskReqStr
pattern = Pattern.compile("assemble(\\w*)(Release|Debug)")
} else {
pattern = Pattern.compile("generate(\\w*)(Release|Debug)")
}
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find()) {
String task = matcher.group(0).toLowerCase()
println("[BuildType] Current task: " + task)
return task.contains("release")
} else {
println "[BuildType] NO MATCH FOUND"
return true
}
}
ext.isRelease = this.&isRelease
//默认添加代码仓库路径
static def addRepos(RepositoryHandler handler) {
handler.mavenLocal()
handler.google { url 'https://maven.aliyun.com/repository/google' }
handler.jcenter { url 'https://maven.aliyun.com/repository/jcenter' }
handler.mavenCentral { url 'https://maven.aliyun.com/repository/central' }
handler.maven { url "https://jitpack.io" }
handler.maven { url 'https://maven.aliyun.com/repository/public' }
handler.maven { url "https://repo1.maven.org/maven2/" }
handler.maven { url 'https://oss.sonatype.org/content/repositories/public' }
//Add the Local repository
handler.maven { url 'LocalRepository' }
}
ext.addRepos = this.&addRepos
//自动添加XAOP和XRouter插件
project.buildscript.configurations.each { configuration ->
def dependencies = getProject().dependencies
if (configuration.name == "classpath") {
//XAOP插件
configuration.dependencies.add(dependencies.create(deps.xlibrary.xaop_plugin))
//XRouter插件
configuration.dependencies.add(dependencies.create(deps.xlibrary.xrouter_plugin))
}
}
================================================
FILE: xtask-core/.gitignore
================================================
/build
================================================
FILE: xtask-core/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion build_versions.target_sdk
buildToolsVersion build_versions.build_tools
defaultConfig {
minSdkVersion 14
targetSdkVersion build_versions.target_sdk
}
lintOptions {
abortOnError false
}
}
dependencies {
compileOnly deps.androidx.appcompat
api project(':xtask-thread')
}
apply from: '../JitPackUpload.gradle'
================================================
FILE: xtask-core/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: xtask-core/src/main/AndroidManifest.xml
================================================
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/XTask.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.xtask;
import androidx.annotation.NonNull;
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.ThreadType;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.step.impl.TaskCommand;
import com.xuexiang.xtask.logger.ILogger;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.XTaskExecutor;
import com.xuexiang.xtask.thread.executor.ICategoryExecutorCore;
import com.xuexiang.xtask.thread.executor.IPriorityExecutorCore;
import com.xuexiang.xtask.thread.executor.IScheduledExecutorCore;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.pool.cancel.ICancellerPool;
import com.xuexiang.xtask.utils.CancellerPoolUtils;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
/**
* XTask对外统一API入口
*
* @author xuexiang
* @since 2/7/22 1:14 PM
*/
public final class XTask {
//========================TaskLogger===============================//
/**
* 设置是否打开调试
*
* @param isDebug 是否是调试模式
*/
public static void debug(boolean isDebug) {
TaskLogger.debug(isDebug);
}
/**
* 设置调试模式
*
* @param tag tag,如果不为空是调试模式
*/
public static void debug(String tag) {
TaskLogger.debug(tag);
}
/**
* 设置日志记录接口
*
* @param logger 日志记录接口
*/
public static void setLogger(@NonNull ILogger logger) {
TaskLogger.setLogger(logger);
}
/**
* 设置是否打印任务执行所在的线程名
*
* @param isLogThreadName 是否打印任务执行所在的线程名
*/
public static void setIsLogThreadName(boolean isLogThreadName) {
TaskLogger.setIsLogThreadName(isLogThreadName);
}
//========================TaskChainEngine===============================//
/**
* 获取任务链执行引擎
*
* @return 任务链执行引擎
*/
public static TaskChainEngine getTaskChain() {
return new TaskChainEngine();
}
/**
* 获取任务链执行引擎
*
* @param name 任务链名称
* @return 任务链执行引擎
*/
public static TaskChainEngine getTaskChain(String name) {
return new TaskChainEngine(name);
}
//========================XTaskStep===============================//
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command) {
return XTaskStep.getTask(command);
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param isAutoNotify 是否自动通知任务成功或者失败
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, boolean isAutoNotify) {
return XTaskStep.getTask(command, isAutoNotify);
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param threadType 线程类型
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, ThreadType threadType) {
return XTaskStep.getTask(command, threadType);
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param taskParam 任务参数
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, @NonNull ITaskParam taskParam) {
return XTaskStep.getTask(command, taskParam);
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param threadType 线程类型
* @param taskParam 任务参数
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, ThreadType threadType, @NonNull ITaskParam taskParam) {
return XTaskStep.getTask(command, threadType, taskParam);
}
/**
* 获取简化任务的构建者
*
* @param command 任务执行内容
* @return 简化任务的构建者
*/
public static XTaskStep.Builder getTaskBuilder(@NonNull TaskCommand command) {
return XTaskStep.newBuilder(command);
}
//========================ConcurrentGroupTaskStep===============================//
/**
* 获取并行任务组
*
* @return 并行任务组
*/
public static ConcurrentGroupTaskStep getConcurrentGroupTask() {
return ConcurrentGroupTaskStep.get();
}
/**
* 获取并行任务组
*
* @param name 任务组名称
* @return 并行任务组
*/
public static ConcurrentGroupTaskStep getConcurrentGroupTask(@NonNull String name) {
return ConcurrentGroupTaskStep.get(name);
}
/**
* 获取并行任务组
*
* @param threadType 线程类型
* @return 并行任务组
*/
public static ConcurrentGroupTaskStep getConcurrentGroupTask(@NonNull ThreadType threadType) {
return ConcurrentGroupTaskStep.get(threadType);
}
//========================SerialGroupTaskStep===============================//
/**
* 获取串行任务组
*
* @return 串行任务组
*/
public static SerialGroupTaskStep getSerialGroupTask() {
return SerialGroupTaskStep.get();
}
/**
* 获取串行任务组
*
* @param name 任务组名称
* @return 串行任务组
*/
public static SerialGroupTaskStep getSerialGroupTask(@NonNull String name) {
return SerialGroupTaskStep.get(name);
}
/**
* 获取串行任务组
*
* @param threadType 任务组名称
* @return 串行任务组
*/
public static SerialGroupTaskStep getSerialGroupTask(@NonNull ThreadType threadType) {
return SerialGroupTaskStep.get(threadType);
}
//========================CancellerPoolUtils===============================//
/**
* 设置自定义的取消者订阅池
*
* @param cancellerPool 取消者订阅池
*/
public static void setCancellerPool(ICancellerPool cancellerPool) {
CancellerPoolUtils.setCancellerPool(cancellerPool);
}
/**
* 取消指定任务链
*
* @param name 任务链名称
* @return 是否执行成功
*/
public static boolean cancelTaskChain(String name) {
return CancellerPoolUtils.cancel(name);
}
/**
* 取消指定任务链集合
*
* @param names 任务链名称集合
*/
public static void cancelTaskChain(String... names) {
CancellerPoolUtils.cancel(names);
}
/**
* 取消指定任务链集合
*
* @param names 任务链名称集合
*/
public static void cancelTaskChain(Collection names) {
CancellerPoolUtils.cancel(names);
}
/**
* 取消所有任务链
*/
public static void cancelAllTaskChain() {
CancellerPoolUtils.cancelAll();
}
/**
* 清除所有任务链
*
* @param ifNeedCancel 是否在清除前取消任务链
*/
public static void clearTaskChain(boolean ifNeedCancel) {
CancellerPoolUtils.clear(ifNeedCancel);
}
//========================XTaskExecutor===============================//
/**
* 设置优先级控制的执行内核实现接口
*
* @param priorityExecutorCore 优先级控制的执行内核实现接口
*/
public static void setPriorityExecutorCore(@NonNull IPriorityExecutorCore priorityExecutorCore) {
XTaskExecutor.get().setPriorityExecutorCore(priorityExecutorCore);
}
/**
* 设置类别执行内核实现接口
*
* @param categoryExecutorCore 类别执行内核实现接口
*/
public static void setCategoryExecutorCore(@NonNull ICategoryExecutorCore categoryExecutorCore) {
XTaskExecutor.get().setCategoryExecutorCore(categoryExecutorCore);
}
/**
* 设置周期执行内核的实现接口
*
* @param scheduledExecutorCore 周期执行内核的实现接口
*/
public static void setScheduledExecutorCore(@NonNull IScheduledExecutorCore scheduledExecutorCore) {
XTaskExecutor.get().setScheduledExecutorCore(scheduledExecutorCore);
}
/**
* 停止工作
*/
public static void shutdown() {
XTaskExecutor.get().shutdown();
}
//================PriorityExecutorCore==================//
/**
* 按优先级执行异步任务
*
* @param task 任务
* @param priority 优先级
* @return 取消接口
*/
public static ICancelable submit(Runnable task, int priority) {
return XTaskExecutor.get().submit(task, priority);
}
/**
* 分组按优先级执行异步任务
*
* @param groupName 任务组名
* @param task 任务
* @param priority 优先级
* @return 取消接口
*/
public static ICancelable submit(String groupName, Runnable task, int priority) {
return XTaskExecutor.get().submit(groupName, task, priority);
}
//================CategoryExecutorCore==================//
/**
* 执行任务到主线程
*
* @param task 任务
* @return 是否执行成功
*/
public static boolean postToMain(Runnable task) {
return XTaskExecutor.get().postToMain(task);
}
/**
* 延迟执行任务到主线程
*
* @param task 任务
* @param delayMillis 延迟时间
* @return 是否执行成功
*/
public static ICancelable postToMainDelay(Runnable task, long delayMillis) {
return XTaskExecutor.get().postToMainDelay(task, delayMillis);
}
/**
* 执行紧急异步任务【线程的优先级默认是10】
*
* @param task 任务
* @return 取消接口
*/
public static ICancelable emergentSubmit(Runnable task) {
return XTaskExecutor.get().emergentSubmit(task);
}
/**
* 执行普通异步任务【线程的优先级是5】
*
* @param task 任务
* @return 取消接口
*/
public static ICancelable submit(Runnable task) {
return XTaskExecutor.get().submit(task);
}
/**
* 执行后台异步任务【线程的优先级是1】
*
* @param task 任务
* @return 取消接口
*/
public static ICancelable backgroundSubmit(Runnable task) {
return XTaskExecutor.get().backgroundSubmit(task);
}
/**
* 执行io耗时的异步任务【线程的优先级是5】
*
* @param task 任务
* @return 取消接口
*/
public static ICancelable ioSubmit(Runnable task) {
return XTaskExecutor.get().ioSubmit(task);
}
/**
* 执行分组异步任务【线程的优先级是5】
*
* @param groupName 任务组名
* @param task 任务
* @return 取消接口
*/
public static ICancelable groupSubmit(String groupName, Runnable task) {
return XTaskExecutor.get().groupSubmit(groupName, task);
}
//================ScheduledExecutorCore==================//
/**
* 执行延期任务
*
* @param task 任务
* @param delay 延迟时长
* @param unit 时间单位
* @return 取消接口
*/
public static ICancelable schedule(Runnable task, long delay, TimeUnit unit) {
return XTaskExecutor.get().schedule(task, delay, unit);
}
/**
* 执行周期任务(以固定频率执行的任务)
*
* @param task 任务
* @param initialDelay 初始延迟时长
* @param period 间隔时长
* @param unit 时间单位
* @return 取消接口
*/
public static ICancelable scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return XTaskExecutor.get().scheduleAtFixedRate(task, initialDelay, period, unit);
}
/**
* 执行周期任务(以固定延时执行的任务,延时是相对当前任务结束为起点计算开始时间)
*
* @param task 任务
* @param initialDelay 初始延迟时长
* @param period 间隔时长
* @param unit 时间单位
* @return 取消接口
*/
public static ICancelable scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit unit) {
return XTaskExecutor.get().scheduleWithFixedDelay(task, initialDelay, period, unit);
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/api/TaskChainEngine.java
================================================
/*
* Copyright (C) 2021 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.xtask.api;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.ITaskChainCallback;
import com.xuexiang.xtask.core.ITaskChainEngine;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.param.ITaskResult;
import com.xuexiang.xtask.core.param.impl.TaskResult;
import com.xuexiang.xtask.core.step.ITaskStep;
import com.xuexiang.xtask.core.step.impl.AutoDestroyTaskChainCallback;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.pool.cancel.ICanceller;
import com.xuexiang.xtask.utils.CancellerPoolUtils;
import com.xuexiang.xtask.utils.CommonUtils;
import com.xuexiang.xtask.utils.TaskUtils;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 默认实现的任务链执行引擎
*
* @author xuexiang
* @since 2021/11/2 1:46 AM
*/
public class TaskChainEngine implements ITaskChainEngine {
private static final String TAG = TaskLogger.getLogTag("TaskChainEngine");
/**
* 任务链名前缀
*/
private static final String TASK_CHAIN_NAME_PREFIX = "TaskChain-";
/**
* 是否取消
*/
private AtomicBoolean mIsCancelled = new AtomicBoolean(false);
/**
* 任务链名称
*/
private String mName;
/**
* 任务结果
*/
private TaskResult mResult = new TaskResult();
/**
* 执行任务集合
*/
private List mTasks = new CopyOnWriteArrayList<>();
/**
* 任务链执行回调, 默认回调处理是自动销毁任务链
*/
private ITaskChainCallback mTaskChainCallback = new AutoDestroyTaskChainCallback();
/**
* 执行的时候,是否把取消者加入到缓存池中去
*/
private boolean mIsAddCancellerPool;
/**
* 获取任务链执行引擎
*
* @return 任务链执行引擎
*/
public static TaskChainEngine get() {
return new TaskChainEngine();
}
/**
* 获取任务链执行引擎
*
* @param name 任务链名称
* @return 任务链执行引擎
*/
public static TaskChainEngine get(String name) {
return new TaskChainEngine(name);
}
/**
* 构造方法
*/
public TaskChainEngine() {
this(TASK_CHAIN_NAME_PREFIX + UUID.randomUUID().toString());
}
/**
* 构造方法
*
* @param name 任务链名称
*/
public TaskChainEngine(String name) {
mName = name;
}
@Override
public String getName() {
return mName;
}
@Override
public TaskChainEngine setTaskParam(@NonNull ITaskParam taskParam) {
mResult.updateParam(taskParam);
return this;
}
@Override
public TaskChainEngine setTaskChainCallback(ITaskChainCallback iTaskChainCallback) {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " setTaskChainCallback failed, task chain has destroyed!");
return this;
}
mTaskChainCallback = iTaskChainCallback;
return this;
}
@Override
public TaskChainEngine addTask(ITaskStep taskStep) {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " addTask failed, task chain has destroyed!");
return this;
}
if (taskStep != null) {
taskStep.setTaskStepLifecycle(this);
mTasks.add(taskStep);
}
return this;
}
@Override
public TaskChainEngine addTasks(List taskStepList) {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " addTasks failed, task chain has destroyed!");
return this;
}
if (!CommonUtils.isEmpty(taskStepList)) {
for (ITaskStep taskStep : taskStepList) {
addTask(taskStep);
}
}
return this;
}
@Override
public void clearTask() {
if (isDestroy()) {
return;
}
if (CommonUtils.isEmpty(mTasks)) {
return;
}
for (ITaskStep taskStep : mTasks) {
taskStep.recycle();
}
mTasks.clear();
}
@Override
public ICanceller start() {
return start(true);
}
@Override
public ICanceller start(boolean isAddPool) {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " start failed, task chain has destroyed!");
return null;
}
mIsAddCancellerPool = isAddPool;
onTaskChainStart();
ITaskStep firstTaskStep = TaskUtils.findNextTaskStep(mTasks, null);
if (firstTaskStep != null) {
firstTaskStep.prepareTask(mResult);
ICancelable cancelable = TaskUtils.executeTask(firstTaskStep);
firstTaskStep.setCancelable(cancelable);
} else {
onTaskChainCompleted(mResult);
}
if (isAddPool) {
CancellerPoolUtils.add(getName(), this);
}
return this;
}
@Override
public void reset() {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " reset failed, task chain has destroyed!");
return;
}
mIsCancelled.set(false);
mResult.clear();
clearTask();
CancellerPoolUtils.remove(getName());
}
@Override
public void destroy() {
if (isDestroy()) {
return;
}
TaskLogger.dTag(TAG, getTaskChainName() + " destroy...");
reset();
mTaskChainCallback = null;
mResult = null;
mTasks = null;
}
/**
* 是否已经销毁
*
* @return 是否已经销毁
*/
private boolean isDestroy() {
return mResult == null || mTasks == null;
}
@Override
public void cancel() {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " cancel failed, task chain has destroyed!");
return;
}
if (isCancelled()) {
return;
}
for (ITaskStep taskStep : mTasks) {
taskStep.cancel();
}
mIsCancelled.set(true);
onTaskChainCancelled();
}
@Override
public boolean isCancelled() {
return mIsCancelled.get();
}
@Override
public void onTaskStepCompleted(@NonNull ITaskStep step, @NonNull ITaskResult result) {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " onTaskStepCompleted failed, task chain has destroyed!");
return;
}
mResult.saveResult(result);
ITaskStep nextTaskStep = TaskUtils.findNextTaskStep(mTasks, step);
if (nextTaskStep != null) {
// 更新数据,将上一个task的结果更新到下一个task
nextTaskStep.prepareTask(mResult);
ICancelable cancelable = TaskUtils.executeTask(nextTaskStep);
nextTaskStep.setCancelable(cancelable);
} else {
onTaskChainCompleted(result);
}
}
@Override
public void onTaskStepError(@NonNull ITaskStep step, @NonNull ITaskResult result) {
if (isDestroy()) {
TaskLogger.eTag(TAG, getTaskChainName() + " onTaskStepError failed, task chain has destroyed!");
return;
}
onTaskChainError(result);
}
private boolean isNeedChangeToMainThread() {
return mTaskChainCallback.isCallBackOnMainThread() && !TaskUtils.isMainThread();
}
private void onTaskChainStart() {
TaskLogger.dTag(TAG, getTaskChainName() + "(size=" + CommonUtils.getSize(mTasks) + ") start...");
if (mTaskChainCallback == null) {
return;
}
if (isNeedChangeToMainThread()) {
TaskUtils.runOnMainThread(new Runnable() {
@Override
public void run() {
mTaskChainCallback.onTaskChainStart(TaskChainEngine.this);
}
});
} else {
mTaskChainCallback.onTaskChainStart(this);
}
}
private void onTaskChainCancelled() {
TaskLogger.dTag(TAG, getTaskChainName() + " cancelled!");
if (mIsAddCancellerPool) {
CancellerPoolUtils.remove(getName());
}
if (mTaskChainCallback == null) {
return;
}
if (isNeedChangeToMainThread()) {
TaskUtils.runOnMainThread(new Runnable() {
@Override
public void run() {
mTaskChainCallback.onTaskChainCancelled(TaskChainEngine.this);
}
});
} else {
mTaskChainCallback.onTaskChainCancelled(this);
}
}
private void onTaskChainCompleted(final ITaskResult result) {
TaskLogger.dTag(TAG, getTaskChainName() + " completed!");
if (mIsAddCancellerPool) {
CancellerPoolUtils.remove(getName());
}
if (mTaskChainCallback == null) {
return;
}
if (isNeedChangeToMainThread()) {
TaskUtils.runOnMainThread(new Runnable() {
@Override
public void run() {
mTaskChainCallback.onTaskChainCompleted(TaskChainEngine.this, result);
}
});
} else {
mTaskChainCallback.onTaskChainCompleted(this, result);
}
}
private void onTaskChainError(final ITaskResult result) {
TaskLogger.dTag(TAG, getTaskChainName() + " error!");
if (mIsAddCancellerPool) {
CancellerPoolUtils.remove(getName());
}
if (mTaskChainCallback == null) {
return;
}
if (isNeedChangeToMainThread()) {
TaskUtils.runOnMainThread(new Runnable() {
@Override
public void run() {
mTaskChainCallback.onTaskChainError(TaskChainEngine.this, result);
}
});
} else {
mTaskChainCallback.onTaskChainError(this, result);
}
}
/**
* 获取任务链的名称
*
* @return 任务链的名称
*/
protected String getTaskChainName() {
return "Task chain [" + getName() + "]";
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/api/step/ConcurrentGroupTaskStep.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.xtask.api.step;
import androidx.annotation.NonNull;
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.step.ITaskStep;
import com.xuexiang.xtask.core.step.impl.AbstractGroupTaskStep;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.utils.TaskUtils;
/**
* 并行任务组(不进行具体的任务)
*
* @author xuexiang
* @since 2021/11/8 2:04 AM
*/
public class ConcurrentGroupTaskStep extends AbstractGroupTaskStep {
/**
* 获取并行任务组
*
* @return 并行任务组
*/
public static ConcurrentGroupTaskStep get() {
return new ConcurrentGroupTaskStep();
}
/**
* 获取并行任务组
*
* @param name 任务组名称
* @return 并行任务组
*/
public static ConcurrentGroupTaskStep get(@NonNull String name) {
return new ConcurrentGroupTaskStep(name);
}
/**
* 获取并行任务组
*
* @param threadType 线程类型
* @return 并行任务组
*/
public static ConcurrentGroupTaskStep get(@NonNull ThreadType threadType) {
return new ConcurrentGroupTaskStep(threadType);
}
public ConcurrentGroupTaskStep() {
super();
}
public ConcurrentGroupTaskStep(@NonNull String name) {
super(name);
}
public ConcurrentGroupTaskStep(@NonNull ThreadType threadType) {
super(threadType);
}
public ConcurrentGroupTaskStep(@NonNull String name, @NonNull ITaskParam taskParam) {
super(name, taskParam);
}
public ConcurrentGroupTaskStep(@NonNull String name, @NonNull ThreadType threadType) {
super(name, threadType);
}
@Override
public void doTask() throws Exception {
initGroupTask();
if (mTaskTotalSize > 0) {
for (ITaskStep taskStep : getTasks()) {
if (taskStep != null && taskStep.accept()) {
taskStep.prepareTask(getResult());
ICancelable cancelable = TaskUtils.executeTask(taskStep);
taskStep.setCancelable(cancelable);
}
}
} else {
notifyTaskSucceed(getResult());
}
}
@Override
public void onTaskStepCompleted(@NonNull ITaskStep step, @NonNull ITaskResult result) {
getResult().saveResultNotPath(result);
getResult().addGroupPath(step.getName(), mTaskIndex.getAndIncrement(), mTaskTotalSize);
if (mTaskIndex.get() == mTaskTotalSize) {
notifyTaskSucceed(result);
}
}
@Override
public void onTaskStepError(@NonNull ITaskStep step, @NonNull ITaskResult result) {
if (mTaskIndex.get() != -1) {
// 并行任务,只通知一次失败
notifyTaskFailed(result);
mTaskIndex.set(-1);
}
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/api/step/SerialGroupTaskStep.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.xtask.api.step;
import androidx.annotation.NonNull;
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.step.ITaskStep;
import com.xuexiang.xtask.core.step.impl.AbstractGroupTaskStep;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.utils.TaskUtils;
/**
* 串行任务组(不进行具体的任务)
*
* @author xuexiang
* @since 2021/11/8 2:02 AM
*/
public class SerialGroupTaskStep extends AbstractGroupTaskStep {
/**
* 获取串行任务组
*
* @return 串行任务组
*/
public static SerialGroupTaskStep get() {
return new SerialGroupTaskStep();
}
/**
* 获取串行任务组
*
* @param name 任务组名称
* @return 串行任务组
*/
public static SerialGroupTaskStep get(@NonNull String name) {
return new SerialGroupTaskStep(name);
}
/**
* 获取串行任务组
*
* @param threadType 线程类型
* @return 串行任务组
*/
public static SerialGroupTaskStep get(@NonNull ThreadType threadType) {
return new SerialGroupTaskStep(threadType);
}
public SerialGroupTaskStep() {
super();
}
public SerialGroupTaskStep(@NonNull String name) {
super(name);
}
public SerialGroupTaskStep(@NonNull ThreadType threadType) {
super(threadType);
}
public SerialGroupTaskStep(@NonNull String name, @NonNull ITaskParam taskParam) {
super(name, taskParam);
}
public SerialGroupTaskStep(@NonNull String name, @NonNull ThreadType threadType) {
super(name, threadType);
}
@Override
public void doTask() throws Exception {
ITaskStep firstTaskStep = TaskUtils.findNextTaskStep(getTasks(), null);
if (firstTaskStep != null) {
initGroupTask();
firstTaskStep.prepareTask(getResult());
ICancelable cancelable = TaskUtils.executeTask(firstTaskStep);
firstTaskStep.setCancelable(cancelable);
} else {
notifyTaskSucceed(getResult());
}
}
@Override
public void onTaskStepCompleted(@NonNull ITaskStep step, @NonNull ITaskResult result) {
getResult().saveResultNotPath(result);
getResult().addGroupPath(step.getName(), mTaskIndex.getAndIncrement(), mTaskTotalSize);
ITaskStep nextTaskStep = TaskUtils.findNextTaskStep(getTasks(), step);
if (nextTaskStep != null) {
// 更新数据,将上一个task的结果更新到下一个task
nextTaskStep.prepareTask(getResult());
ICancelable cancelable = TaskUtils.executeTask(nextTaskStep);
nextTaskStep.setCancelable(cancelable);
} else {
notifyTaskSucceed(result);
}
}
@Override
public void onTaskStepError(@NonNull ITaskStep step, @NonNull ITaskResult result) {
notifyTaskFailed(result);
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/api/step/SimpleTaskStep.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.xtask.api.step;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.ThreadType;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.step.impl.AbstractTaskStep;
import com.xuexiang.xtask.core.step.impl.AutoNotifyTaskStepHandler;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 简单的任务执行步骤
*
* @author xuexiang
* @since 2/1/22 11:11 PM
*/
public abstract class SimpleTaskStep extends AbstractTaskStep {
private static final AtomicInteger TASK_NUMBER = new AtomicInteger(1);
/**
* 任务名称
*/
private String mName;
/**
* 构造方法
*/
public SimpleTaskStep() {
this("SimpleTaskStep-" + TASK_NUMBER.getAndIncrement());
}
/**
* 构造方法
*
* @param name 任务名称
*/
public SimpleTaskStep(String name) {
initTaskStep(name);
}
/**
* 构造方法
*
* @param name 任务名称
* @param threadType 线程类型
*/
public SimpleTaskStep(String name, ThreadType threadType) {
super(threadType);
initTaskStep(name);
}
/**
* 构造方法
*
* @param name 任务名称
* @param taskParam 任务参数
*/
public SimpleTaskStep(String name, @NonNull ITaskParam taskParam) {
super(taskParam);
initTaskStep(name);
}
/**
* 构造方法
*
* @param name 任务名称
* @param threadType 线程类型
* @param taskParam 任务参数
*/
public SimpleTaskStep(String name, ThreadType threadType, @NonNull ITaskParam taskParam) {
super(threadType, taskParam);
initTaskStep(name);
}
/**
* 初始化任务步骤
*
* @param name 任务步骤名
*/
protected void initTaskStep(String name) {
mName = name;
if (isAutoNotify()) {
setTaskStepHandler(new AutoNotifyTaskStepHandler());
}
}
@Override
public String getName() {
return mName;
}
/**
* 是否自动通知执行结果【需要手动控制的请设置false】
*
* @return 是否自动通知执行结果
*/
protected boolean isAutoNotify() {
return true;
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/api/step/XTaskStep.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.xtask.api.step;
import androidx.annotation.NonNull;
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.ITaskStepHandler;
import com.xuexiang.xtask.core.step.impl.AbstractTaskStep;
import com.xuexiang.xtask.core.step.impl.TaskCommand;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.utils.CommonUtils;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 简化任务步骤的使用
*
* @author xuexiang
* @since 1/30/22 5:08 PM
*/
public class XTaskStep extends AbstractTaskStep {
private static final String TAG = TaskLogger.getLogTag("XTaskStep");
/**
* 任务的编号【静态全局】
*/
private static final AtomicInteger TASK_NUMBER = new AtomicInteger(1);
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command) {
return new Builder(command).build();
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param isAutoNotify 是否自动通知任务成功或者失败
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, boolean isAutoNotify) {
return new Builder(command).setIsAutoNotify(isAutoNotify).build();
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param threadType 线程类型
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, ThreadType threadType) {
return new Builder(command)
.setThreadType(threadType)
.build();
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param taskParam 任务参数
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, @NonNull ITaskParam taskParam) {
return new Builder(command)
.setTaskParam(taskParam)
.build();
}
/**
* 获取简化的任务
*
* @param command 任务执行内容
* @param threadType 线程类型
* @param taskParam 任务参数
* @return 简化任务的构建者
*/
public static XTaskStep getTask(@NonNull TaskCommand command, ThreadType threadType, @NonNull ITaskParam taskParam) {
return new Builder(command)
.setThreadType(threadType)
.setTaskParam(taskParam)
.build();
}
/**
* 获取简化任务的构建者
*
* @param command 任务执行内容
* @return 简化任务的构建者
*/
public static Builder newBuilder(@NonNull TaskCommand command) {
return new Builder(command);
}
/**
* 任务名称
*/
private String mName;
/**
* 任务执行内容
*/
private TaskCommand mTaskCommand;
/**
* 是否自动通知任务成功或者失败
*/
private boolean mIsAutoNotify;
/**
* 构造方法
*
* @param name 任务步骤名称
* @param command 任务执行内容
* @param threadType 线程类型
* @param taskParam 任务参数
* @param taskHandler 任务处理者
* @param isAutoNotify 是否自动通知任务成功或者失败
*/
private XTaskStep(@NonNull String name, @NonNull TaskCommand command, ThreadType threadType, @NonNull ITaskParam taskParam, ITaskStepHandler taskHandler, boolean isAutoNotify) {
super(threadType, taskParam);
mName = name;
mTaskCommand = command;
mTaskCommand.setTaskStepResultController(this);
mIsAutoNotify = isAutoNotify;
setTaskStepHandler(taskHandler);
}
@Override
public String getName() {
return mName;
}
@Override
public void doTask() throws Exception {
if (mIsAutoNotify) {
try {
mTaskCommand.run();
} catch (Exception e) {
TaskLogger.eTag(TAG, getTaskLogName() + " has error!", e);
notifyTaskFailed(ITaskResult.PROCESS_TASK_THROW_EXCEPTION, e.getMessage());
return;
}
mTaskCommand.notifyTaskSucceed();
} else {
mTaskCommand.run();
}
}
/**
* 简化任务构建者
*
* @author xuexiang
* @since 1/30/22 5:22 PM
*/
public static final class Builder {
/**
* 任务步骤名称
*/
String name;
/**
* 任务执行内容
*/
TaskCommand command;
/**
* 线程执行类型
*/
ThreadType threadType = ThreadType.ASYNC;
/**
* 任务参数
*/
ITaskParam taskParam;
/**
* 任务处理者
*/
ITaskStepHandler taskHandler;
/**
* 是否自动通知任务成功或者失败
*/
boolean isAutoNotify = true;
/**
* 构造方法
*
* @param command 执行内容
*/
private Builder(@NonNull TaskCommand command) {
this.command = command;
}
public Builder setName(String name) {
this.name = name;
return this;
}
public Builder setCommand(TaskCommand command) {
this.command = command;
return this;
}
public Builder setThreadType(ThreadType threadType) {
this.threadType = threadType;
return this;
}
public Builder setTaskParam(ITaskParam taskParam) {
this.taskParam = taskParam;
return this;
}
public Builder setTaskHandler(ITaskStepHandler taskHandler) {
this.taskHandler = taskHandler;
return this;
}
public Builder setIsAutoNotify(boolean isAutoNotify) {
this.isAutoNotify = isAutoNotify;
return this;
}
public XTaskStep build() {
CommonUtils.requireNonNull(this.command, "XTaskStep.Builder command can not be null!");
if (CommonUtils.isEmpty(name)) {
name = "XTaskStep-" + TASK_NUMBER.getAndIncrement();
}
if (taskParam == null) {
taskParam = new TaskParam();
}
return new XTaskStep(name, command, threadType, taskParam, taskHandler, isAutoNotify);
}
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/ITaskChainCallback.java
================================================
/*
* Copyright (C) 2021 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.xtask.core;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.param.ITaskResult;
/**
* 任务链执行回调
*
* @author xuexiang
* @since 2021/10/18 10:32 PM
*/
public interface ITaskChainCallback {
/**
* 回调是否返回主线程
*
* @return 是否返回主线程
*/
boolean isCallBackOnMainThread();
/**
* 任务步骤开始执行
*
* @param engine 任务链
*/
void onTaskChainStart(@NonNull ITaskChainEngine engine);
/**
* 任务步骤执行完毕
*
* @param engine 任务链
* @param result 任务执行结果
*/
void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result);
/**
* 任务步骤执行发生异常
*
* @param engine 任务链
* @param result 任务执行结果
*/
void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result);
/**
* 任务步骤执行被取消
*
* @param engine 任务链
*/
void onTaskChainCancelled(@NonNull ITaskChainEngine engine);
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/ITaskChainEngine.java
================================================
/*
* Copyright (C) 2021 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.xtask.core;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.step.IGroupTaskStep;
import com.xuexiang.xtask.core.step.ITaskStepLifecycle;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.pool.cancel.ICanceller;
/**
* 任务链执行引擎实现接口
*
* @author xuexiang
* @since 2021/10/19 1:43 AM
*/
public interface ITaskChainEngine extends ITaskStepLifecycle, IGroupTaskStep, ICanceller {
/**
* 获取任务链的名称
*
* @return 任务链的名称
*/
@Override
String getName();
/**
* 初始化任务参数
*
* @param taskParam 任务参数
* @return 任务链执行引擎
*/
ITaskChainEngine setTaskParam(@NonNull ITaskParam taskParam);
/**
* 设置任务链执行回调
*
* @param iTaskChainCallback 任务链执行回调
* @return 任务链执行引擎
*/
ITaskChainEngine setTaskChainCallback(ITaskChainCallback iTaskChainCallback);
/**
* 开始任务
*
* @return 取消的接口
*/
ICancelable start();
/**
* 开始任务
*
* @param isAddPool 是否增加到取消者订阅池里
* @return 取消的接口
*/
ICancelable start(boolean isAddPool);
/**
* 重置任务链(可继续使用)
*/
void reset();
/**
* 销毁任务链(不可使用)
*/
void destroy();
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/ThreadType.java
================================================
/*
* Copyright (C) 2021 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.xtask.core;
/**
* 线程执行类型
*
* @author xuexiang
* @since 2021/10/27 2:01 AM
*/
public enum ThreadType {
/**
* 主线程(UI线程)
*/
MAIN,
/**
* 异步线程(开子线程,普通线程池)
*/
ASYNC,
/**
* 异步线程(开子线程,io线程池)
*/
ASYNC_IO,
/**
* 异步线程(开子线程,紧急线程池)
*/
ASYNC_EMERGENT,
/**
* 异步线程(开子线程,优先级较低线程池)
*/
ASYNC_BACKGROUND,
/**
* 同步线程(直接执行)
*/
SYNC
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/param/IDataStore.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.xtask.core.param;
import java.util.Map;
/**
* 数据存储仓库接口
*
* @author xuexiang
* @since 2021/11/13 4:08 PM
*/
public interface IDataStore {
/**
* String的默认值
*/
String DEFAULT_STRING = "";
/**
* Integer的默认值
*/
int DEFAULT_INTEGER = -1;
/**
* Boolean的默认值
*/
boolean DEFAULT_BOOLEAN = false;
/**
* 获取数据
*
* @param key 键
* @return 值
*/
Object get(String key);
/**
* 存储数据
*
* @param key 键
* @param value 值
* @return 数据存储仓库
*/
IDataStore put(String key, Object value);
/**
* 获取目标数据
*
* @param key 键
* @param clazz 目标数据类型
* @return 值
*/
T getObject(String key, Class clazz);
/**
* 获取目标数据
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
T getObject(String key, T defaultValue);
/**
* 获取String类型数据
*
* @param key 键
* @return 值
*/
String getString(String key);
/**
* 获取String类型数据
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
String getString(String key, String defaultValue);
/**
* 获取Boolean类型数据
*
* @param key 键
* @return 值
*/
boolean getBoolean(String key);
/**
* 获取boolean类型数据
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
boolean getBoolean(String key, boolean defaultValue);
/**
* 获取Integer类型数据
*
* @param key 键
* @return 值
*/
int getInt(String key);
/**
* 获取int类型数据
*
* @param key 键
* @param defaultValue 默认值
* @return 值
*/
int getInt(String key, int defaultValue);
/**
* 获取存储的所以信息
*
* @return 存储信息
*/
Map getData();
/**
* 清除数据
*/
void clear();
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/param/ITaskParam.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.xtask.core.param;
/**
* 任务参数信息实现接口
*
* @author xuexiang
* @since 2021/10/19 1:44 AM
*/
public interface ITaskParam extends IDataStore {
/**
* 增加任务路径
*
* @param path 任务路径
*/
void addPath(String path);
/**
* 增加组任务路径
*
* @param path 任务路径
* @param index 任务索引
* @param total 总任务数
*/
void addGroupPath(String path, int index, int total);
/**
* 获取当前任务执行的全路径
*
* @return 当前任务执行的全路径
*/
String getPath();
/**
* 更新路径
*
* @param path 任务全路径
*/
void updatePath(String path);
/**
* 获取数据存储仓库
*
* @return 数据存储仓库
*/
IDataStore getDataStore();
/**
* 更新数据
*
* @param iDataStore 数据存储仓库
*/
void updateData(IDataStore iDataStore);
/**
* 更新参数
*
* @param path 任务全路径
* @param iDataStore 数据存储仓库
*/
void updateParam(String path, IDataStore iDataStore);
/**
* 更新参数
*
* @param taskParam 任务参数
*/
void updateParam(ITaskParam taskParam);
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/param/ITaskResult.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.xtask.core.param;
/**
* 任务执行结果
*
* @author xuexiang
* @since 2021/10/27 2:10 AM
*/
public interface ITaskResult {
/**
* 任务成功
*/
int SUCCESS = 0;
/**
* 任务失败
*/
int ERROR = -1;
/**
* 任务执行过程中发生异常
*/
int PROCESS_TASK_THROW_EXCEPTION = -2;
/**
* 获取任务结果码
*
* @return 任务结果码
*/
int getCode();
/**
* 获取任务执行信息
*
* @return 任务执行信息
*/
String getMessage();
/**
* 设置结果信息
*
* @param code 任务结果码
* @param message 任务执行信息
*/
void setResult(int code, String message);
/**
* 保存执行结果
*
* @param taskResult 执行结果
*/
void saveResult(ITaskResult taskResult);
/**
* 保存执行结果,但是不保存执行路径
*
* @param taskResult 执行结果
*/
void saveResultNotPath(ITaskResult taskResult);
/**
* 更新参数
*
* @param taskParam 任务参数
*/
void updateParam(ITaskParam taskParam);
/**
* 获取数据存储仓库
*
* @return 数据存储仓库
*/
IDataStore getDataStore();
/**
* 获取当前任务执行的全路径
*
* @return 当前任务执行的全路径
*/
String getPath();
/**
* 获取详细信息
*
* @return 详细信息
*/
String getDetailMessage();
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/param/impl/MapDataStore.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.xtask.core.param.impl;
import com.xuexiang.xtask.core.param.IDataStore;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.utils.CommonUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 默认数据存储仓库, 使用ConcurrentHashMap实现
*
* @author xuexiang
* @since 2021/11/13 4:09 PM
*/
public class MapDataStore implements IDataStore {
private static final String TAG = TaskLogger.getLogTag("MapDataStore");
/**
* 数据存储
*/
private Map mData = new ConcurrentHashMap<>();
@Override
public Object get(String key) {
if (key == null) {
return null;
}
return mData.get(key);
}
@Override
public T getObject(String key, Class clazz) {
return CommonUtils.cast(get(key), clazz);
}
@Override
public T getObject(String key, T defaultValue) {
return CommonUtils.cast(get(key), defaultValue);
}
@Override
public String getString(String key) {
return getString(key, DEFAULT_STRING);
}
@Override
public String getString(String key, String defaultValue) {
String value = getObject(key, String.class);
return value != null ? value : defaultValue;
}
@Override
public boolean getBoolean(String key) {
return getBoolean(key, DEFAULT_BOOLEAN);
}
@Override
public boolean getBoolean(String key, boolean defaultValue) {
Boolean value = getObject(key, Boolean.class);
return value != null ? value : defaultValue;
}
@Override
public int getInt(String key) {
return getInt(key, DEFAULT_INTEGER);
}
@Override
public int getInt(String key, int defaultValue) {
Integer value = getObject(key, Integer.class);
return value != null ? value : defaultValue;
}
@Override
public Map getData() {
return mData;
}
@Override
public void clear() {
mData.clear();
}
@Override
public MapDataStore put(String key, Object value) {
if (key == null || value == null) {
TaskLogger.eTag(TAG, "put param error, key or value is null!");
return this;
}
mData.put(key, value);
return this;
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/param/impl/TaskParam.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.xtask.core.param.impl;
import com.xuexiang.xtask.core.param.IDataStore;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.utils.CommonUtils;
import java.util.Map;
/**
* 任务参数信息[数据+任务路径]
*
* @author xuexiang
* @since 2021/10/26 1:36 AM
*/
public class TaskParam implements ITaskParam {
private static final String TAG = TaskLogger.getLogTag("TaskParam");
/**
* 路径箭头
*/
private static final String PATH_ARROW = "->";
/**
* 数据存储仓库
*/
private final IDataStore mDataStore = new MapDataStore();
/**
* 任务执行路径
*/
private StringBuilder mPath = new StringBuilder();
/**
* 构建一个空的任务参数
*
* @return 任务参数
*/
public static TaskParam get() {
return new TaskParam();
}
/**
* 构建一个任务参数
*
* @param key 键
* @param value 值
* @return 任务参数
*/
public static TaskParam get(String key, Object value) {
TaskParam taskParam = new TaskParam();
taskParam.put(key, value);
return taskParam;
}
@Override
public void addPath(String path) {
mPath.append(PATH_ARROW).append(path);
}
@Override
public void addGroupPath(String path, int index, int total) {
if (index < 0 || index >= total) {
return;
}
mPath.append(PATH_ARROW);
if (index == 0) {
mPath.append("[");
}
mPath.append(path);
if (index == total - 1) {
mPath.append("]");
}
}
@Override
public String getPath() {
return mPath.toString();
}
@Override
public void updatePath(String path) {
mPath = new StringBuilder(path);
}
@Override
public IDataStore getDataStore() {
return mDataStore;
}
@Override
public void updateData(IDataStore iDataStore) {
if (iDataStore == null) {
TaskLogger.wTag(TAG, "updateData ignore, iDataStore is null!");
return;
}
if (CommonUtils.isEmpty(iDataStore.getData())) {
return;
}
for (Map.Entry entry : iDataStore.getData().entrySet()) {
put(entry.getKey(), entry.getValue());
}
}
@Override
public void updateParam(String path, IDataStore iDataStore) {
updatePath(path);
updateData(iDataStore);
}
@Override
public void updateParam(ITaskParam taskParam) {
if (taskParam == null) {
TaskLogger.wTag(TAG, "updateParam ignore, taskParam is null!");
return;
}
updateParam(taskParam.getPath(), taskParam.getDataStore());
}
@Override
public Object get(String key) {
return mDataStore.get(key);
}
@Override
public TaskParam put(String key, Object value) {
mDataStore.put(key, value);
return this;
}
@Override
public T getObject(String key, Class clazz) {
return mDataStore.getObject(key, clazz);
}
@Override
public T getObject(String key, T defaultValue) {
return mDataStore.getObject(key, defaultValue);
}
@Override
public String getString(String key) {
return mDataStore.getString(key);
}
@Override
public String getString(String key, String defaultValue) {
return mDataStore.getString(key, defaultValue);
}
@Override
public boolean getBoolean(String key) {
return mDataStore.getBoolean(key);
}
@Override
public boolean getBoolean(String key, boolean defaultValue) {
return mDataStore.getBoolean(key, defaultValue);
}
@Override
public int getInt(String key) {
return mDataStore.getInt(key);
}
@Override
public int getInt(String key, int defaultValue) {
return mDataStore.getInt(key, defaultValue);
}
@Override
public Map getData() {
return mDataStore.getData();
}
@Override
public void clear() {
mDataStore.clear();
mPath.delete(0, mPath.length());
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/param/impl/TaskResult.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.xtask.core.param.impl;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.param.ITaskResult;
import com.xuexiang.xtask.logger.TaskLogger;
/**
* 任务执行结果
*
* @author xuexiang
* @since 2021/10/27 2:11 AM
*/
public class TaskResult extends TaskParam implements ITaskResult {
private static final String TAG = TaskLogger.getLogTag("TaskResult");
/**
* 获取成功的结果
*
* @return 任务执行结果
*/
public static TaskResult succeed() {
return new TaskResult(ITaskResult.SUCCESS, "");
}
/**
* 获取失败的结果
*
* @return 任务执行结果
*/
public static TaskResult failed() {
return new TaskResult(ITaskResult.ERROR, "");
}
/**
* 获取失败的结果
*
* @param code 失败的错误码
* @return 任务执行结果
*/
public static TaskResult failed(int code) {
return new TaskResult(code, "");
}
/**
* 获取失败的结果
*
* @param code 失败的错误码
* @param message 错误信息
* @return 任务执行结果
*/
public static TaskResult failed(int code, String message) {
return new TaskResult(code, message);
}
/**
* 任务执行结果码
*/
private int mCode;
/**
* 任务执行信息
*/
private String mMessage;
/**
* 空构造方法
*/
public TaskResult() {
}
/**
* 构造方法
*
* @param taskParam 任务参数
*/
public TaskResult(@NonNull ITaskParam taskParam) {
updateParam(taskParam.getPath(), taskParam.getDataStore());
}
/**
* 构造方法
*
* @param taskResult 任务结果
*/
public TaskResult(@NonNull ITaskResult taskResult) {
saveResult(taskResult);
}
/**
* 构造方法
*
* @param code 结果码
* @param message 消息
*/
public TaskResult(int code, String message) {
setResult(code, message);
}
@Override
public int getCode() {
return mCode;
}
public TaskResult setCode(int code) {
mCode = code;
return this;
}
@Override
public String getMessage() {
return mMessage;
}
public TaskResult setMessage(String message) {
mMessage = message;
return this;
}
/**
* 设置结果
*
* @param code 结果码
* @param message 消息
*/
@Override
public void setResult(int code, String message) {
mCode = code;
mMessage = message;
}
@Override
public void saveResult(ITaskResult taskResult) {
if (taskResult == null) {
TaskLogger.eTag(TAG, "saveResult error, taskResult is null!");
return;
}
updateParam(taskResult.getPath(), taskResult.getDataStore());
setResult(taskResult.getCode(), taskResult.getMessage());
}
@Override
public void saveResultNotPath(ITaskResult taskResult) {
if (taskResult == null) {
TaskLogger.eTag(TAG, "saveResultNotPath error, taskResult is null!");
return;
}
updateData(taskResult.getDataStore());
setResult(taskResult.getCode(), taskResult.getMessage());
}
@Override
public String getDetailMessage() {
return "[code]:" + mCode + ", [msg]:" + mMessage;
}
@Override
public String toString() {
return "TaskResult{" +
"mCode=" + mCode +
", mMessage='" + mMessage + '\'' +
'}';
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/IGroupTaskStep.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.xtask.core.step;
import java.util.List;
/**
* 组任务步骤
*
* @author xuexiang
* @since 1/30/22 6:18 PM
*/
public interface IGroupTaskStep {
/**
* 增加执行任务
*
* @param taskStep 执行任务
* @return 任务链执行引擎
*/
IGroupTaskStep addTask(ITaskStep taskStep);
/**
* 增加执行任务集合
*
* @param taskStepList 执行任务集合
* @return 任务链执行引擎
*/
IGroupTaskStep addTasks(List taskStepList);
/**
* 清理任务
*/
void clearTask();
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/ITaskStep.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.xtask.core.step;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.ThreadType;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.param.impl.TaskParam;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
/**
* 任务步骤
*
* @author xuexiang
* @since 2021/10/18 9:11 PM
*/
public interface ITaskStep extends Runnable, ICancelable, ITaskStepController {
/**
* 设置任务步骤的生命周期
*
* @param taskStepLifecycle 任务步骤的生命周期
* @return 任务步骤
*/
ITaskStep setTaskStepLifecycle(@NonNull ITaskStepLifecycle taskStepLifecycle);
/**
* 设置任务处理者
*
* @param taskStepHandler 任务处理者
* @return 任务步骤
*/
ITaskStep setTaskStepHandler(@NonNull ITaskStepHandler taskStepHandler);
/**
* 设置执行的线程类型
*
* @param threadType 线程类型
* @return 任务步骤
*/
ITaskStep setThreadType(@NonNull ThreadType threadType);
/**
* 设置执行的任务参数
*
* @param taskParam 任务参数
* @return 任务步骤
*/
ITaskStep setTaskParam(@NonNull ITaskParam taskParam);
/**
* 获取执行的线程类型
*
* @return 线程类型
*/
@NonNull
ThreadType getThreadType();
/**
* 是否接收执行
*
* @return 是否执行,默认是true
*/
boolean accept();
/**
* 任务准备工作
*
* @param taskParam 任务参数
*/
void prepareTask(TaskParam taskParam);
/**
* 设置任务取消接口
*
* @param cancelable 取消接口
*/
void setCancelable(ICancelable cancelable);
/**
* 执行任务
*
* @throws Exception 异常
*/
void doTask() throws Exception;
/**
* 设置是否正在运行
*
* @param isRunning 是否正在运行
*/
void setIsRunning(boolean isRunning);
/**
* 是否正在运行
*
* @return 是否正在运行
*/
boolean isRunning();
/**
* 是否正在等待
*
* @return 是否正在等待
*/
boolean isPending();
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/ITaskStepController.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.xtask.core.step;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.param.ITaskResult;
/**
* 任务步骤执行控制器
*
* @author xuexiang
* @since 1/30/22 6:33 PM
*/
public interface ITaskStepController {
/**
* 获取任务步骤名称
*
* @return 任务步骤的名称
*/
String getName();
/**
* 获取任务的参数
*
* @return 任务参数
*/
@NonNull
ITaskParam getTaskParam();
/**
* 通知任务链任务步骤执行完毕
*
* @param result 任务执行结果
*/
void notifyTaskSucceed(@NonNull ITaskResult result);
/**
* 通知任务链执行发生异常
*
* @param result 任务执行结果
*/
void notifyTaskFailed(@NonNull ITaskResult result);
/**
* 资源释放
*/
void recycle();
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/ITaskStepHandler.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.xtask.core.step;
import androidx.annotation.NonNull;
/**
* 任务处理者
*
* @author xuexiang
* @since 2021/10/18 9:10 PM
*/
public interface ITaskStepHandler {
/**
* 任务执行之前的处理
*
* @param step 任务
*/
void beforeTask(@NonNull ITaskStep step);
/**
* 任务执行完毕的处理
*
* @param step 任务
*/
void afterTask(@NonNull ITaskStep step);
/**
* 任务执行发生异常
*
* @param step 任务
* @param exception 异常
*/
void onTaskException(@NonNull ITaskStep step, Exception exception);
/**
* 是否接收执行任务
*
* @param step 任务
* @return true:执行;false:不执行
*/
boolean accept(@NonNull ITaskStep step);
/**
* 任务执行完成的处理
*
* @param step 任务
*/
void handleTaskSucceed(@NonNull ITaskStep step);
/**
* 任务执行失败的处理
*
* @param step 任务
*/
void handleTaskFailed(@NonNull ITaskStep step);
/**
* 任务执行被取消的处理
*
* @param step 任务
*/
void handleTaskCancelled(@NonNull ITaskStep step);
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/ITaskStepLifecycle.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.xtask.core.step;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.param.ITaskResult;
/**
* 任务步骤的生命周期管理
*
* @author xuexiang
* @since 1/30/22 4:51 PM
*/
public interface ITaskStepLifecycle {
/**
* 任务步骤执行完毕
*
* @param step 任务步骤
* @param result 任务执行结果
*/
void onTaskStepCompleted(@NonNull ITaskStep step, @NonNull ITaskResult result);
/**
* 任务步骤执行发生异常
*
* @param step 任务步骤
* @param result 任务执行结果
*/
void onTaskStepError(@NonNull ITaskStep step, @NonNull ITaskResult result);
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/impl/AbstractGroupTaskStep.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.xtask.core.step.impl;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.ThreadType;
import com.xuexiang.xtask.core.param.ITaskParam;
import com.xuexiang.xtask.core.param.impl.TaskParam;
import com.xuexiang.xtask.core.param.impl.TaskResult;
import com.xuexiang.xtask.core.step.IGroupTaskStep;
import com.xuexiang.xtask.core.step.ITaskStep;
import com.xuexiang.xtask.core.step.ITaskStepLifecycle;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.utils.CommonUtils;
import com.xuexiang.xtask.utils.TaskUtils;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 抽象任务组(不进行具体的任务)
*
* @author xuexiang
* @since 2/1/22 11:27 PM
*/
public abstract class AbstractGroupTaskStep extends AbstractTaskStep implements ITaskStepLifecycle, IGroupTaskStep {
private static final String TAG = TaskLogger.getLogTag("AbstractGroupTaskStep");
/**
* 任务组的编号【静态全局】
*/
private static final AtomicInteger GROUP_TASK_NUMBER = new AtomicInteger(1);
/**
* 任务组名称
*/
private final String mName;
/**
* 任务结果
*/
private final TaskResult mResult = new TaskResult();
/**
* 执行任务集合
*/
private final List mTasks = new CopyOnWriteArrayList<>();
/**
* 任务执行索引
*/
protected AtomicInteger mTaskIndex = new AtomicInteger(0);
/**
* 任务执行总数
*/
protected int mTaskTotalSize;
/**
* 构造方法
*/
public AbstractGroupTaskStep() {
mName = generationGroupName();
}
/**
* 构造方法
*
* @param name 任务组名称
*/
public AbstractGroupTaskStep(@NonNull String name) {
mName = name;
}
/**
* 构造方法
*
* @param threadType 线程类型
*/
public AbstractGroupTaskStep(@NonNull ThreadType threadType) {
super(threadType);
mName = generationGroupName();
}
/**
* 构造方法
*
* @param name 任务组名称
* @param taskParam 任务参数
*/
public AbstractGroupTaskStep(@NonNull String name, @NonNull ITaskParam taskParam) {
mName = name;
setTaskParam(taskParam);
}
/**
* 构造方法
*
* @param name 任务组名称
* @param threadType 线程类型
*/
public AbstractGroupTaskStep(@NonNull String name, @NonNull ThreadType threadType) {
super(threadType);
mName = name;
}
@Override
public AbstractGroupTaskStep addTask(ITaskStep taskStep) {
if (taskStep != null) {
taskStep.setTaskStepLifecycle(this);
mTasks.add(taskStep);
}
return this;
}
@Override
public AbstractGroupTaskStep addTasks(List taskStepList) {
if (!CommonUtils.isEmpty(taskStepList)) {
for (ITaskStep taskStep : taskStepList) {
addTask(taskStep);
}
}
return this;
}
@Override
public AbstractGroupTaskStep setTaskParam(@NonNull ITaskParam taskParam) {
super.setTaskParam(taskParam);
mResult.updateParam(taskParam);
return this;
}
@Override
public AbstractGroupTaskStep setThreadType(@NonNull ThreadType threadType) {
super.setThreadType(threadType);
return this;
}
/**
* 初始化组任务
*/
protected void initGroupTask() {
mTaskTotalSize = TaskUtils.findTaskStepSize(getTasks());
mTaskIndex.set(0);
TaskLogger.dTag(TAG, getTaskLogName() + " initGroupTask, task total size:" + mTaskTotalSize);
}
@Override
public void prepareTask(TaskParam taskParam) {
super.prepareTask(taskParam);
mResult.updateParam(taskParam);
}
@Override
public void clearTask() {
if (CommonUtils.isEmpty(mTasks)) {
return;
}
for (ITaskStep taskStep : mTasks) {
taskStep.recycle();
}
mTasks.clear();
}
@Override
public void recycle() {
mResult.clear();
clearTask();
super.recycle();
}
@Override
public void cancel() {
if (isCancelled()) {
return;
}
for (ITaskStep taskStep : mTasks) {
taskStep.cancel();
}
super.cancel();
}
@NonNull
@Override
public ITaskParam getTaskParam() {
return mResult;
}
@Override
public String getName() {
return mName;
}
public List getTasks() {
return mTasks;
}
public TaskResult getResult() {
return mResult;
}
@Override
protected String getTaskLogName() {
return "Group task step [" + getName() + "]";
}
/**
* 自动生成组名
*
* @return 组名
*/
@NonNull
private String generationGroupName() {
return "GroupTaskStep-" + GROUP_TASK_NUMBER.getAndIncrement();
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/impl/AbstractTaskStep.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.xtask.core.step.impl;
import androidx.annotation.NonNull;
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.ITaskStep;
import com.xuexiang.xtask.core.step.ITaskStepHandler;
import com.xuexiang.xtask.core.step.ITaskStepLifecycle;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 抽象任务执行步骤
*
* @author xuexiang
* @since 2021/11/2 1:22 AM
*/
public abstract class AbstractTaskStep implements ITaskStep {
private static final String TAG = TaskLogger.getLogTag("AbstractTaskStep");
/**
* 是否正在等待
*/
private final AtomicBoolean mIsPending = new AtomicBoolean(true);
/**
* 是否正在运行
*/
private final AtomicBoolean mIsRunning = new AtomicBoolean(false);
/**
* 是否取消
*/
private final AtomicBoolean mIsCancelled = new AtomicBoolean(false);
/**
* 是否已经通知执行结果
*/
private final AtomicBoolean mIsNotified = new AtomicBoolean(false);
/**
* 任务步骤的生命周期管理
*/
private ITaskStepLifecycle mTaskStepLifecycle;
/**
* 线程执行类型
*/
private ThreadType mThreadType;
/**
* 任务参数
*/
@NonNull
private ITaskParam mTaskParam;
/**
* 任务处理
*/
private ITaskStepHandler mTaskHandler;
/**
* 任务取消接口
*/
private ICancelable mCancelable;
/**
* 构造方法
*/
public AbstractTaskStep() {
this(ThreadType.ASYNC, TaskParam.get());
}
/**
* 构造方法
*
* @param threadType 线程类型
*/
public AbstractTaskStep(ThreadType threadType) {
this(threadType, TaskParam.get());
}
/**
* 构造方法
*
* @param taskParam 任务参数
*/
public AbstractTaskStep(@NonNull ITaskParam taskParam) {
this(ThreadType.ASYNC, taskParam);
}
/**
* 构造方法
*
* @param threadType 线程类型
* @param taskParam 任务参数
*/
public AbstractTaskStep(ThreadType threadType, @NonNull ITaskParam taskParam) {
mThreadType = threadType;
mTaskParam = taskParam;
}
@Override
public AbstractTaskStep setTaskStepLifecycle(@NonNull ITaskStepLifecycle taskStepLifecycle) {
mTaskStepLifecycle = taskStepLifecycle;
return this;
}
@Override
public AbstractTaskStep setTaskStepHandler(@NonNull ITaskStepHandler taskStepHandler) {
mTaskHandler = taskStepHandler;
return this;
}
@Override
public AbstractTaskStep setThreadType(@NonNull ThreadType threadType) {
mThreadType = threadType;
return this;
}
@Override
public AbstractTaskStep setTaskParam(@NonNull ITaskParam taskParam) {
mTaskParam = taskParam;
return this;
}
@NonNull
@Override
public ThreadType getThreadType() {
return mThreadType;
}
@NonNull
@Override
public ITaskParam getTaskParam() {
return mTaskParam;
}
@Override
public void setCancelable(ICancelable cancelable) {
mCancelable = cancelable;
}
@Override
public void setIsRunning(boolean isRunning) {
mIsRunning.set(isRunning);
}
@Override
public boolean isRunning() {
return mIsRunning.get();
}
@Override
public boolean isPending() {
return mIsPending.get();
}
@Override
public boolean accept() {
if (mTaskHandler != null) {
return mTaskHandler.accept(this);
}
return true;
}
@Override
public void prepareTask(TaskParam taskParam) {
getTaskParam().updateParam(taskParam);
}
@Override
public void notifyTaskSucceed(@NonNull ITaskResult result) {
if (mIsNotified.get()) {
TaskLogger.wTag(TAG, getTaskLogName() + " has notified!");
return;
}
mIsNotified.set(true);
mIsRunning.set(false);
if (isCancelled()) {
TaskLogger.wTag(TAG, getTaskLogName() + " has cancelled!");
return;
}
TaskLogger.dTag(TAG, getTaskLogName() + " succeed!");
if (mTaskHandler != null) {
mTaskHandler.handleTaskSucceed(this);
}
if (mTaskStepLifecycle != null) {
result.updateParam(getTaskParam());
mTaskStepLifecycle.onTaskStepCompleted(this, result);
}
}
@Override
public void notifyTaskFailed(@NonNull ITaskResult result) {
if (mIsNotified.get()) {
TaskLogger.wTag(TAG, getTaskLogName() + " has notified!");
return;
}
mIsNotified.set(true);
mIsRunning.set(false);
TaskLogger.eTag(TAG, getTaskLogName() + " failed, " + result.getDetailMessage());
if (mTaskHandler != null) {
mTaskHandler.handleTaskFailed(this);
}
if (mTaskStepLifecycle != null) {
result.updateParam(getTaskParam());
mTaskStepLifecycle.onTaskStepError(this, result);
}
}
@Override
public void recycle() {
TaskLogger.dTag(TAG, getTaskLogName() + " recycle...");
if (isRunning() && !isCancelled()) {
cancel();
}
mTaskParam.clear();
mTaskStepLifecycle = null;
mTaskHandler = null;
mCancelable = null;
}
@Override
public void cancel() {
if (isCancelled()) {
return;
}
if (isPending() || isRunning()) {
TaskLogger.dTag(TAG, getTaskLogName() + " cancel...");
}
if (mCancelable != null) {
mCancelable.cancel();
}
mIsCancelled.set(true);
if (mTaskHandler != null) {
mTaskHandler.handleTaskCancelled(this);
}
}
@Override
public boolean isCancelled() {
return mIsCancelled.get();
}
@Override
public void run() {
if (isCancelled()) {
TaskLogger.wTag(TAG, getTaskLogName() + " has cancelled, do not need to run!");
return;
}
setIsRunning(true);
try {
processTask();
} catch (Exception e) {
TaskLogger.eTag(TAG, getTaskLogName() + " has error!", e);
if (mTaskHandler != null) {
mTaskHandler.onTaskException(this, e);
}
}
}
/**
* 执行任务
*
* @throws Exception 异常
*/
protected void processTask() throws Exception {
updateProcessTaskPath();
if (mTaskHandler != null) {
mTaskHandler.beforeTask(this);
}
if (isRunning()) {
doTask();
}
if (mTaskHandler != null) {
mTaskHandler.afterTask(this);
}
}
/**
* 更新任务处理的路径
*/
private void updateProcessTaskPath() {
getTaskParam().addPath(getName());
TaskLogger.dTag(TAG, getTaskRunningDetailLogInfo());
mIsPending.set(false);
}
private String getTaskRunningDetailLogInfo() {
if (TaskLogger.isLogThreadName()) {
return getTaskLogName() + " has run, thread: " + Thread.currentThread().getName() + ", path:" + getTaskParam().getPath();
} else {
return getTaskLogName() + " has run, path: " + getTaskParam().getPath();
}
}
/**
* 获取任务的日志名称
*
* @return 任务的日志名称
*/
protected String getTaskLogName() {
return "Task step [" + getName() + "]";
}
// ==================默认提供的通知方法=========================//
/**
* 通知任务链任务步骤执行完毕
*/
public void notifyTaskSucceed() {
notifyTaskSucceed(TaskResult.succeed());
}
/**
* 通知任务链任务步骤执行失败
*/
public void notifyTaskFailed() {
notifyTaskFailed(TaskResult.failed());
}
/**
* 通知任务链任务步骤执行失败
*
* @param code 失败的错误码
*/
public void notifyTaskFailed(int code) {
notifyTaskFailed(TaskResult.failed(code));
}
/**
* 通知任务链任务步骤执行失败
*
* @param code 失败的错误码
* @param message 错误信息
*/
public void notifyTaskFailed(int code, String message) {
notifyTaskFailed(TaskResult.failed(code, message));
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/impl/AutoDestroyTaskChainCallback.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.xtask.core.step.impl;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.ITaskChainCallback;
import com.xuexiang.xtask.core.ITaskChainEngine;
import com.xuexiang.xtask.core.param.ITaskResult;
/**
* 自动销毁任务链的任务链执行回调
*
* @author xuexiang
* @since 2/10/22 10:26 PM
*/
public class AutoDestroyTaskChainCallback implements ITaskChainCallback {
@Override
public boolean isCallBackOnMainThread() {
return false;
}
@Override
public void onTaskChainStart(@NonNull ITaskChainEngine engine) {
}
@Override
public void onTaskChainCompleted(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
engine.destroy();
}
@Override
public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
}
@Override
public void onTaskChainCancelled(@NonNull ITaskChainEngine engine) {
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/impl/AutoNotifyTaskStepHandler.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.xtask.core.step.impl;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.param.ITaskResult;
import com.xuexiang.xtask.core.param.impl.TaskResult;
import com.xuexiang.xtask.core.step.ITaskStep;
import com.xuexiang.xtask.core.step.ITaskStepHandler;
/**
* 自动通知任务执行结果处理者
*
* @author xuexiang
* @since 2021/11/2 1:48 AM
*/
public class AutoNotifyTaskStepHandler implements ITaskStepHandler {
@Override
public void beforeTask(@NonNull ITaskStep step) {
}
@Override
public void afterTask(@NonNull ITaskStep step) {
step.notifyTaskSucceed(TaskResult.succeed());
}
@Override
public void onTaskException(@NonNull ITaskStep step, Exception exception) {
step.notifyTaskFailed(TaskResult.failed(ITaskResult.PROCESS_TASK_THROW_EXCEPTION, exception.getMessage()));
}
@Override
public boolean accept(@NonNull ITaskStep step) {
return true;
}
@Override
public void handleTaskSucceed(@NonNull ITaskStep step) {
}
@Override
public void handleTaskFailed(@NonNull ITaskStep step) {
}
@Override
public void handleTaskCancelled(@NonNull ITaskStep step) {
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/impl/TaskChainCallbackAdapter.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.xtask.core.step.impl;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.core.ITaskChainCallback;
import com.xuexiang.xtask.core.ITaskChainEngine;
import com.xuexiang.xtask.core.param.ITaskResult;
/**
* 任务链执行回调适配器【默认回主线程的回调】
*
* @author xuexiang
* @since 1/31/22 9:11 PM
*/
public abstract class TaskChainCallbackAdapter implements ITaskChainCallback {
@Override
public boolean isCallBackOnMainThread() {
return true;
}
@Override
public void onTaskChainStart(@NonNull ITaskChainEngine engine) {
}
@Override
public void onTaskChainError(@NonNull ITaskChainEngine engine, @NonNull ITaskResult result) {
}
@Override
public void onTaskChainCancelled(@NonNull ITaskChainEngine engine) {
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/core/step/impl/TaskCommand.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.xtask.core.step.impl;
import androidx.annotation.NonNull;
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.ITaskStepController;
/**
* 任务命令,执行简单任务
*
* @author xuexiang
* @since 1/31/22 2:28 AM
*/
public abstract class TaskCommand implements ITaskStepController {
/**
* 任务步骤执行控制器
*/
private ITaskStepController mController;
/**
* 设置任务步骤执行控制器
*
* @param controller 控制器
* @return this
*/
public TaskCommand setTaskStepResultController(ITaskStepController controller) {
mController = controller;
return this;
}
/**
* 通知任务链任务步骤执行完毕
*/
public void notifyTaskSucceed() {
notifyTaskSucceed(TaskResult.succeed());
}
/**
* 通知任务链任务步骤执行失败
*/
public void notifyTaskFailed() {
notifyTaskFailed(TaskResult.failed());
}
/**
* 通知任务链任务步骤执行失败
*
* @param code 失败的错误码
*/
public void notifyTaskFailed(int code) {
notifyTaskFailed(TaskResult.failed(code));
}
/**
* 通知任务链任务步骤执行失败
*
* @param code 失败的错误码
* @param message 错误信息
*/
public void notifyTaskFailed(int code, String message) {
notifyTaskFailed(TaskResult.failed(code, message));
}
@Override
public void notifyTaskSucceed(@NonNull ITaskResult result) {
if (mController != null) {
mController.notifyTaskSucceed(result);
}
}
@Override
public void notifyTaskFailed(@NonNull ITaskResult result) {
if (mController != null) {
mController.notifyTaskFailed(result);
}
}
@Override
public String getName() {
return mController != null ? mController.getName() : "unknown";
}
@NonNull
@Override
public ITaskParam getTaskParam() {
return mController != null ? mController.getTaskParam() : TaskParam.get();
}
/**
* 执行任务
*
* @throws Exception 异常
*/
public abstract void run() throws Exception;
@Override
public void recycle() {
if (mController != null) {
mController.recycle();
}
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/utils/CancellerPoolUtils.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.xtask.utils;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.pool.cancel.ICancellerPool;
import com.xuexiang.xtask.thread.pool.cancel.TaskCancellerPool;
import java.util.Collection;
/**
* 取消者订阅池工具类
*
* @author xuexiang
* @since 2/7/22 1:17 PM
*/
public final class CancellerPoolUtils {
private static ICancellerPool sCancellerPool = new TaskCancellerPool();
private CancellerPoolUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 设置自定义的取消者订阅池
*
* @param sCancellerPool 取消者订阅池
*/
public static void setCancellerPool(ICancellerPool sCancellerPool) {
CancellerPoolUtils.sCancellerPool = sCancellerPool;
}
/**
* 添加取消者
*
* @param name 取消者名称
* @param cancelable 取消接口
* @return 是否执行成功
*/
public static boolean add(String name, ICancelable cancelable) {
return sCancellerPool.add(name, cancelable);
}
/**
* 去除取消者
*
* @param name 取消者名称
* @return 是否执行成功
*/
public static boolean remove(String name) {
return sCancellerPool.remove(name);
}
/**
* 指定取消者执行
*
* @param name 取消者名称
* @return 是否执行成功
*/
public static boolean cancel(String name) {
return sCancellerPool.cancel(name);
}
/**
* 指定取消者集合执行
*
* @param names 取消者集合
*/
public static void cancel(String... names) {
sCancellerPool.cancel(names);
}
/**
* 指定取消者集合执行
*
* @param names 取消者集合
*/
public static void cancel(Collection names) {
sCancellerPool.cancel(names);
}
/**
* 所有取消者执行
*/
public static void cancelAll() {
sCancellerPool.cancelAll();
}
/**
* 清除所有
*
* @param ifNeedCancel 是否在清除前取消
*/
public static void clear(boolean ifNeedCancel) {
sCancellerPool.clear(ifNeedCancel);
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/utils/CommonUtils.java
================================================
/*
* Copyright (C) 2021 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.xtask.utils;
import com.xuexiang.xtask.logger.TaskLogger;
import java.util.Collection;
import java.util.Map;
/**
* 普通工具类
*
* @author xuexiang
* @since 2021/10/26 1:47 AM
*/
public final class CommonUtils {
private static final String TAG = TaskLogger.getLogTag("CommonUtils");
private CommonUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 类型强转
*
* @param object 需要强转的对象
* @param clazz 需要强转的类型
* @param
* @return 类型强转结果
*/
public static T cast(final Object object, Class clazz) {
return clazz != null && clazz.isInstance(object) ? (T) object : null;
}
/**
* 类型强转
*
* @param object 需要强转的对象
* @param defaultValue 强转的默认值
* @param
* @return 类型强转结果
*/
public static T cast(Object object, T defaultValue) {
if (defaultValue == null) {
return null;
} else if (object == null) {
return null;
} else {
return defaultValue.getClass() == object.getClass() ? (T) object : defaultValue;
}
}
/**
* 判断字符串是否为 null 或长度为 0
*
* @param s 待校验字符串
* @return {@code true}: 空 {@code false}: 不为空
*/
public static boolean isEmpty(final CharSequence s) {
return s == null || s.length() == 0;
}
/**
* 集合是否为空
*
* @param collection 集合
* @return true: 为空,false:不为空
*/
public static boolean isEmpty(final Collection collection) {
return collection == null || collection.isEmpty();
}
/**
* Map是否为空
*
* @param obj Map
* @return true: 为空,false:不为空
*/
public static boolean isEmpty(final Map obj) {
return obj == null || obj.isEmpty();
}
/**
* 不能为null
*
* @param object 对象
* @param message 错误信息
* @param
* @return
*/
public static T requireNonNull(final T object, final String message) {
if (object == null) {
throw new NullPointerException(message);
}
return object;
}
/**
* 获取集合的大小
*
* @param collection 集合
* @return 集合的大小
*/
public static int getSize(final Collection collection) {
return collection != null ? collection.size() : 0;
}
}
================================================
FILE: xtask-core/src/main/java/com/xuexiang/xtask/utils/TaskUtils.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.xtask.utils;
import android.os.Looper;
import com.xuexiang.xtask.core.ThreadType;
import com.xuexiang.xtask.core.step.ITaskStep;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.XTaskExecutor;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import java.util.List;
/**
* XTask内部工具类
*
* @author xuexiang
* @since 1/31/22 8:40 PM
*/
public final class TaskUtils {
private static final String TAG = TaskLogger.getLogTag("TaskUtils");
private TaskUtils() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
/**
* 是否是主线程
*
* @return 是否是主线程
*/
public static boolean isMainThread() {
return Looper.getMainLooper() == Looper.myLooper();
}
/**
* 切到主线程运行
*
* @param runnable 命令
*/
public static void runOnMainThread(Runnable runnable) {
XTaskExecutor.get().postToMain(runnable);
}
/**
* 查找下一条需要执行的任务
*
* @param taskStepList 执行任务集合
* @param taskStep 当前任务
* @return 下一条执行任务
*/
public static ITaskStep findNextTaskStep(List taskStepList, ITaskStep taskStep) {
if (CommonUtils.isEmpty(taskStepList)) {
return null;
}
int index = 0;
if (taskStep != null) {
index = taskStepList.indexOf(taskStep) + 1;
}
for (; index < taskStepList.size(); index++) {
ITaskStep target = taskStepList.get(index);
if (target != null && target.accept()) {
return target;
}
}
return null;
}
/**
* 查找需要执行的任务总数
*
* @param taskStepList 执行任务集合
* @return 需要执行的任务总数
*/
public static int findTaskStepSize(List taskStepList) {
int count = 0;
if (!CommonUtils.isEmpty(taskStepList)) {
for (ITaskStep taskStep: taskStepList) {
if (taskStep != null && taskStep.accept()) {
count++;
}
}
}
return count;
}
/**
* 执行任务
*
* @param taskStep 需要执行的任务
* @return 取消接口
*/
public static ICancelable executeTask(ITaskStep taskStep) {
if (taskStep == null) {
TaskLogger.eTag(TAG, "execute task failed, taskStep is null!");
return null;
}
ThreadType type = taskStep.getThreadType();
if (type == ThreadType.MAIN) {
XTaskExecutor.get().postToMain(taskStep);
return null;
} else if (type == ThreadType.ASYNC_EMERGENT) {
return XTaskExecutor.get().emergentSubmit(taskStep);
} else if (type == ThreadType.ASYNC) {
return XTaskExecutor.get().submit(taskStep);
} else if (type == ThreadType.ASYNC_IO) {
return XTaskExecutor.get().ioSubmit(taskStep);
} else if (type == ThreadType.ASYNC_BACKGROUND) {
return XTaskExecutor.get().backgroundSubmit(taskStep);
} else {
taskStep.run();
return null;
}
}
}
================================================
FILE: xtask-thread/.gitignore
================================================
/build
================================================
FILE: xtask-thread/build.gradle
================================================
apply plugin: 'com.android.library'
android {
compileSdkVersion build_versions.target_sdk
buildToolsVersion build_versions.build_tools
defaultConfig {
minSdkVersion 14
targetSdkVersion build_versions.target_sdk
}
lintOptions {
abortOnError false
}
}
dependencies {
compileOnly deps.androidx.appcompat
}
apply from: '../JitPackUpload.gradle'
================================================
FILE: xtask-thread/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: xtask-thread/src/main/AndroidManifest.xml
================================================
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/logger/ILogger.java
================================================
/*
* Copyright (C) 2021 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.xtask.logger;
/**
* 日志记录接口
*
* @author xuexiang
* @since 2021/10/9 4:33 PM
*/
public interface ILogger {
/**
* 打印信息
*
* @param priority 优先级
* @param tag 标签
* @param message 信息
* @param t 出错信息
*/
void log(int priority, String tag, String message, Throwable t);
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/logger/LogcatLogger.java
================================================
/*
* Copyright (C) 2021 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.xtask.logger;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* 默认Logcat日志记录
*
* @author xuexiang
* @since 2021/10/9 4:29 PM
*/
public class LogcatLogger implements ILogger {
/**
* logcat里日志的最大长度.
*/
private static final int MAX_LOG_LENGTH = 4000;
/**
* 打印信息
*
* @param priority 优先级
* @param tag 标签
* @param message 信息
* @param t 出错信息
*/
@Override
public void log(int priority, String tag, String message, Throwable t) {
if (message != null && message.length() == 0) {
message = null;
}
if (message == null) {
if (t == null) {
return; // Swallow message if it's null and there's no throwable.
}
message = getStackTraceString(t);
} else {
if (t != null) {
message += "\n" + getStackTraceString(t);
}
}
log(priority, tag, message);
}
public static String getStackTraceString(Throwable t) {
// Don't replace this with Log.getStackTraceString() - it hides
// UnknownHostException, which is not what we want.
StringWriter sw = new StringWriter(256);
PrintWriter pw = new PrintWriter(sw, false);
t.printStackTrace(pw);
pw.flush();
return sw.toString();
}
/**
* 使用LogCat输出日志,字符长度超过4000则自动换行.
*
* @param priority 优先级
* @param tag 标签
* @param message 信息
*/
public void log(int priority, String tag, String message) {
int subNum = message.length() / MAX_LOG_LENGTH;
if (subNum > 0) {
int index = 0;
for (int i = 0; i < subNum; i++) {
int lastIndex = index + MAX_LOG_LENGTH;
String sub = message.substring(index, lastIndex);
logSub(priority, tag, sub);
index = lastIndex;
}
logSub(priority, tag, message.substring(index, message.length()));
} else {
logSub(priority, tag, message);
}
}
/**
* 使用LogCat输出日志.
*
* @param priority 优先级
* @param tag 标签
* @param sub 信息
*/
private void logSub(int priority, @NonNull String tag, @NonNull String sub) {
switch (priority) {
case Log.VERBOSE:
Log.v(tag, sub);
break;
case Log.DEBUG:
Log.d(tag, sub);
break;
case Log.INFO:
Log.i(tag, sub);
break;
case Log.WARN:
Log.w(tag, sub);
break;
case Log.ERROR:
Log.e(tag, sub);
break;
case Log.ASSERT:
Log.wtf(tag, sub);
break;
default:
Log.v(tag, sub);
break;
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/logger/TaskLogger.java
================================================
/*
* Copyright (C) 2021 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.xtask.logger;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
/**
* TaskLogger日志记录
*
* @author xuexiang
* @since 2021/10/9 4:31 PM
*/
public final class TaskLogger {
private TaskLogger() {
throw new UnsupportedOperationException("u can't instantiate me...");
}
//==============常量================//
/**
* 默认tag
*/
public final static String TASK_LOG_TAG = "TaskLogger";
/**
* 最大日志优先级【日志优先级为最大等级,所有日志都不打印】
*/
private final static int MAX_LOG_PRIORITY = 10;
/**
* 最小日志优先级【日志优先级为最小等级,所有日志都打印】
*/
private final static int MIN_LOG_PRIORITY = 0;
//==============属性================//
/**
* 默认的日志记录为Logcat
*/
private static ILogger sILogger = new LogcatLogger();
private static String sTag = TASK_LOG_TAG;
/**
* 是否是调试模式
*/
private static boolean sIsDebug = false;
/**
* 日志打印优先级
*/
private static int sLogPriority = MAX_LOG_PRIORITY;
/**
* 是否打印任务执行所在的线程名
*/
private static boolean sIsLogThreadName = false;
//==============属性设置================//
/**
* 获取类的打印日志TAG
*
* @param clazz 类
* @return 打印日志TAG
*/
public static String getLogTag(@NonNull Class> clazz) {
return sTag + "_" + clazz.getSimpleName();
}
/**
* 获取类的打印日志TAG
*
* @param className 类名
* @return 打印日志TAG
*/
public static String getLogTag(@NonNull String className) {
return sTag + "_" + className;
}
/**
* 设置日志记录者的接口
*
* @param logger 日志记录接口
*/
public static void setLogger(@NonNull ILogger logger) {
TaskLogger.sILogger = logger;
}
/**
* 设置日志的tag
*
* @param tag 日志的tag
*/
public static void setTag(String tag) {
TaskLogger.sTag = tag;
}
/**
* 设置是否是调试模式
*
* @param isDebug 是否是调试模式
*/
public static void setDebug(boolean isDebug) {
TaskLogger.sIsDebug = isDebug;
}
/**
* 设置打印日志的等级(只打印改等级以上的日志)
*
* @param priority 日志的等级
*/
public static void setPriority(int priority) {
TaskLogger.sLogPriority = priority;
}
/**
* 设置是否打印任务执行所在的线程名
*
* @param isLogThreadName 是否打印任务执行所在的线程名
*/
public static void setIsLogThreadName(boolean isLogThreadName) {
TaskLogger.sIsLogThreadName = isLogThreadName;
}
/**
* 是否打印任务执行所在的线程名
*
* @return 是否打印任务执行所在的线程名
*/
public static boolean isLogThreadName() {
return sIsLogThreadName;
}
//===================对外接口=======================//
/**
* 设置是否打开调试
*
* @param isDebug 是否是调试模式
*/
public static void debug(boolean isDebug) {
debug(isDebug ? TASK_LOG_TAG : "");
}
/**
* 设置调试模式
*
* @param tag tag,如果不为空是调试模式
*/
public static void debug(String tag) {
if (!TextUtils.isEmpty(tag)) {
setDebug(true);
setPriority(MIN_LOG_PRIORITY);
setTag(tag);
} else {
setDebug(false);
setPriority(MAX_LOG_PRIORITY);
setTag("");
}
}
//=============打印方法===============//
/**
* 打印任何(所有)信息
*
* @param msg 日志信息
*/
public static void v(String msg) {
if (enableLog(Log.VERBOSE)) {
sILogger.log(Log.VERBOSE, sTag, msg, null);
}
}
/**
* 打印任何(所有)信息
*
* @param tag tag信息
* @param msg 日志信息
*/
public static void vTag(String tag, String msg) {
if (enableLog(Log.VERBOSE)) {
sILogger.log(Log.VERBOSE, tag, msg, null);
}
}
/**
* 打印调试信息
*
* @param msg 调试信息
*/
public static void d(String msg) {
if (enableLog(Log.DEBUG)) {
sILogger.log(Log.DEBUG, sTag, msg, null);
}
}
/**
* 打印调试信息
*
* @param tag tag信息
* @param msg 调试信息
*/
public static void dTag(String tag, String msg) {
if (enableLog(Log.DEBUG)) {
sILogger.log(Log.DEBUG, tag, msg, null);
}
}
/**
* 打印提示性的信息
*
* @param msg 提示性的信息
*/
public static void i(String msg) {
if (enableLog(Log.INFO)) {
sILogger.log(Log.INFO, sTag, msg, null);
}
}
/**
* 打印提示性的信息
*
* @param tag tag信息
* @param msg 提示性的信息
*/
public static void iTag(String tag, String msg) {
if (enableLog(Log.INFO)) {
sILogger.log(Log.INFO, tag, msg, null);
}
}
/**
* 打印warning警告信息
*
* @param msg 警告信息
*/
public static void w(String msg) {
if (enableLog(Log.WARN)) {
sILogger.log(Log.WARN, sTag, msg, null);
}
}
/**
* 打印warning警告信息
*
* @param tag tag信息
* @param msg 警告信息
*/
public static void wTag(String tag, String msg) {
if (enableLog(Log.WARN)) {
sILogger.log(Log.WARN, tag, msg, null);
}
}
/**
* 打印出错信息
*
* @param msg 出错信息
*/
public static void e(String msg) {
if (enableLog(Log.ERROR)) {
sILogger.log(Log.ERROR, sTag, msg, null);
}
}
/**
* 打印出错信息
*
* @param tag tag信息
* @param msg 出错信息
*/
public static void eTag(String tag, String msg) {
if (enableLog(Log.ERROR)) {
sILogger.log(Log.ERROR, tag, msg, null);
}
}
/**
* 打印出错堆栈信息
*
* @param t 出错堆栈信息
*/
public static void e(Throwable t) {
if (enableLog(Log.ERROR)) {
sILogger.log(Log.ERROR, sTag, null, t);
}
}
/**
* 打印出错堆栈信息
*
* @param tag tag信息
* @param t 出错堆栈信息
*/
public static void eTag(String tag, Throwable t) {
if (enableLog(Log.ERROR)) {
sILogger.log(Log.ERROR, tag, null, t);
}
}
/**
* 打印出错堆栈信息
*
* @param msg 出错信息
* @param t 出错堆栈信息
*/
public static void e(String msg, Throwable t) {
if (enableLog(Log.ERROR)) {
sILogger.log(Log.ERROR, sTag, msg, t);
}
}
/**
* 打印出错堆栈信息
*
* @param tag tag信息
* @param msg 出错堆栈信息
* @param t 出错堆栈信息
*/
public static void eTag(String tag, String msg, Throwable t) {
if (enableLog(Log.ERROR)) {
sILogger.log(Log.ERROR, tag, msg, t);
}
}
/**
* 打印严重的错误信息
*
* @param msg 严重的错误信息
*/
public static void wtf(String msg) {
if (enableLog(Log.ASSERT)) {
sILogger.log(Log.ASSERT, sTag, msg, null);
}
}
/**
* 打印严重的错误信息
*
* @param tag tag信息
* @param msg 严重的错误信息
*/
public static void wtfTag(String tag, String msg) {
if (enableLog(Log.ASSERT)) {
sILogger.log(Log.ASSERT, tag, msg, null);
}
}
/**
* 打印日志
*
* @param tag tag信息
* @param msg 日志信息
*/
public static void log(int priority, String tag, String msg) {
if (enableLog(priority)) {
sILogger.log(priority, tag, msg, null);
}
}
/**
* 能否打印
*
* @param logPriority 日志等级
* @return 能否打印日志
*/
private static boolean enableLog(int logPriority) {
return isDebug() && logPriority >= sLogPriority;
}
/**
* 当前是否是调试模式
*
* @return 是否是调试模式
*/
public static boolean isDebug() {
return sILogger != null && sIsDebug;
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/XTaskExecutor.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.thread.executor.ICategoryExecutorCore;
import com.xuexiang.xtask.thread.executor.IPriorityExecutorCore;
import com.xuexiang.xtask.thread.executor.IScheduledExecutorCore;
import com.xuexiang.xtask.thread.executor.impl.CategoryExecutorCore;
import com.xuexiang.xtask.thread.executor.impl.PriorityExecutorCore;
import com.xuexiang.xtask.thread.executor.impl.ScheduledExecutorCore;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import java.util.concurrent.TimeUnit;
/**
* XTask的执行者
*
* @author xuexiang
* @since 2021/10/9 2:30 AM
*/
public class XTaskExecutor implements IPriorityExecutorCore, ICategoryExecutorCore, IScheduledExecutorCore {
private static volatile XTaskExecutor sInstance = null;
/**
* 优先级执行内核实现接口
*/
private IPriorityExecutorCore mPriorityExecutorCore;
/**
* 分类执行内核实现接口
*/
private ICategoryExecutorCore mCategoryExecutorCore;
/**
* 周期执行内核实现接口
*/
private IScheduledExecutorCore mScheduledExecutorCore;
/**
* 获取XTask的执行者
*
* @return XTask的执行者
*/
public static XTaskExecutor get() {
if (sInstance == null) {
synchronized (XTaskExecutor.class) {
if (sInstance == null) {
sInstance = new XTaskExecutor();
}
}
}
return sInstance;
}
/**
* 私有构造方法
*/
private XTaskExecutor() {
mCategoryExecutorCore = new CategoryExecutorCore();
mPriorityExecutorCore = new PriorityExecutorCore();
mScheduledExecutorCore = new ScheduledExecutorCore();
}
/**
* 设置优先级控制的执行内核实现接口
*
* @param priorityExecutorCore 优先级控制的执行内核实现接口
* @return this
*/
public XTaskExecutor setPriorityExecutorCore(@NonNull IPriorityExecutorCore priorityExecutorCore) {
mPriorityExecutorCore = priorityExecutorCore;
return this;
}
/**
* 设置类别执行内核实现接口
*
* @param categoryExecutorCore 类别执行内核实现接口
* @return this
*/
public XTaskExecutor setCategoryExecutorCore(@NonNull ICategoryExecutorCore categoryExecutorCore) {
mCategoryExecutorCore = categoryExecutorCore;
return this;
}
/**
* 设置周期执行内核的实现接口
*
* @param scheduledExecutorCore 周期执行内核的实现接口
* @return this
*/
public XTaskExecutor setScheduledExecutorCore(@NonNull IScheduledExecutorCore scheduledExecutorCore) {
mScheduledExecutorCore = scheduledExecutorCore;
return this;
}
@Override
public void shutdown() {
mCategoryExecutorCore.shutdown();
mPriorityExecutorCore.shutdown();
mScheduledExecutorCore.shutdown();
}
//================PriorityExecutorCore==================//
@Override
public ICancelable submit(Runnable task, int priority) {
return mPriorityExecutorCore.submit(task, priority);
}
@Override
public ICancelable submit(String groupName, Runnable task, int priority) {
return mPriorityExecutorCore.submit(groupName, task, priority);
}
//================CategoryExecutorCore==================//
@Override
public boolean postToMain(Runnable task) {
return mCategoryExecutorCore.postToMain(task);
}
@Override
public ICancelable postToMainDelay(Runnable task, long delayMillis) {
return mCategoryExecutorCore.postToMainDelay(task, delayMillis);
}
@Override
public ICancelable emergentSubmit(Runnable task) {
return mCategoryExecutorCore.emergentSubmit(task);
}
@Override
public ICancelable submit(Runnable task) {
return mCategoryExecutorCore.submit(task);
}
@Override
public ICancelable backgroundSubmit(Runnable task) {
return mCategoryExecutorCore.backgroundSubmit(task);
}
@Override
public ICancelable ioSubmit(Runnable task) {
return mCategoryExecutorCore.ioSubmit(task);
}
@Override
public ICancelable groupSubmit(String groupName, Runnable task) {
return mCategoryExecutorCore.groupSubmit(groupName, task);
}
//================ScheduledExecutorCore==================//
@Override
public ICancelable schedule(Runnable task, long delay, TimeUnit unit) {
return mScheduledExecutorCore.schedule(task, delay, unit);
}
@Override
public ICancelable scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return mScheduledExecutorCore.scheduleAtFixedRate(task, initialDelay, period, unit);
}
@Override
public ICancelable scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit unit) {
return mScheduledExecutorCore.scheduleWithFixedDelay(task, initialDelay, period, unit);
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/ICategoryExecutorCore.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.xtask.thread.executor;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
/**
* 拥有不同类别的执行者内核实现接口
*
* @author xuexiang
* @since 1/27/22 12:50 AM
*/
public interface ICategoryExecutorCore extends IExecutorCore {
/**
* 执行任务到主线程
*
* @param task 任务
* @return 是否执行成功
*/
boolean postToMain(Runnable task);
/**
* 延迟执行任务到主线程
*
* @param task 任务
* @param delayMillis 延迟时间
* @return 取消接口
*/
ICancelable postToMainDelay(Runnable task, long delayMillis);
/**
* 执行紧急异步任务【线程的优先级默认是10】
*
* @param task 任务
* @return 取消接口
*/
ICancelable emergentSubmit(Runnable task);
/**
* 执行普通异步任务【线程的优先级是5】
*
* @param task 任务
* @return 取消接口
*/
ICancelable submit(Runnable task);
/**
* 执行后台异步任务【线程的优先级是1】
*
* @param task 任务
* @return 取消接口
*/
ICancelable backgroundSubmit(Runnable task);
/**
* 执行io耗时的异步任务【线程的优先级是5】
*
* @param task 任务
* @return 取消接口
*/
ICancelable ioSubmit(Runnable task);
/**
* 执行分组异步任务【线程的优先级是5】
*
* @param groupName 任务组名
* @param task 任务
* @return 取消接口
*/
ICancelable groupSubmit(String groupName, Runnable task);
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/IExecutorCore.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.xtask.thread.executor;
/**
* 执行者内核实现接口
*
* @author xuexiang
* @since 1/26/22 2:25 AM
*/
public interface IExecutorCore {
/**
* 停止工作
*/
void shutdown();
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/IPriorityExecutorCore.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.executor;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
/**
* 拥有优先级控制的执行者内核实现接口
*
* @author xuexiang
* @since 2021/11/10 1:04 AM
*/
public interface IPriorityExecutorCore extends IExecutorCore {
/**
* 执行异步任务
*
* @param task 任务
* @param priority 优先级
* @return 取消接口
*/
ICancelable submit(Runnable task, int priority);
/**
* 按组执行异步任务
*
* @param groupName 任务组名
* @param task 任务
* @param priority 优先级
* @return 取消接口
*/
ICancelable submit(String groupName, Runnable task, int priority);
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/IScheduledExecutorCore.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.xtask.thread.executor;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import java.util.concurrent.TimeUnit;
/**
* 拥有周期执行能力的内核实现接口
*
* @author xuexiang
* @since 3/19/22 6:40 PM
*/
public interface IScheduledExecutorCore extends IExecutorCore {
/**
* 执行延期任务
*
* @param task 任务
* @param delay 延迟时长
* @param unit 时间单位
* @return 取消接口
*/
ICancelable schedule(Runnable task, long delay, TimeUnit unit);
/**
* 执行周期任务(以固定频率执行的任务)
*
* @param task 任务
* @param initialDelay 初始延迟时长
* @param period 间隔时长
* @param unit 时间单位
* @return 取消接口
*/
ICancelable scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit);
/**
* 执行周期任务(以固定延时执行的任务,延时是相对当前任务结束为起点计算开始时间)
*
* @param task 任务
* @param initialDelay 初始延迟时长
* @param period 间隔时长
* @param unit 时间单位
* @return 取消接口
*/
ICancelable scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit unit);
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/impl/CategoryExecutorCore.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.xtask.thread.executor.impl;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.thread.executor.ICategoryExecutorCore;
import com.xuexiang.xtask.thread.pool.DefaultThreadPoolExecutor;
import com.xuexiang.xtask.thread.pool.TaskThreadFactory;
import com.xuexiang.xtask.thread.pool.cancel.CancelHandlerRunnable;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.utils.ExecutorUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
/**
* 不同类别的执行者内核实现
*
* Emergent: 核心线程数为2,最大线程为∞,60s keepTime,SynchronousQueue(不阻塞),线程优先级10
* Normal: 核心线程数和最大线程为CPU数,0s keepTime,LinkedBlockingQueue(128),线程优先级5
* Background: 核心线程数和最大线程为2,0s keepTime,LinkedBlockingQueue(128),线程优先级1
*
* 线程组: 核心线程数和最大线程为2~4之间,30s keepTime,LinkedBlockingQueue(128),线程优先级5
* io: 核心线程数和最大线程为(2*CPU数+1),30s keepTime,LinkedBlockingQueue(128),线程优先级5
*
* @author xuexiang
* @since 1/26/22 2:33 AM
*/
public class CategoryExecutorCore implements ICategoryExecutorCore {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = 2;
private static final int GROUP_CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int IO_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int EMERGENT_KEEP_ALIVE_SECONDS = 60;
private static final String GROUP_FACTORY_NAME_PREFIX = "CategoryGroup-";
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
/**
* 紧急线程池,核心线程数为2,最大线程为∞,60s keepTime,SynchronousQueue(不阻塞),线程优先级10
*/
private DefaultThreadPoolExecutor mEmergentExecutor;
/**
* 普通线程池,核心线程数和最大线程为CPU数,0s keepTime,LinkedBlockingQueue(128),线程优先级5
*/
private DefaultThreadPoolExecutor mNormalExecutor;
/**
* 后台线程池,核心线程数和最大线程为2,0s keepTime,LinkedBlockingQueue(128),线程优先级1
*/
private DefaultThreadPoolExecutor mBackgroundExecutor;
/**
* 组线程池,核心线程数和最大线程为2~4之间,30s keepTime,LinkedBlockingQueue(128),线程优先级5
*/
private Map mGroupExecutorMap = new ConcurrentHashMap<>();
/**
* io线程池,核心线程数和最大线程为(2*CPU数+1),30s keepTime,LinkedBlockingQueue(128),线程优先级5
*/
private DefaultThreadPoolExecutor mIoExecutor;
@Override
public boolean postToMain(Runnable task) {
if (ExecutorUtils.isMainThread()) {
task.run();
return true;
}
return mMainHandler.post(task);
}
@Override
public ICancelable postToMainDelay(Runnable task, long delayMillis) {
return CancelHandlerRunnable.get(mMainHandler, task).startDelayed(delayMillis);
}
@Override
public ICancelable emergentSubmit(Runnable task) {
return submitTask(getTargetExecutor(Thread.MAX_PRIORITY), task);
}
@Override
public ICancelable submit(Runnable task) {
return submitTask(getTargetExecutor(Thread.NORM_PRIORITY), task);
}
@Override
public ICancelable backgroundSubmit(Runnable task) {
return submitTask(getTargetExecutor(Thread.MIN_PRIORITY), task);
}
@Override
public ICancelable ioSubmit(Runnable task) {
return submitTask(getIoExecutor(), task);
}
@Override
public ICancelable groupSubmit(String groupName, Runnable task) {
return submitTask(getGroupExecutor(groupName), task);
}
@Override
public void shutdown() {
mMainHandler.removeCallbacksAndMessages(null);
ExecutorUtils.shutdown(mEmergentExecutor);
mEmergentExecutor = null;
ExecutorUtils.shutdown(mNormalExecutor);
mNormalExecutor = null;
ExecutorUtils.shutdown(mBackgroundExecutor);
mBackgroundExecutor = null;
ExecutorUtils.shutdown(mIoExecutor);
mIoExecutor = null;
ExecutorUtils.shutdown(mGroupExecutorMap.values());
mGroupExecutorMap.clear();
}
/**
* 提交任务
*
* @param executor 线程池
* @param task 任务
* @return 取消执行的接口
*/
private ICancelable submitTask(@NonNull DefaultThreadPoolExecutor executor, Runnable task) {
return executor.submit(task);
}
/**
* 获取线程池
*
* @param priority 优先级
* @return 线程池
*/
@NonNull
private DefaultThreadPoolExecutor getTargetExecutor(int priority) {
if (priority == Thread.MAX_PRIORITY) {
return getEmergentExecutor();
} else if (priority == Thread.NORM_PRIORITY) {
return getNormalExecutor();
} else {
return getBackgroundExecutor(priority);
}
}
private DefaultThreadPoolExecutor getEmergentExecutor() {
if (mEmergentExecutor == null) {
mEmergentExecutor = DefaultThreadPoolExecutor.newBuilder(CORE_POOL_SIZE)
.setMaximumPoolSize(Integer.MAX_VALUE)
.setKeepAliveTime(EMERGENT_KEEP_ALIVE_SECONDS)
.setWorkQueue(new SynchronousQueue())
.setThreadFactory(TaskThreadFactory.getFactory("Emergent", Thread.MAX_PRIORITY))
.build();
}
return mEmergentExecutor;
}
private DefaultThreadPoolExecutor getNormalExecutor() {
if (mNormalExecutor == null) {
mNormalExecutor = DefaultThreadPoolExecutor.newBuilder(CPU_COUNT)
.setKeepAliveTime(0)
.setThreadFactory(TaskThreadFactory.getFactory("Normal", Thread.NORM_PRIORITY))
.build();
}
return mNormalExecutor;
}
private DefaultThreadPoolExecutor getBackgroundExecutor(int priority) {
if (mBackgroundExecutor == null) {
mBackgroundExecutor = DefaultThreadPoolExecutor.newBuilder(CORE_POOL_SIZE)
.setKeepAliveTime(0)
.setThreadFactory(TaskThreadFactory.getFactory("Background", priority))
.build();
}
return mBackgroundExecutor;
}
private DefaultThreadPoolExecutor getIoExecutor() {
if (mIoExecutor == null) {
mIoExecutor = DefaultThreadPoolExecutor.newBuilder(IO_POOL_SIZE)
.setThreadFactory(TaskThreadFactory.getFactory("Io", Thread.NORM_PRIORITY))
.build();
}
return mIoExecutor;
}
/**
* 获取组线程池
*
* @param groupName 组名称
* @return 线程池
*/
@NonNull
private DefaultThreadPoolExecutor getGroupExecutor(String groupName) {
if (TextUtils.isEmpty(groupName)) {
return getTargetExecutor(Thread.NORM_PRIORITY);
} else {
DefaultThreadPoolExecutor executor = mGroupExecutorMap.get(groupName);
if (executor == null) {
executor = DefaultThreadPoolExecutor.newBuilder(GROUP_CORE_POOL_SIZE)
.setThreadFactory(TaskThreadFactory.getFactory(GROUP_FACTORY_NAME_PREFIX + groupName))
.build();
mGroupExecutorMap.put(groupName, executor);
}
return executor;
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/impl/PriorityExecutorCore.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.executor.impl;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.thread.executor.IExecutorCore;
import com.xuexiang.xtask.thread.executor.IPriorityExecutorCore;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.pool.PriorityThreadPoolExecutor;
import com.xuexiang.xtask.thread.pool.TaskThreadFactory;
import com.xuexiang.xtask.thread.utils.ExecutorUtils;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 拥有优先级控制,使用PriorityThreadPoolExecutor实现的线程执行内核,通过阻塞队列(PriorityBlockingQueue)来实现优先级的控制。
*
* @author xuexiang
* @since 2021/11/10 1:17 AM
*/
public class PriorityExecutorCore implements IPriorityExecutorCore, IExecutorCore {
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int GROUP_CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final String GROUP_FACTORY_NAME_PREFIX = "PriorityGroup-";
private PriorityThreadPoolExecutor mExecutor;
private Map mGroupExecutorMap = new ConcurrentHashMap<>();
@Override
public ICancelable submit(Runnable task, int priority) {
return submitTask(getThreadPoolExecutor(null), task, priority);
}
@Override
public ICancelable submit(String groupName, Runnable task, int priority) {
return submitTask(getThreadPoolExecutor(groupName), task, priority);
}
@Override
public void shutdown() {
ExecutorUtils.shutdown(mExecutor);
mExecutor = null;
ExecutorUtils.shutdown(mGroupExecutorMap.values());
mGroupExecutorMap.clear();
}
/**
* 提交任务
*
* @param executor 线程池
* @param task 任务
* @param priority 优先级
* @return 取消执行的接口
*/
private ICancelable submitTask(@NonNull PriorityThreadPoolExecutor executor, Runnable task, int priority) {
return executor.submit(task, priority);
}
/**
* 获取线程池
*
* @param groupName 组名称
* @return 线程池
*/
@NonNull
private PriorityThreadPoolExecutor getThreadPoolExecutor(String groupName) {
if (TextUtils.isEmpty(groupName)) {
if (mExecutor == null) {
mExecutor = PriorityThreadPoolExecutor.getDefault();
}
return mExecutor;
} else {
PriorityThreadPoolExecutor executor = mGroupExecutorMap.get(groupName);
if (executor == null) {
executor = PriorityThreadPoolExecutor.newBuilder(GROUP_CORE_POOL_SIZE)
.setThreadFactory(TaskThreadFactory.getFactory(GROUP_FACTORY_NAME_PREFIX + groupName))
.build();
mGroupExecutorMap.put(groupName, executor);
}
return executor;
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/executor/impl/ScheduledExecutorCore.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.xtask.thread.executor.impl;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.thread.executor.IScheduledExecutorCore;
import com.xuexiang.xtask.thread.pool.DefaultScheduledThreadPoolExecutor;
import com.xuexiang.xtask.thread.pool.cancel.ICancelable;
import com.xuexiang.xtask.thread.utils.ExecutorUtils;
import java.util.concurrent.TimeUnit;
/**
* 拥有周期执行能力的内核实现
*
* @author xuexiang
* @since 3/20/22 1:15 AM
*/
public class ScheduledExecutorCore implements IScheduledExecutorCore {
private DefaultScheduledThreadPoolExecutor mExecutor;
@Override
public ICancelable schedule(Runnable task, long delay, TimeUnit unit) {
return getThreadPoolExecutor().schedule(task, delay, unit);
}
@Override
public ICancelable scheduleAtFixedRate(Runnable task, long initialDelay, long period, TimeUnit unit) {
return getThreadPoolExecutor().scheduleAtFixedRate(task, initialDelay, period, unit);
}
@Override
public ICancelable scheduleWithFixedDelay(Runnable task, long initialDelay, long period, TimeUnit unit) {
return getThreadPoolExecutor().scheduleWithFixedDelay(task, initialDelay, period, unit);
}
@Override
public void shutdown() {
ExecutorUtils.shutdown(mExecutor);
mExecutor = null;
}
/**
* 获取线程池
*
* @return 线程池
*/
@NonNull
private DefaultScheduledThreadPoolExecutor getThreadPoolExecutor() {
if (mExecutor == null) {
mExecutor = DefaultScheduledThreadPoolExecutor.getDefault();
}
return mExecutor;
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/DefaultScheduledThreadPoolExecutor.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.xtask.thread.pool;
import com.xuexiang.xtask.thread.pool.base.BaseScheduledThreadPoolExecutor;
import com.xuexiang.xtask.thread.pool.cancel.IScheduledFuture;
import java.util.concurrent.Callable;
import java.util.concurrent.Delayed;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableScheduledFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* 默认的定时线程池
*
* @author xuexiang
* @since 3/19/22 5:41 PM
*/
public class DefaultScheduledThreadPoolExecutor extends BaseScheduledThreadPoolExecutor {
/**
* 获取默认配置的优先级线程池
*
* @return 线程池
*/
public static DefaultScheduledThreadPoolExecutor getDefault() {
return new Builder().build();
}
/**
* 获取优先级线程池的构建者
*
* @return 构建者
*/
public static Builder newBuilder() {
return new Builder();
}
/**
* 获取优先级线程池的构建者
*
* @param corePoolSize 核心(执行)线程池的线程数
* @return 构建者
*/
public static Builder newBuilder(int corePoolSize) {
return new Builder(corePoolSize);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param threadFactory 线程创建工厂
* @param handler 拒绝执行的处理
*/
public DefaultScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, threadFactory, handler);
}
@Override
protected RunnableScheduledFuture decorateTask(Callable callable, RunnableScheduledFuture task) {
return new DefaultScheduledFuture<>(callable, task);
}
@Override
protected RunnableScheduledFuture decorateTask(Runnable runnable, RunnableScheduledFuture task) {
return new DefaultScheduledFuture<>(runnable, task);
}
@Override
public IScheduledFuture> schedule(Runnable command, long delay, TimeUnit unit) {
return (IScheduledFuture>) super.schedule(command, delay, unit);
}
@Override
public IScheduledFuture> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
return (IScheduledFuture>) super.scheduleAtFixedRate(command, initialDelay, period, unit);
}
@Override
public IScheduledFuture> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
return (IScheduledFuture>) super.scheduleWithFixedDelay(command, initialDelay, delay, unit);
}
/**
* 默认ScheduledFuture
*
* @author xuexiang
* @since 3/19/22 5:52 PM
*/
public static class DefaultScheduledFuture extends FutureTask implements IScheduledFuture {
private RunnableScheduledFuture mTask;
DefaultScheduledFuture(Callable callable, RunnableScheduledFuture task) {
super(callable);
mTask = task;
}
DefaultScheduledFuture(Runnable runnable, RunnableScheduledFuture task) {
super(runnable, null);
mTask = task;
}
@Override
public void cancel() {
cancel(true);
}
@Override
public long getDelay(TimeUnit unit) {
return mTask.getDelay(unit);
}
@Override
public int compareTo(Delayed o) {
return mTask.compareTo(o);
}
@Override
public boolean isPeriodic() {
return mTask.isPeriodic();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return mTask.cancel(mayInterruptIfRunning);
}
@Override
public void run() {
mTask.run();
}
}
//==============================构建者===================================//
/**
* 默认线程池构建者
*/
public static final class Builder {
/**
* 默认核心线程数
*/
private static final int DEFAULT_CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1;
/**
* 默认线程工厂名
*/
private static final String DEFAULT_FACTORY_NAME = "DefaultScheduled";
/**
* 核心(执行)线程池的线程数
*/
int corePoolSize;
/**
* 线程创建工厂
*/
ThreadFactory threadFactory;
/**
* 拒绝执行的处理
*/
RejectedExecutionHandler handler;
/**
* 构造方法
*/
public Builder() {
this(DEFAULT_CORE_POOL_SIZE);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
*/
public Builder(int corePoolSize) {
this.corePoolSize = corePoolSize;
}
public Builder setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
return this;
}
public Builder setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
return this;
}
public Builder setHandler(RejectedExecutionHandler handler) {
this.handler = handler;
return this;
}
/**
* 构建
*
* @return 优先级线程池
*/
public DefaultScheduledThreadPoolExecutor build() {
if (threadFactory == null) {
threadFactory = TaskThreadFactory.getFactory(DEFAULT_FACTORY_NAME);
}
if (handler == null) {
handler = new TaskRecordPolicy();
}
return new DefaultScheduledThreadPoolExecutor(corePoolSize, threadFactory, handler);
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/DefaultThreadPoolExecutor.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.xtask.thread.pool;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.thread.pool.base.BaseThreadPoolExecutor;
import com.xuexiang.xtask.thread.pool.cancel.IFuture;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* 默认线程池
*
* @author xuexiang
* @since 1/25/22 2:00 AM
*/
public class DefaultThreadPoolExecutor extends BaseThreadPoolExecutor {
/**
* 获取默认配置的优先级线程池
*
* @return 线程池
*/
public static DefaultThreadPoolExecutor getDefault() {
return new Builder().build();
}
/**
* 获取优先级线程池的构建者
*
* @return 构建者
*/
public static DefaultThreadPoolExecutor.Builder newBuilder() {
return new Builder();
}
/**
* 获取优先级线程池的构建者
*
* @param corePoolSize 核心(执行)线程池的线程数
* @return 构建者
*/
public static Builder newBuilder(int corePoolSize) {
return new Builder(corePoolSize);
}
/**
* 获取优先级线程池的构建者
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @return 构建者
*/
public static Builder newBuilder(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
return new Builder(corePoolSize, maximumPoolSize, keepAliveTime, unit);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @param workQueue 线程池工作队列,用于在任务完成之前保留任务的队列执行
* @param threadFactory 线程创建工厂
* @param handler 拒绝执行的处理
*/
private DefaultThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
protected RunnableFuture newTaskFor(Callable callable) {
return new DefaultFuture<>(callable);
}
@Override
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new DefaultFuture<>(runnable, value);
}
@Override
public IFuture> submit(@NonNull Runnable task) {
return (IFuture>) super.submit(task);
}
/**
* 默认Future
*
* @author xuexiang
* @since 1/25/22 2:09 AM
*/
public static class DefaultFuture extends FutureTask implements IFuture {
DefaultFuture(Callable callable) {
super(callable);
}
DefaultFuture(Runnable runnable, V result) {
super(runnable, result);
}
@Override
public void cancel() {
cancel(true);
}
}
//==============================构建者===================================//
/**
* 默认线程池构建者
*/
public static final class Builder {
/**
* 默认核心线程数【非IO操作】
*/
private static final int DEFAULT_CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1;
/**
* 默认的等待时长
*/
private static final long DEFAULT_KEEP_ALIVE_TIME = 30;
/**
* 默认线程工厂名
*/
private static final String DEFAULT_FACTORY_NAME = "Default";
/**
* 默认阻塞队列的大小
*/
private static final int DEFAULT_BLOCKING_QUEUE_SIZE = 128;
/**
* 核心(执行)线程池的线程数
*/
int corePoolSize;
/**
* 线程池最大能容纳的线程数
*/
int maximumPoolSize;
/**
* 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
*/
long keepAliveTime;
/**
* 等待时长的单位
*/
TimeUnit unit;
/**
* 线程池工作队列,用于在任务完成之前保留任务的队列执行
*/
BlockingQueue workQueue;
/**
* 线程创建工厂
*/
ThreadFactory threadFactory;
/**
* 拒绝执行的处理
*/
RejectedExecutionHandler handler;
/**
* 构造方法
*/
public Builder() {
this(DEFAULT_CORE_POOL_SIZE);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
*/
public Builder(int corePoolSize) {
this(corePoolSize, corePoolSize, DEFAULT_KEEP_ALIVE_TIME, TimeUnit.SECONDS);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
*/
public Builder(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit) {
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.keepAliveTime = keepAliveTime;
this.unit = unit;
}
public Builder setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
return this;
}
public Builder setMaximumPoolSize(int maximumPoolSize) {
this.maximumPoolSize = maximumPoolSize;
return this;
}
public Builder setKeepAliveTime(long keepAliveTime) {
this.keepAliveTime = keepAliveTime;
return this;
}
public Builder setUnit(TimeUnit unit) {
this.unit = unit;
return this;
}
public Builder setWorkQueue(BlockingQueue workQueue) {
this.workQueue = workQueue;
return this;
}
public Builder setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
return this;
}
public Builder setHandler(RejectedExecutionHandler handler) {
this.handler = handler;
return this;
}
/**
* 构建
*
* @return 优先级线程池
*/
public DefaultThreadPoolExecutor build() {
if (workQueue == null) {
workQueue = new LinkedBlockingQueue<>(DEFAULT_BLOCKING_QUEUE_SIZE);
}
if (threadFactory == null) {
threadFactory = TaskThreadFactory.getFactory(DEFAULT_FACTORY_NAME);
}
if (handler == null) {
handler = new TaskRecordPolicy();
}
return new DefaultThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/PriorityThreadPoolExecutor.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.xtask.thread.pool;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.pool.base.BaseThreadPoolExecutor;
import com.xuexiang.xtask.thread.priority.IPriority;
import com.xuexiang.xtask.thread.priority.IPriorityComparable;
import com.xuexiang.xtask.thread.priority.IPriorityFuture;
import com.xuexiang.xtask.thread.priority.IPriorityRunnable;
import com.xuexiang.xtask.thread.priority.impl.DefaultPriorityCallable;
import com.xuexiang.xtask.thread.priority.impl.DefaultPriorityFuture;
import com.xuexiang.xtask.thread.priority.impl.DefaultPriorityRunnable;
import com.xuexiang.xtask.thread.priority.impl.Priority;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* 一个具有指定和动态调整任务优先级能力的Java线程池,阻塞队列是PriorityBlockingQueue(无界),按优先级进行排序。
* 注意:PriorityBlockingQueue是无界阻塞队列。
*
* 线程池的执行顺序:
*
* 核心线程池 -> 阻塞队列 -> 最大线程数(新建线程) -> RejectedExecutionHandler
*
* 因此,该线程池起作用的主要是在阻塞队列这一层。
*
* @author xuexiang
* @since 2021/10/9 2:28 AM
*/
public class PriorityThreadPoolExecutor extends BaseThreadPoolExecutor {
private static final String TAG = TaskLogger.getLogTag("PriorityThreadPoolExecutor");
/**
* 获取默认配置的优先级线程池
*
* @return 线程池
*/
public static PriorityThreadPoolExecutor getDefault() {
return new Builder().build();
}
/**
* 获取优先级线程池的构建者
*
* @return 构建者
*/
public static Builder newBuilder() {
return new Builder();
}
/**
* 获取优先级线程池的构建者
*
* @param corePoolSize 核心(执行)线程池的线程数
* @return 构建者
*/
public static Builder newBuilder(int corePoolSize) {
return new Builder(corePoolSize);
}
/**
* 获取优先级线程池的构建者
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @return 构建者
*/
public static Builder newBuilder(int corePoolSize, long keepAliveTime, TimeUnit unit) {
return new Builder(corePoolSize, keepAliveTime, unit);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数,因为PriorityBlockingQueue是无界阻塞队列,因此maximumPoolSize没有任何意义。
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @param workQueue 线程池工作队列,用于在任务完成之前保留任务的队列执行
* @param threadFactory 线程创建工厂
* @param handler 拒绝执行的处理
*/
private PriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, PriorityBlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
/**
* 指定优先级执行Runnable
*
* @param command 命令
* @param priority 优先级
* @return IPriorityRunnable
*/
public IPriorityRunnable execute(Runnable command, int priority) {
if (command instanceof IPriorityRunnable) {
this.execute(command);
return (IPriorityRunnable) command;
}
IPriorityRunnable runnable = new DefaultPriorityRunnable(new Priority(priority), command);
this.execute(runnable);
return runnable;
}
/**
* 指定优先级执行Runnable
*
* @param task 任务
* @param priority 优先级
* @return IPriorityFuture
*/
public IPriorityFuture> submit(Runnable task, int priority) {
if (task instanceof IPriority) {
return (IPriorityFuture>) this.submit(task);
}
return (IPriorityFuture>) this.submit(new DefaultPriorityRunnable(new Priority(priority), task));
}
/**
* 指定优先级执行Runnable
*
* @param task 任务
* @param result 结果
* @param priority 优先级
* @return IPriorityFuture
*/
public IPriorityFuture submit(Runnable task, T result, int priority) {
if (task instanceof IPriority) {
return (IPriorityFuture) this.submit(task, result);
}
return (IPriorityFuture) this.submit(new DefaultPriorityRunnable(new Priority(priority), task),
result);
}
/**
* 指定优先级执行Callable
*
* @param task 任务
* @param priority 优先级
* @return IPriorityFuture
*/
public IPriorityFuture submit(Callable task, int priority) {
if (task instanceof IPriority) {
return (IPriorityFuture) this.submit(task);
}
return (IPriorityFuture) this.submit(new DefaultPriorityCallable<>(new Priority(priority), task));
}
@Override
protected RunnableFuture newTaskFor(Callable callable) {
return new DefaultPriorityFuture<>(callable);
}
@Override
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new DefaultPriorityFuture<>(runnable, value);
}
/**
* 传递子类PriorityRunnable,传递Runnable而非PriorityRunnable的话,将不支持优先级调整
* 如果要使用Runnable又需要支持优先级可用扩展方法{{@link #execute(Runnable, int)}}并使用其返回值进行优先级调整
*
* @param command 命令
*/
@Override
public void execute(Runnable command) {
if (command instanceof IPriorityComparable) {
super.execute(command);
return;
}
if (command instanceof IPriority) {
super.execute(new DefaultPriorityRunnable((IPriority) command, command));
return;
}
super.execute(new DefaultPriorityRunnable(new Priority(), command));
}
/**
* 传递子类PriorityRunnable,传递Runnable而非PriorityRunnable的话,将不支持优先级调整
* 如果要使用Runnable又需要支持优先级可用扩展方法{{@link #submit(Runnable, int)}}并使用其返回值进行优先级调整
*
* @param task 任务
* @return Future
*/
@Override
public Future> submit(Runnable task) {
if (task instanceof IPriorityComparable) {
return super.submit(task);
}
if (task instanceof IPriority) {
return super.submit(new DefaultPriorityRunnable((IPriority) task, task));
}
return super.submit(new DefaultPriorityRunnable(new Priority(), task));
}
/**
* 传递子类PriorityRunnable,传递Runnable而非PriorityRunnable的话,将不支持优先级调整
* 如果要使用Runnable又需要支持优先级可用扩展方法{{@link #submit(Runnable, Object, int)}}并使用其返回值进行优先级调整
*
* @param task 任务
* @param result 结果
* @return Future
*/
@Override
public Future submit(Runnable task, T result) {
if (task instanceof IPriorityComparable) {
return super.submit(task, result);
}
if (task instanceof IPriority) {
return super.submit(new DefaultPriorityRunnable((IPriority) task, task), result);
}
return super.submit(new DefaultPriorityRunnable(new Priority(), task), result);
}
/**
* 传递子类PriorityCallable,传递Callable而非PriorityCallable的话,将不支持优先级调整
* 如果要使用Callable又需要支持优先级可用扩展方法{{@link #submit(Callable, int)}}并使用其返回值进行优先级调整
*
* @param task 任务
* @return Future
*/
@Override
public Future submit(Callable task) {
if (task instanceof IPriorityComparable) {
return super.submit(task);
}
if (task instanceof IPriority) {
return super.submit(new DefaultPriorityCallable<>((IPriority) task, task));
}
return super.submit(new DefaultPriorityCallable<>(new Priority(), task));
}
//==============================构建者===================================//
/**
* 优先级线程池构建者
*/
public static final class Builder {
/**
* 默认核心线程数【非IO操作】
*/
private static final int DEFAULT_CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors() + 1;
/**
* 默认的等待时长
*/
private static final long DEFAULT_KEEP_ALIVE_TIME = 30;
/**
* 默认线程工厂名
*/
private static final String DEFAULT_FACTORY_NAME = "Priority";
/**
* 核心(执行)线程池的线程数
*/
int corePoolSize;
/**
* 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
*/
long keepAliveTime;
/**
* 等待时长的单位
*/
TimeUnit unit;
/**
* 线程池工作队列,用于在任务完成之前保留任务的队列执行
*/
PriorityBlockingQueue workQueue;
/**
* 线程创建工厂
*/
ThreadFactory threadFactory;
/**
* 拒绝执行的处理
*/
RejectedExecutionHandler handler;
/**
* 构造方法
*/
public Builder() {
this(DEFAULT_CORE_POOL_SIZE);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
*/
public Builder(int corePoolSize) {
this(corePoolSize, DEFAULT_KEEP_ALIVE_TIME, TimeUnit.SECONDS);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
*/
public Builder(int corePoolSize, long keepAliveTime, TimeUnit unit) {
this.corePoolSize = corePoolSize;
this.keepAliveTime = keepAliveTime;
this.unit = unit;
}
public Builder setCorePoolSize(int corePoolSize) {
this.corePoolSize = corePoolSize;
return this;
}
public Builder setKeepAliveTime(long keepAliveTime) {
this.keepAliveTime = keepAliveTime;
return this;
}
public Builder setUnit(TimeUnit unit) {
this.unit = unit;
return this;
}
public Builder setWorkQueue(PriorityBlockingQueue workQueue) {
this.workQueue = workQueue;
return this;
}
public Builder setThreadFactory(ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
return this;
}
public Builder setHandler(RejectedExecutionHandler handler) {
this.handler = handler;
return this;
}
/**
* 构建
*
* @return 优先级线程池
*/
public PriorityThreadPoolExecutor build() {
if (workQueue == null) {
workQueue = new PriorityBlockingQueue<>();
}
if (threadFactory == null) {
threadFactory = TaskThreadFactory.getFactory(DEFAULT_FACTORY_NAME);
}
if (handler == null) {
handler = new TaskRecordPolicy();
}
return new PriorityThreadPoolExecutor(corePoolSize, corePoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/TaskRecordPolicy.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.xtask.thread.pool;
import com.xuexiang.xtask.logger.TaskLogger;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 记录日志的拒绝策略
*
* @author xuexiang
* @since 2021/12/16 2:02 AM
*/
public class TaskRecordPolicy implements RejectedExecutionHandler {
private static final String TAG = TaskLogger.getLogTag("RecordPolicy");
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
TaskLogger.eTag(TAG, "Runnable task has been rejected! Thread [" + Thread.currentThread().getName() + "], Runnable: " + r + ", ThreadPoolExecutor: " + e);
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/TaskThreadFactory.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.xtask.thread.pool;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import com.xuexiang.xtask.logger.TaskLogger;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 自定义线程创建工厂
*
* @author xuexiang
* @since 2021/10/9 4:01 PM
*/
public class TaskThreadFactory implements ThreadFactory {
private static final String TAG = TaskLogger.getLogTag("TaskThreadFactory");
/**
* 线程池的编号池【静态全局】<优先级,线程池的编号>
*/
private static final Map POOL_NUMBER_MAP = new ConcurrentHashMap<>();
/**
* 线程组
*/
private final ThreadGroup mThreadGroup;
/**
* 线程的编号
*/
private final AtomicInteger mThreadNumber = new AtomicInteger(1);
/**
* 线程名前缀
*/
private final String mNamePrefix;
/**
* 创建的线程优先级
*/
private final int mPriority;
/**
* 获取线程创建工厂
*
* @param factoryName 工厂名
* @return 线程创建工厂
*/
public static TaskThreadFactory getFactory(@NonNull String factoryName) {
return new TaskThreadFactory(factoryName, Thread.NORM_PRIORITY);
}
/**
* 获取线程创建工厂
*
* @param factoryName 工厂名
* @param priority 线程的优先级
* @return 线程创建工厂
*/
public static TaskThreadFactory getFactory(@NonNull String factoryName, @IntRange(from = Thread.MIN_PRIORITY, to = Thread.MAX_PRIORITY) int priority) {
return new TaskThreadFactory(factoryName, priority);
}
/**
* 构造方法
*
* @param factoryName 工厂名
* @param priority 线程的优先级
*/
private TaskThreadFactory(@NonNull String factoryName, @IntRange(from = Thread.MIN_PRIORITY, to = Thread.MAX_PRIORITY) int priority) {
SecurityManager securityManager = System.getSecurityManager();
mThreadGroup = (securityManager != null) ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();
mNamePrefix = factoryName + "-task-pool(" + priority + ") No." + getTaskPoolNumber(priority) + ", thread No.";
mPriority = priority;
}
@Override
public Thread newThread(Runnable r) {
String threadName = mNamePrefix + mThreadNumber.getAndIncrement();
TaskLogger.iTag(TAG, "Thread production, name is [" + threadName + "]");
Thread thread = new Thread(mThreadGroup, r, threadName, 0);
if (thread.isDaemon()) {
// 设置为非守护线程
thread.setDaemon(false);
}
if (thread.getPriority() != mPriority) {
thread.setPriority(mPriority);
}
// 捕获多线程处理中的异常
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable ex) {
TaskLogger.eTag(TAG, "Running task appeared exception! Thread [" + thread.getName() + "], because [" + ex.getMessage() + "]");
}
});
return thread;
}
private int getTaskPoolNumber(int priority) {
AtomicInteger poolNumber = POOL_NUMBER_MAP.get(priority);
if (poolNumber == null) {
poolNumber = new AtomicInteger(1);
POOL_NUMBER_MAP.put(priority, poolNumber);
}
return poolNumber.getAndIncrement();
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/base/BaseScheduledThreadPoolExecutor.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.xtask.thread.pool.base;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.priority.IPriority;
import com.xuexiang.xtask.thread.utils.PriorityUtils;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
/**
* ScheduledThreadPoolExecutor线程池基类
*
* maximumPoolSize:Integer.MAX_VALUE
* keepAliveTime:10L
* unit:MILLISECONDS
* workQueue:DelayedWorkQueue
*
* @author xuexiang
* @since 3/19/22 4:49 PM
*/
public class BaseScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {
private static final String TAG = TaskLogger.getLogTag("BaseScheduledThreadPoolExecutor");
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
*/
public BaseScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param threadFactory 线程创建工厂
*/
public BaseScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, threadFactory);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param handler 拒绝执行的处理
*/
public BaseScheduledThreadPoolExecutor(int corePoolSize, RejectedExecutionHandler handler) {
super(corePoolSize, handler);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param threadFactory 线程创建工厂
* @param handler 拒绝执行的处理
*/
public BaseScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, threadFactory, handler);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
if (t == null) {
return;
}
if (!TaskLogger.isLogThreadName()) {
return;
}
if (r instanceof IPriority) {
TaskLogger.dTag(TAG, "Running task start execute, id:" + ((IPriority) r).getId() + ", priority:" + ((IPriority) r).priority() + ", in Thread [" + Thread.currentThread().getName() + "]");
} else {
TaskLogger.dTag(TAG, "Running task start execute, in Thread [" + Thread.currentThread().getName() + "]");
}
}
/**
* 线程执行结束,顺便看一下有么有什么乱七八糟的异常
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future> && ((Future>) r).isDone()) {
try {
((Future>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
TaskLogger.wTag(TAG, "Running task appeared exception! Thread [" + Thread.currentThread().getName() + "], because [" + t.getMessage() + "]\n" + PriorityUtils.formatStackTrace(t.getStackTrace()));
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/base/BaseThreadPoolExecutor.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.xtask.thread.pool.base;
import com.xuexiang.xtask.logger.TaskLogger;
import com.xuexiang.xtask.thread.priority.IPriority;
import com.xuexiang.xtask.thread.utils.PriorityUtils;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* ThreadPoolExecutor线程池基类
*
* 线程池的执行顺序:
*
* 核心线程池(A) -> 阻塞队列(B) -> 最大线程数(C)[新建线程] -> RejectedExecutionHandler
*
* 一个线程最多同时容纳的任务数= 阻塞队列(B) + 最大线程数(C)
*
* allowCoreThreadTimeOut为true, 线程池数量最后销毁到0个。为false时, 线程池数量最后销毁到核心线程数。
*
* 阻塞队列:
*
* 1.ArrayBlockingQueue、LinkedBlockingQueue是有界阻塞队列。如果直接使用LinkedBlockingQueue的默认构造函数(默认大小是Integer.MAX_VALUE)可视为无界阻塞队列。
* 2.PriorityBlockingQueue是无界阻塞队列。
* 3.SynchronousQueue是无缓冲队列,一般设置这个的时候maximumPoolSize需要设置为Integer.MAX_VALUE
*
* @author xuexiang
* @since 2021/12/16 2:05 AM
*/
public class BaseThreadPoolExecutor extends ThreadPoolExecutor {
private static final String TAG = TaskLogger.getLogTag("BaseThreadPoolExecutor");
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @param workQueue 线程池工作队列,用于在任务完成之前保留任务的队列执行
*/
public BaseThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @param workQueue 线程池工作队列,用于在任务完成之前保留任务的队列执行
* @param threadFactory 线程创建工厂
*/
public BaseThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @param workQueue 线程池工作队列,用于在任务完成之前保留任务的队列执行
* @param handler 拒绝执行的处理
*/
public BaseThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
/**
* 构造方法
*
* @param corePoolSize 核心(执行)线程池的线程数
* @param maximumPoolSize 线程池最大能容纳的线程数
* @param keepAliveTime 当线程数大于核心数时,多余空闲线程在终止前等待新任务的最长时间。
* @param unit 等待时长的单位
* @param workQueue 线程池工作队列,用于在任务完成之前保留任务的队列执行
* @param threadFactory 线程创建工厂
* @param handler 拒绝执行的处理
*/
public BaseThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
if (t == null) {
return;
}
if (!TaskLogger.isLogThreadName()) {
return;
}
if (r instanceof IPriority) {
TaskLogger.dTag(TAG, "Running task start execute, id:" + ((IPriority) r).getId() + ", priority:" + ((IPriority) r).priority() + ", in Thread [" + Thread.currentThread().getName() + "]");
} else {
TaskLogger.dTag(TAG, "Running task start execute, in Thread [" + Thread.currentThread().getName() + "]");
}
}
/**
* 线程执行结束,顺便看一下有么有什么乱七八糟的异常
*/
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t == null && r instanceof Future> && ((Future>) r).isDone()) {
try {
((Future>) r).get();
} catch (CancellationException ce) {
t = ce;
} catch (ExecutionException ee) {
t = ee.getCause();
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
if (t != null) {
TaskLogger.wTag(TAG, "Running task appeared exception! Thread [" + Thread.currentThread().getName() + "], because [" + t.getMessage() + "]\n" + PriorityUtils.formatStackTrace(t.getStackTrace()));
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/CancelHandlerRunnable.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.xtask.thread.pool.cancel;
import android.os.Handler;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 可以取消的供Handler使用的Runnable
*
* @author xuexiang
* @since 3/21/22 1:34 AM
*/
public class CancelHandlerRunnable implements Runnable, ICancelable {
/**
* 获取可以取消的Runnable
*
* @param handler handler
* @param runnable 执行任务
* @return 可以取消的Runnable
*/
public static CancelHandlerRunnable get(@NonNull Handler handler, @NonNull Runnable runnable) {
return new CancelHandlerRunnable(handler, runnable);
}
private WeakReference mHandler;
private Runnable mRunnable;
private AtomicBoolean mIsCancelled = new AtomicBoolean(false);
/**
* 构造方法
*
* @param handler handler
* @param runnable 执行任务
*/
private CancelHandlerRunnable(@NonNull Handler handler, @NonNull Runnable runnable) {
mHandler = new WeakReference<>(handler);
mRunnable = runnable;
}
/**
* 开始延期执行
*
* @param delayMillis 延期时间
* @return 取消接口
*/
public ICancelable startDelayed(long delayMillis) {
final Handler handler = mHandler.get();
if (handler != null) {
handler.postDelayed(this, delayMillis);
}
return this;
}
@Override
public void cancel() {
if (isCancelled()) {
return;
}
final Handler handler = mHandler.get();
if (handler == null) {
return;
}
handler.removeCallbacks(this);
mIsCancelled.set(true);
}
@Override
public boolean isCancelled() {
return mIsCancelled.get();
}
@Override
public void run() {
if (mRunnable != null) {
mRunnable.run();
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/ICancelable.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.xtask.thread.pool.cancel;
/**
* 可取消执行的实现接口
*
* @author xuexiang
* @since 2021/11/2 12:20 AM
*/
public interface ICancelable {
/**
* 取消
*/
void cancel();
/**
* 获取是否已取消
*
* @return 是否已取消
*/
boolean isCancelled();
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/ICanceller.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.xtask.thread.pool.cancel;
/**
* 取消者
*
* @author xuexiang
* @since 2/7/22 12:39 PM
*/
public interface ICanceller extends ICancelable {
/**
* 获取取消的标识
*
* @return 取消的标识
*/
String getName();
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/ICancellerPool.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.xtask.thread.pool.cancel;
import java.util.Collection;
/**
* 取消者订阅池
*
* @author xuexiang
* @since 2/7/22 1:03 PM
*/
public interface ICancellerPool {
/**
* 添加取消者
*
* @param name 取消者名称
* @param cancelable 取消接口
* @return 是否执行成功
*/
boolean add(String name, ICancelable cancelable);
/**
* 去除取消者
*
* @param name 取消者名称
* @return 是否执行成功
*/
boolean remove(String name);
/**
* 指定取消者执行
*
* @param name 取消者名称
* @return 是否执行成功
*/
boolean cancel(String name);
/**
* 指定取消者集合执行
*
* @param names 取消者集合
*/
void cancel(String... names);
/**
* 指定取消者集合执行
*
* @param names 取消者集合
*/
void cancel(Collection names);
/**
* 所有取消者执行
*/
void cancelAll();
/**
* 清楚所有
*
* @param ifNeedCancel 是否在清除前取消
*/
void clear(boolean ifNeedCancel);
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/IFuture.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.xtask.thread.pool.cancel;
import java.util.concurrent.Future;
/**
* 可直接取消任务的Future接口
*
* @author xuexiang
* @since 1/25/22 2:04 AM
*/
public interface IFuture extends Future, ICancelable {
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/IScheduledFuture.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.xtask.thread.pool.cancel;
import java.util.concurrent.RunnableScheduledFuture;
/**
* 可直接取消任务的RunnableScheduledFuture接口
*
* @author xuexiang
* @since 3/20/22 1:30 AM
*/
public interface IScheduledFuture extends RunnableScheduledFuture, ICancelable {
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/pool/cancel/TaskCancellerPool.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.xtask.thread.pool.cancel;
import com.xuexiang.xtask.thread.utils.CancelUtils;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 任务取消者池
*
* @author xuexiang
* @since 2/7/22 12:43 PM
*/
public final class TaskCancellerPool implements ICancellerPool {
private Map mPool = new ConcurrentHashMap<>();
@Override
public boolean add(String name, ICancelable cancelable) {
if (name == null || cancelable == null) {
return false;
}
return mPool.put(name, cancelable) != null;
}
@Override
public boolean remove(String name) {
if (name == null) {
return false;
}
return mPool.remove(name) != null;
}
@Override
public boolean cancel(String name) {
if (name == null) {
return false;
}
ICancelable cancelable = mPool.get(name);
boolean result = CancelUtils.cancel(cancelable);
mPool.remove(name);
return result;
}
@Override
public void cancel(String... names) {
for (String name : names) {
cancel(name);
}
}
@Override
public void cancel(Collection names) {
for (String name : names) {
cancel(name);
}
}
@Override
public void cancelAll() {
if (mPool.isEmpty()) {
return;
}
CancelUtils.cancel(mPool.values());
mPool.clear();
}
@Override
public void clear(boolean ifNeedCancel) {
if (ifNeedCancel) {
cancelAll();
} else {
mPool.clear();
}
}
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/priority/IPriority.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.priority;
/**
* 优先级排序接口
*
* @author xuexiang
* @since 2021/10/9 2:32 AM
*/
public interface IPriority {
/**
* 获取优先级,越大越靠前
*
* @return 优先级
*/
int priority();
/**
* 改变优先级,越大越靠前
*
* @param priority 优先级
*/
void priority(int priority);
/**
* 获取序号
*
* @return 序号
*/
long getId();
/**
* 设置序号
*
* @param id 序号
*/
void setId(long id);
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/priority/IPriorityCallable.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.priority;
import java.util.concurrent.Callable;
/**
* 具有优先级排序的Callable接口
*
* @author xuexiang
* @since 2021/10/9 2:36 AM
*/
public interface IPriorityCallable extends IPriorityComparable, Callable {
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/priority/IPriorityComparable.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.priority;
/**
* 具有优先级排序的Comparable接口
*
* @author xuexiang
* @since 2021/10/9 2:36 AM
*/
public interface IPriorityComparable extends IPriority, Comparable {
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/priority/IPriorityFuture.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.priority;
import com.xuexiang.xtask.thread.pool.cancel.IFuture;
/**
* 具有优先级排序的Future接口
*
* @author xuexiang
* @since 2021/10/9 2:31 AM
*/
public interface IPriorityFuture extends IPriorityComparable, IFuture {
}
================================================
FILE: xtask-thread/src/main/java/com/xuexiang/xtask/thread/priority/IPriorityRunnable.java
================================================
/*
* Copyright (C) 2021 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.xtask.thread.priority;
/**
* 具有优先级排序的Runnable接口
*
* @author xuexiang
* @since 2021/10/9 2:34 AM
*/
public interface IPriorityRunnable extends IPriorityComparable