Skip to content

Commit

Permalink
Add an pure-java version
Browse files Browse the repository at this point in the history
  • Loading branch information
markzhai committed Oct 5, 2015
1 parent d0521b4 commit b85e817
Show file tree
Hide file tree
Showing 23 changed files with 697 additions and 49 deletions.
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,22 @@ The library does not depend on any third-party library, it depends on Android in

[For Chinese 中文戳这里](https://github.com/markzhai/init/blob/master/README_CN.md)

# How

The initialization procedure is abstracted to flow, wave and task.

![flow](art/flow.png "how it works")

Flow is a coarse-grained concept, normally we have only one flow, but under certain condition, we may have several flow like patch flow, fake UI flow(to make user feel faster), broadcast flow, etc.

Each wave can be started only when all blocked task in last wave finished, and all tasks belongs tothe wave will started at the same time(if no delay set).

As for task, they can be divided into two types
1. Blocked task, blue tasks in the picture.
2. Asynchronous task, can be
- completely asynchronous or across several waves like the green task.
- asynchronous task chain, like the two red tasks.

# Usage

```gradle
Expand Down Expand Up @@ -96,9 +112,9 @@ Init.getFlowStatus(...)
flow.getTaskStatus(taskName)
// set timeout, may not be useful for application init, but can be used for other init operation
flow.setTimeout
flow.setTimeout(5000)
etc
etc.
```
# Why this
Expand Down Expand Up @@ -158,22 +174,6 @@ You see how complicated the initialization can be when the application grows, so
How to make it simpler? I came up with this library.
# How
The initialization procedure is abstracted to flow, wave and task.
![flow](art/flow.png "how it works")
Flow is a coarse-grained concept, normally we have only one flow, but under certain condition, we may have several flow like patch flow, fake UI flow(to make user feel faster), broadcast flow, etc.
Each wave can be started only when all blocked task in last wave finished, and all tasks belongs tothe wave will started at the same time(if no delay set).
As for task, they can be divided into two types
1. Blocked task, blue tasks in the picture.
2. Asynchronous task, can be
- completely asynchronous or across several waves like the green task.
- asynchronous task chain, like the two red tasks.
# Roadmap
- 1.0 *October - A workable solution to principles mentioned above* DONE
- 1.1 **In this year - Support more complex init flow** WIP
Expand Down
34 changes: 17 additions & 17 deletions README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ Init帮助Android应用调度初始化流程,囊括类型、优先级、多进

Init不依赖于任何第三方库,使用Java concurrent并部分依赖于Android SDK(Context, Log),所以理论上也可以在简单修改后直接用于Java工程。

# How

初始化流程被抽象为flow、wave和task。

![flow](art/flow.png "how it works")

flow是一个粗粒度概念,通常一个应用只有一个flow,但某些情况下我们可能拥有多个flow,像是patch flow,broadcast flow,fake UI flow等等,可以把它们都交给Init处理。

每个wave只有在上一wave的所有阻塞task完成后才能开始,而所有属于该wave的task会一起开始执行(除非被赋予了delay)。

至于task,在本库中属于原子性操作,他们可以被分为2大类型
1. 阻塞task,即图中的蓝色任务。
2. 异步task,又可以被分为
- 完全异步或者横跨若干个wave后才需要阻塞,像图中的绿色task。
- 异步链,像图中的红色task。

# 使用

```gradle
Expand Down Expand Up @@ -87,7 +103,7 @@ flow.getTaskStatus(taskName)
// 设置超时限制
flow.setTimeout(5000)

etc
等等
```

更多详情请见demo工程。
Expand Down Expand Up @@ -149,22 +165,6 @@ public class ProcessInit {

怎么可以使它变得简单呢?Init就是来帮助你做这个事的。

# How

初始化流程被抽象为flow、wave和task。

![flow](art/flow.png "how it works")

flow是一个粗粒度概念,通常一个应用只有一个flow,但某些情况下我们可能拥有多个flow,像是patch flow,broadcast flow,fake UI flow等等,可以把它们都交给Init处理。

每个wave只有在上一wave的所有阻塞task完成后才能开始,而所有属于该wave的task会一起开始执行(除非被赋予了delay)。

至于task,在本库中属于原子性操作,他们可以被分为2大类型
1. 阻塞task,即图中的蓝色任务。
2. 异步task,又可以被分为
- 完全异步或者横跨若干个wave后才需要阻塞,像图中的绿色task。
- 异步链,像图中的红色task。

# 路线图
- 1.0 *10- 一个实现上述概念的可运行库* 已完成
- 1.1 **2015年内 - 支持更复杂的初始化flow** 进行中
Expand Down
6 changes: 3 additions & 3 deletions demo/src/main/java/cn/zhaiyifan/demo/DemoApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

import android.app.Application;

import cn.zhaiyifan.appinit.Init;
import cn.zhaiyifan.appinit.Flow;
import cn.zhaiyifan.appinit.Task;
import cn.zhaiyifan.init.Init;
import cn.zhaiyifan.init.Flow;
import cn.zhaiyifan.init.Task;

public class DemoApplication extends Application {

Expand Down
1 change: 1 addition & 0 deletions init-java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
5 changes: 5 additions & 0 deletions init-java/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apply plugin: 'java'

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
150 changes: 150 additions & 0 deletions init-java/src/main/java/cn/zhaiyifan/init/Flow.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package cn.zhaiyifan.init;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
* <p>A coarse-grained concept, normally an app has only one flow, complex apps can have flow other
* than init flow, like patch, broadcast, etc.</p>
* Created by mark.zhai on 2015/10/2.
*/
public class Flow {
private static final String TAG = "Flow";
private static final long DEFAULT_FLOW_TIMEOUT = 3000;

private Map<Integer, Wave> mWaveMap;
private Map<String, Integer> mTaskToWaveMap;

private int mFlowStatus = Status.STATUS_UNKNOWN;

private String mName;
private long mTimeout = DEFAULT_FLOW_TIMEOUT;
private boolean mCancel = false;

/**
* Constructor
*
* @param flowName flow name
*/
public Flow(String flowName) {
mName = flowName;
mFlowStatus = Status.STATUS_PENDING_START;

mWaveMap = new HashMap<>();
mTaskToWaveMap = new HashMap<>();
}

/**
* Add task to this flow.
*
* @param waveSeq Which wave sequence to add.
* @param task task
* @return Flow
*/
public Flow addTask(int waveSeq, Task task) {
if (task != null) {
Wave wave = mWaveMap.get(waveSeq);
if (wave == null) {
wave = new Wave(waveSeq, ProcessUtils.myProcessName());
mWaveMap.put(waveSeq, wave);
}
wave.addTask(task);
mTaskToWaveMap.put(task.getName(), waveSeq);
}
return this;
}

/**
* Set timeout to this flow.
*
* @param timeout timeout in milliseconds
*/
public void setTimeout(long timeout) {
mTimeout = timeout;
}

/**
* Start flow, return when all blocked tasks completed.
*/
public synchronized void start() {
if (mFlowStatus != Status.STATUS_PENDING_START) {
throw new RuntimeException("Error! Flow has already started.");
}

long startTime = System.currentTimeMillis();

ExecutorService threadPool = Init.getThreadPool();

Callable<Boolean> flowTask = new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
for (Map.Entry<Integer, Wave> entry : mWaveMap.entrySet()) {
if (mCancel) {
return false;
}
Wave wave = entry.getValue();
wave.start();
}
return true;
}
};

Future<Boolean> initTask = threadPool.submit(flowTask);

try {
initTask.get(mTimeout, TimeUnit.MILLISECONDS);
} catch (Exception e) {
LogImpl.w(TAG, "timeout for flow: " + getName());
}

long endTime = System.currentTimeMillis();
LogImpl.i(TAG, getName() + " runs " + (endTime - startTime));

mFlowStatus = Status.STATUS_EXECUTING;
}

/**
* cannot guarantee immediately cancel
*/
public void cancel() {
mCancel = true;
}

/**
* Get flow status.
*
* @return status
*/
public int getFlowStatus() {
return mFlowStatus;
}

/**
* Get task status.
*
* @param taskName task name
* @return status
*/
public int getTaskStatus(String taskName) {
Integer waveSeq = mTaskToWaveMap.get(taskName);
Wave wave = mWaveMap.get(waveSeq);
if (wave != null) {
return wave.getTaskStatus(taskName);
} else {
return Status.STATUS_UNKNOWN;
}
}

/**
* Get name of the flow.
*
* @return flow name.
*/
public String getName() {
return mName;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package cn.zhaiyifan.appinit;
package cn.zhaiyifan.init;

/**
* <h1>Log interface for application to implement so that the library can use application's custom
Expand Down
104 changes: 104 additions & 0 deletions init-java/src/main/java/cn/zhaiyifan/init/Init.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package cn.zhaiyifan.init;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* <p>Entry to add, start and manage init flow.</p>
* Created by mark.zhai on 2015/10/2.
*/
public class Init {
private static final int DEFAULT_THREAD_POOL_SIZE = 8;

private static Map<String, Flow> sFlowMap = new HashMap<>();
private static int mThreadPoolSize = DEFAULT_THREAD_POOL_SIZE;

/**
* Init with context and log class.
*
* @param logProxy log class implements {@link ILog}
*/
public static void init(ILog logProxy) {
LogImpl.setLogProxy(logProxy);
}

public static void addFlow(Flow flow) {
sFlowMap.put(flow.getName(), flow);
}

public static void addFlow(Map<String, Flow> flowMap) {
sFlowMap.putAll(flowMap);
}

/**
* Set thread pool size used by tasks.
*
* @param size thread pool size, value less or equal than 0 will produce a cached thread pool.
*/
public static void setThreadPoolSize(int size) {
mThreadPoolSize = size;
}

public static Flow getFlow(String flowName) {
Flow flow = sFlowMap.get(flowName);
return flow != null ? flow : new Flow(flowName);
}

/**
* start flow.
*
* @param flowName flow key, should be unique for each flow.
*/
public static void start(String flowName) {
Flow flow = sFlowMap.get(flowName);
if (flow != null) {
flow.start();
}
}

/**
* start flow.
*/
public static void start(Flow flow) {
flow.start();
}

/**
* Cancel the flow.
*
* @param flowName flow key, should be unique for each flow.
*/
public static void cancel(String flowName) {
Flow flow = sFlowMap.get(flowName);
if (flow != null) {
flow.cancel();
}
}

/**
* Get status of flow specified by given name, see {@link Status}.
*
* @param flowName flow key, should be unique for each flow.
* @return flow status in {@code STATUS_UNKNOWN}, {@code STATUS_PENDING_START},
* {@code STATUS_EXECUTING} and {@code STATUS_DONE}.
*/
public static int getFlowStatus(String flowName) {
Flow flow = sFlowMap.get(flowName);
return flow != null ? flow.getFlowStatus() : Status.STATUS_UNKNOWN;
}

/**
* Get thread pool used internally by Init library.
*
* @return thread pool
*/
public static ExecutorService getThreadPool() {
if (mThreadPoolSize <= 0) {
return Executors.newCachedThreadPool();
} else {
return Executors.newFixedThreadPool(mThreadPoolSize);
}
}
}
Loading

0 comments on commit b85e817

Please sign in to comment.