本文讨论一下异步链式调用的设计与实现。

考虑如下情况:

情况1:

访问网络(或其他耗时的事情)。通常的做法是:

1、显示一个ProgressDialog对话框,提示用户。

2、启动工作线程来执行耗时操作。

3、发送消息到关联到主线程的Handler里面,关闭对话框。

情况2:

从网络下载一个zip文件,下载完成之后,询问用户是否执行解压操作。通常的合理做法:

1、显示一个ProgressDialog对话框,提示用户。

2、启动线程执行下载操作。

3、发送消息到关联到主线程的Handler里面,关闭对话框,然后启动一个询问对话框。

4、用户如果点击[YES],显示一个ProgressDialog对话框。

5、启动用线程执行解压操作。

6、发送消息到关联到主线程的Handler里面,关闭对话框。

通常情况下,在Android我们有两种方式来使用线程,一是Thread + Handler这种标准方式,另外一种是使用AsyncTask类。

实现这两个情况的缺点:

1、定义Handler,发送消息,使得代码变得复杂,不易理解。

2、发送消息是异步处理,在某些情况下可能需要做等待操作。

3、流程执行混乱,不是流水作业。

基于以上情况,我们能不能也像流水线的操作那么调用我们的回调(Callback),使用者只关心第一步干什么,第二步干什么,如果能这样的话,那么在哪步做什么都能明确定义出来,这就是链式调用。

请看下面的链式调用的写法(JavaScript):

  1. Async.go(initialArgument)
  2. .next(firstAsyncOperation)
  3. .next(secondAsyncOperation)
  4. .next(thirdAsyncOperation)
  5. .next(function(finalResult) { alert(finalResult); })

用户只需要添加每一步的task到一个队列里面,然后执行,这些task就会按添加的顺序执行,从而实现链式调用。

这种思想还不挺好的,在写代码的时候,我们更加关注实现的逻辑,不需要去考虑发什么消息等。只考虑第一步干什么,第二步干什么等。这样在以后代码维护时也比较好。

我们能不能设计出一个Android版本的异步链式调用的模块呢,请看下面。

Task

我们抽象出每一步要做的事情,定义一个Task类,它是一个抽象类,有如下核心属性和方法:

mRunInBackground
用来指示这个Task是运行在后台线程还是运行在主线程。

onExecuter(TaskOperation)
我们需要实现该方法,在这里面执行我们想要做的事情。

onProgressUpdate(Object)
我们可以重写该方法,来更新我们所做事情的进度,这个方法运行在主线程。

注意:在使用时,你必须指定这个Task是运行在UI线程还是后台线程。

TaskOperation

1)这个类里面包含了task的运行参数,上一个task的输出将会作为下一个task的输入。

2)它可以指示继续或暂停执行下一个task。

3)它里面使用了一个object[]来存储参数。

TaskManager

1)管理task队列,始终从队列第一个开始执行,执行一个task后,这个task将从队列出移除。

2)内部创建了一个带有消息循环的线程。

3)执行task时,判断其运行的线程环境,如果运行在UI线程,发送消息到UI的Handler来执行。

4)内部封装了Handler,用户不用关心是否发送消息。

5)核心方法有:
     - next(Task)
     - execute()
     - execute(TaskOperation)
     - cancelCurrentTask()
     - removeTasks()
     - publishProgress(Object)

这里只是给了一个最基本的设计思路,现在该设计还有完善的地方,具体的实现请参考相关的代码和测试工程。

实现代码

Task.java

  1. /*
  2. * System: CoreLib
  3. * @version 1.00
  4. *
  5. * Copyright (C) 2010, LiHong
  6. *
  7. */
  8.  
  9. package com.nj1s.lib.task;
  10.  
  11. import java.util.concurrent.atomic.AtomicBoolean;
  12.  
  13. /**
  14. * <p>
  15. * This method define the task used to do something. Typically you should override
  16. * {@link #onExecute(TaskOperation)} method to do you things, on the other hand, you
  17. * also can override the {@link #onProgressUpdate(Object)} method to get the progress of
  18. * you things.
  19. * </p>
  20. *
  21. * <p>
  22. * NOTE:
  23. * There is an very important thing you should pay attention to, you must specify the task
  24. * is running on background thread or UI thread, the default flag is true ---- running on
  25. * background thread.
  26. * </p>
  27. *
  28. * @author LeeHong
  29. *
  30. * @date 2012/10/30
  31. */
  32. public abstract class Task
  33. {
  34. /**
  35. * The id of the task, typically you need NOT set it, if will set automatically when you
  36. * add this task into {@link TaskManager} class.
  37. */
  38. private int mId = 0;
  39.  
  40. /**
  41. * The task name.
  42. */
  43. private String mName = null;
  44.  
  45. /**
  46. * Indicate this task is canceled or not.
  47. */
  48. private AtomicBoolean mCancelled = new AtomicBoolean(false);
  49.  
  50. /**
  51. * The task status, default value is {@link Status#PENDING}.
  52. */
  53. private volatile Status mStatus = Status.PENDING;
  54.  
  55. /**
  56. * The running status, default value is {@link RunningStatus#UI_THREAD}.
  57. */
  58. private volatile RunningStatus mRunStatus = RunningStatus.UI_THREAD;
  59.  
  60. /**
  61. * Indicates the current status of the task. Each status will be set only once
  62. * during the lifetime of a task.
  63. */
  64. public enum Status
  65. {
  66. /**
  67. * Indicates that the task has not been executed yet.
  68. */
  69. PENDING,
  70.  
  71. /**
  72. * Indicates that the task is running.
  73. */
  74. RUNNING,
  75.  
  76. /**
  77. * Indicates that {@link Task#onExecute} has finished.
  78. */
  79. FINISHED,
  80. }
  81.  
  82. /**
  83. * Indicate the task running status.
  84. */
  85. public enum RunningStatus
  86. {
  87. /**
  88. * Indicate the task is running in the background thread.
  89. */
  90. WORK_THREAD,
  91.  
  92. /**
  93. * Indicate the task is running in the UI thread.
  94. */
  95. UI_THREAD,
  96. }
  97.  
  98. /**
  99. * The constructor method.
  100. *
  101. * @param runInBackground
  102. * @param name
  103. */
  104. public Task(Task task)
  105. {
  106. this.mRunStatus = task.mRunStatus;
  107. this.mName = task.mName;
  108. this.mStatus = task.mStatus;
  109. }
  110.  
  111. /**
  112. * The constructor method.
  113. *
  114. * @param status indicate the task is running in background thread or not.
  115. */
  116. public Task(RunningStatus status)
  117. {
  118. this(status, null);
  119. }
  120.  
  121. /**
  122. * The constructor method.
  123. *
  124. * @param runInBackground
  125. * @param name
  126. */
  127. public Task(RunningStatus status, String name)
  128. {
  129. mRunStatus = status;
  130. mName = name;
  131. }
  132.  
  133. /**
  134. * Override this method to do you works.
  135. *
  136. * @param operation The operation is passed from previous task.
  137. *
  138. * @return Typically you should return the {@link #operation}.
  139. */
  140. public abstract TaskOperation onExecute(TaskOperation operation);
  141.  
  142. /**
  143. * Called when change the progress, this method is running in UI thread.
  144. *
  145. * @param progresses
  146. */
  147. public void onProgressUpdate(Object progresses)
  148. {
  149. }
  150.  
  151. /**
  152. * Cancel the task.
  153. */
  154. public void cancel()
  155. {
  156. mCancelled.set(true);
  157. }
  158.  
  159. /**
  160. * Indicate the task is canceled or not.
  161. *
  162. * @return
  163. */
  164. public boolean isCancelled()
  165. {
  166. return mCancelled.get();
  167. }
  168.  
  169. /**
  170. * Get the running status.
  171. *
  172. * @return
  173. */
  174. public RunningStatus getRunningStatus()
  175. {
  176. return mRunStatus;
  177. }
  178.  
  179. /**
  180. * Set the name of the task.
  181. *
  182. * @param name The task name.
  183. */
  184. public void setTaskName(String name)
  185. {
  186. mName = name;
  187. }
  188.  
  189. /**
  190. * Get the task name.
  191. *
  192. * @return the task name.
  193. */
  194. public String getTaskName()
  195. {
  196. return mName;
  197. }
  198.  
  199. /**
  200. * Set the status of the task.
  201. *
  202. * @param status
  203. */
  204. public void setStatus(Status status)
  205. {
  206. mStatus = status;
  207. }
  208.  
  209. /**
  210. * Get the status of the task.
  211. *
  212. * @return
  213. */
  214. public Status getStatus()
  215. {
  216. return mStatus;
  217. }
  218.  
  219. /**
  220. * Set the id of the task.
  221. *
  222. * @param id
  223. */
  224. public void setTaskId(int id)
  225. {
  226. mId = id;
  227. }
  228.  
  229. /**
  230. * Get the task id.
  231. */
  232. public int getTaskId()
  233. {
  234. return mId;
  235. }
  236.  
  237. /**
  238. * @see java.lang.Object#toString()
  239. */
  240. @Override
  241. public String toString()
  242. {
  243. StringBuilder sb = new StringBuilder();
  244. sb.append("name = ").append(mName).append(" ");
  245. sb.append("id = ").append(mId).append(" ");
  246. sb.append(super.toString());
  247.  
  248. return sb.toString();
  249. }
  250. }

TaskOperation.java

  1. /*
  2. * System: CoreLib
  3. * @version 1.00
  4. *
  5. * Copyright (C) 2010, LiHong.
  6. */
  7.  
  8. package com.nj1s.lib.task;
  9.  
  10. import java.util.ArrayList;
  11.  
  12. import com.nj1s.lib.task.TaskManager.TaskManagerState;
  13.  
  14. /**
  15. * The task operation, it wraps the task parameter, etc.
  16. *
  17. * @author LeeHong
  18. *
  19. * @date 2012/10/30
  20. */
  21. public class TaskOperation
  22. {
  23. /**
  24. * The task parameter.
  25. */
  26. private Object[] mNextTaskParams = null;
  27.  
  28. /**
  29. * The task manager status.
  30. */
  31. private TaskManagerState mTaskManagerStatus = TaskManagerState.CONTINUE;
  32.  
  33. /**
  34. * The constructor method.
  35. */
  36. public TaskOperation()
  37. {
  38. }
  39.  
  40. /**
  41. * The constructor method.
  42. *
  43. * @param nextTaskParams
  44. */
  45. public TaskOperation(Object[] nextTaskParams)
  46. {
  47. mNextTaskParams = nextTaskParams;
  48. }
  49.  
  50. /**
  51. * The constructor method.
  52. *
  53. * @param operation
  54. */
  55. public TaskOperation(TaskOperation operation)
  56. {
  57. setTaskParams(operation);
  58. }
  59.  
  60. /**
  61. * Get the task parameter.
  62. */
  63. public Object[] getTaskParams()
  64. {
  65. return mNextTaskParams;
  66. }
  67.  
  68. /**
  69. * Set the task parameter.
  70. *
  71. * @param params
  72. */
  73. public void setTaskParams(Object[] params)
  74. {
  75. mNextTaskParams = params;
  76. }
  77.  
  78. /**
  79. * Set the task parameters.
  80. *
  81. * @param operation
  82. */
  83. public void setTaskParams(TaskOperation operation)
  84. {
  85. if (operation == this)
  86. {
  87. throw new IllegalArgumentException("The argument can NOT be self.");
  88. }
  89.  
  90. if (null == operation)
  91. {
  92. return;
  93. }
  94.  
  95. Object[] params = operation.getTaskParams();
  96. if (null == params)
  97. {
  98. return;
  99. }
  100.  
  101. ArrayList<Object> paramsList = new ArrayList<Object>();
  102.  
  103. if (null != mNextTaskParams)
  104. {
  105. for (Object param : mNextTaskParams)
  106. {
  107. paramsList.add(param);
  108. }
  109. }
  110.  
  111. for (Object param : params)
  112. {
  113. paramsList.add(param);
  114. }
  115.  
  116. mNextTaskParams = paramsList.toArray();
  117. }
  118.  
  119. /**
  120. * @param status the mTaskManagerStatus to set
  121. */
  122. public void setTaskManagerStatus(TaskManagerState status)
  123. {
  124. mTaskManagerStatus = status;
  125. }
  126.  
  127. /**
  128. * @return the mTaskManagerStatus
  129. */
  130. public TaskManagerState getTaskManagerStatus()
  131. {
  132. return mTaskManagerStatus;
  133. }
  134.  
  135. /**
  136. * Append the specified parameter to the end of the parameter list.
  137. *
  138. * @param param
  139. */
  140. public void appendTaskParam(Object param)
  141. {
  142. appendTaskParams(new Object[] {param});
  143. }
  144.  
  145. /**
  146. * Append the specified parameter to the end of the parameter list.
  147. *
  148. * @param params
  149. */
  150. public void appendTaskParams(Object[] params)
  151. {
  152. if (null != params)
  153. {
  154. TaskOperation operation = new TaskOperation(params);
  155. setTaskParams(operation);
  156. }
  157. }
  158. }

TaskManager.java

  1. /*
  2. * System: CoreLib
  3. * @version 1.00
  4. *
  5. * Copyright (C) 2010, LiHong.
  6. *
  7. */
  8.  
  9. package com.nj1s.lib.task;
  10.  
  11. import java.util.HashMap;
  12. import java.util.LinkedList;
  13.  
  14. import android.os.Handler;
  15. import android.os.Looper;
  16. import android.os.Message;
  17. import android.text.TextUtils;
  18. import android.util.Log;
  19.  
  20. import com.nj1s.lib.task.Task.RunningStatus;
  21. import com.nj1s.lib.task.Task.Status;
  22. import com.nj1s.lib.thread.ThreadWorker;
  23.  
  24. /**
  25. * This class is used to manager the tasks so that you can add many tasks into the task manger
  26. * and these tasks will be running one by one.
  27. *
  28. * <h2>Example:</h2>
  29. * <pre class="prettyprint">
  30. * private void showProgressDialog()
  31. * {
  32. * final ProgressDialog mProgressDialog = null;
  33. * final TaskManager taskManager = new TaskManager("ShowProgressDlg");
  34. *
  35. * // Set the state change listener.
  36. * taskManager.setStateChangeListener(new IStateChangeListener()
  37. * {
  38. * public void onStateChanged(TaskManager taskManager, State oldState, State newState)
  39. * {
  40. * Toast.makeText(ShowProgressDlgActivity.this, " onStateChanged state = " + newState, Toast.LENGTH_SHORT).show();
  41. * }
  42. * });
  43. *
  44. * taskManager
  45. * .next(new Task(Task.RunningStatus.UI_THREAD)
  46. * {
  47. * public TaskOperation onExecute(TaskOperation operation)
  48. * {
  49. * mProgressDialog = new ProgressDialog(ShowProgressDlgActivity.this);
  50. * mProgressDialog.setTitle("Download");
  51. * mProgressDialog.setMessage("Downlonding data from server...");
  52. * mProgressDialog.setCancelable(false);
  53. * mProgressDialog.show();
  54. *
  55. * return null;
  56. * }
  57. * })
  58. * .next(new Task(Task.RunningStatus.WORK_THREAD)
  59. * {
  60. * public TaskOperation onExecute(TaskOperation operation)
  61. * {
  62. * // Simulate the work thread.
  63. * sleep(5000);
  64. *
  65. * return null;
  66. * }
  67. * })
  68. * .next(new Task(Task.RunningStatus.UI_THREAD)
  69. * {
  70. * public TaskOperation onExecute(TaskOperation operation)
  71. * {
  72. * if (null != mProgressDialog && mProgressDialog.isShowing())
  73. * {
  74. * mProgressDialog.dismiss();
  75. * mProgressDialog = null;
  76. * }
  77. *
  78. * return null;
  79. * }
  80. * })
  81. * .execute(); // Call this method to execute these tasks.
  82. * }
  83. * </pre>
  84. *
  85. * <h2>Note:</h2>
  86. * <pre>
  87. * The {@link Task} class must be specified the task running state, one of the enum {@link Task#RunningStatus}.
  88. * </pre>
  89. *
  90. * @author LeeHong
  91. *
  92. * @date 2012/10/30
  93. *
  94. * @see {@link Task}
  95. * @see {@link TaskOperation}
  96. */
  97. public class TaskManager
  98. {
  99. /**
  100. * Execute task message.
  101. */
  102. private static final int MESSAGE_POST_EXECUTE = 0x01;
  103.  
  104. /**
  105. * Update progress message.
  106. */
  107. private static final int MESSAGE_POST_PROGRESS = 0x02;
  108.  
  109. /**
  110. * The state change listener.
  111. */
  112. public interface IStateChangeListener
  113. {
  114. /**
  115. * Called when the task manager's state is changed. This method will be called in
  116. * UI thread.
  117. *
  118. * @param taskManager Which task manager's state changed.
  119. * @param oldState The old state.
  120. * @param newState The new state.
  121. */
  122. public void onStateChanged(TaskManager taskManager, State oldState, State newState);
  123. }
  124.  
  125. /**
  126. * A representation of a task manager's state. A given thread may only be in one
  127. * state at a time.
  128. */
  129. public enum State
  130. {
  131. /**
  132. * The task manager has been created, but has never been started.
  133. */
  134. NEW,
  135.  
  136. /**
  137. * Indicate the task manager is running one task.
  138. */
  139. RUNNING,
  140.  
  141. /**
  142. * Indicate the task manager is paused, typically call {@link #pause()} method.
  143. */
  144. PAUSED,
  145.  
  146. /**
  147. * All tasks are finished.
  148. */
  149. FINISHED,
  150. }
  151.  
  152. /**
  153. * The status of the {@link TaskManager} class.
  154. */
  155. public enum TaskManagerState
  156. {
  157. /**
  158. * Continue the task manager to run next task.
  159. */
  160. CONTINUE,
  161.  
  162. /**
  163. * Indicate the task manager pause to run next task.
  164. */
  165. PAUSE,
  166. }
  167.  
  168. /**
  169. * The running task manager collection.
  170. */
  171. private static HashMap<String, TaskManager> s_taskManagers = new HashMap<String, TaskManager>();
  172.  
  173. /**
  174. * The task list.
  175. */
  176. private LinkedList<Task> mTaskList = new LinkedList<Task>();
  177.  
  178. /**
  179. * The task operation, it will pass from first task to the last task.
  180. */
  181. private TaskOperation mTaskOperation = new TaskOperation();
  182.  
  183. /**
  184. * The running thread worker, it own a looper which will be alive until you call
  185. * {@link ThreadWorker#quit()} method.
  186. */
  187. private ThreadWorker mThreadWorker = null;
  188.  
  189. /**
  190. * The current perform task, may be null.
  191. */
  192. private Task mCurTask = null;
  193.  
  194. /**
  195. * The state of the task manager.
  196. */
  197. private State mState = State.NEW;
  198.  
  199. /**
  200. * The name of the task manager.
  201. */
  202. private String mName = null;
  203.  
  204. /**
  205. * The listener.
  206. */
  207. private IStateChangeListener mListener = null;
  208.  
  209. /**
  210. * The background thread handler, which is associated to a background thread looper.
  211. */
  212. private Handler mThreadHandler = null;
  213.  
  214. /**
  215. * The UI thread handler.
  216. */
  217. private Handler mUIHandler = new Handler(Looper.getMainLooper())
  218. {
  219. @Override
  220. public void handleMessage(Message msg)
  221. {
  222. switch (msg.what)
  223. {
  224. case MESSAGE_POST_EXECUTE:
  225. Task task = (Task)msg.obj;
  226. executeTask(task);
  227. // Try to run next task if possible.
  228. runNextTask();
  229. break;
  230.  
  231. case MESSAGE_POST_PROGRESS:
  232. postProgress(msg.obj);
  233. break;
  234. }
  235. }
  236. };
  237.  
  238. /**
  239. * The constructor method.
  240. */
  241. public TaskManager()
  242. {
  243. }
  244.  
  245. /**
  246. * The constructor method.
  247. *
  248. * @param name The name of the task manager.
  249. */
  250. public TaskManager(String name)
  251. {
  252. mName = name;
  253. }
  254.  
  255. /**
  256. * Add the task to {@link TaskManager} class.
  257. *
  258. * @param task The task.
  259. *
  260. * @return the {@link TaskManager} object.
  261. */
  262. public TaskManager next(Task task)
  263. {
  264. if (null != task)
  265. {
  266. synchronized (mTaskList)
  267. {
  268. int id = mTaskList.size() + 1;
  269. task.setTaskId(id);
  270. mTaskList.add(task);
  271. }
  272. }
  273. else
  274. {
  275. throw new NullPointerException("task is null");
  276. }
  277.  
  278. return this;
  279. }
  280.  
  281. /**
  282. * Start to execute the tasks in the task manager.
  283. */
  284. public void execute()
  285. {
  286. if (mTaskList.size() > 0)
  287. {
  288. startThread();
  289.  
  290. // Set the task to RUNNING.
  291. setState(State.RUNNING);
  292.  
  293. // Perform the runnable in the handler which is associated to the background thread.
  294. mThreadHandler.post(new Runnable()
  295. {
  296. @Override
  297. public void run()
  298. {
  299. doInBackground();
  300. }
  301. });
  302. }
  303. else
  304. {
  305. quitLooper();
  306. }
  307. }
  308.  
  309. /**
  310. * Start to execute the tasks in the task manager with the specified parameter.
  311. *
  312. * @param operation The task operation contains the task parameter.
  313. */
  314. public void execute(TaskOperation operation)
  315. {
  316. if (null != operation)
  317. {
  318. mTaskOperation = operation;
  319. }
  320.  
  321. execute();
  322. }
  323.  
  324. /**
  325. * Post execute a task which will be running in UI thread.
  326. *
  327. * @param task the task to be running.
  328. */
  329. public void postExecute(Task task)
  330. {
  331. if (null == task)
  332. {
  333. throw new NullPointerException("Task can NOT be null.");
  334. }
  335.  
  336. final Task runTask = task;
  337.  
  338. // If the task running status is UI_THREAD.
  339. if (RunningStatus.UI_THREAD == runTask.getRunningStatus())
  340. {
  341. // The task is running in UI thread.
  342. mUIHandler.post(new Runnable()
  343. {
  344. @Override
  345. public void run()
  346. {
  347. executeTask(runTask);
  348. }
  349. });
  350. }
  351. }
  352.  
  353. /**
  354. * Publish the task progress, if you call this method, the {@link Task#onProgressUpdate(Object)}
  355. * method will be called, which is running in the UI thread.
  356. *
  357. * @param progresses The progress.
  358. */
  359. public void publishProgress(Object progresses)
  360. {
  361. mUIHandler.obtainMessage(MESSAGE_POST_PROGRESS, progresses).sendToTarget();
  362. }
  363.  
  364. /**
  365. * Cancel the current running task.
  366. */
  367. public void cancelCurrentTask()
  368. {
  369. if (null != mCurTask)
  370. {
  371. mCurTask.cancel();
  372. }
  373. }
  374.  
  375. /**
  376. * Remove the tasks in the list.
  377. */
  378. public void removeTasks()
  379. {
  380. synchronized (mTaskList)
  381. {
  382. if (mTaskList.size() > 0)
  383. {
  384. mTaskList.clear();
  385. quitLooper();
  386. }
  387. }
  388. }
  389.  
  390. /**
  391. * Remove the specified task.
  392. *
  393. * @param task The task to be removed.
  394. */
  395. public void removeTask(Task task)
  396. {
  397. synchronized (mTaskList)
  398. {
  399. mTaskList.remove(task);
  400.  
  401. if (mTaskList.isEmpty())
  402. {
  403. quitLooper();
  404. }
  405. }
  406. }
  407.  
  408. /**
  409. * Set the state change listener.
  410. *
  411. * @param listener
  412. */
  413. public void setStateChangeListener(IStateChangeListener listener)
  414. {
  415. mListener = listener;
  416. }
  417.  
  418. /**
  419. * Get the task operation.
  420. *
  421. * @return
  422. */
  423. public TaskOperation getTaskOperation()
  424. {
  425. return mTaskOperation;
  426. }
  427.  
  428. /**
  429. * @return the mName
  430. */
  431. public String getName()
  432. {
  433. return mName;
  434. }
  435.  
  436. /**
  437. * Pause the worker thread.
  438. */
  439. public void pause()
  440. {
  441. if (null != mThreadWorker)
  442. {
  443. setState(State.PAUSED);
  444.  
  445. mThreadWorker.pause();
  446. }
  447. }
  448.  
  449. /**
  450. * Resume the worker thread from the waiting status.
  451. */
  452. public void resume()
  453. {
  454. if (null != mThreadWorker)
  455. {
  456. setState(State.RUNNING);
  457.  
  458. mThreadWorker.restart();
  459. }
  460. }
  461.  
  462. /**
  463. * Quit the looper so that the thread can finish correctly.
  464. */
  465. public void quitLooper()
  466. {
  467. if (null != mThreadWorker)
  468. {
  469. mThreadWorker.quit();
  470. mThreadWorker = null;
  471. }
  472.  
  473. mThreadHandler = null;
  474.  
  475. // Set the task to FINISHED.
  476. setState(State.FINISHED);
  477. }
  478.  
  479. /**
  480. * Blocks the current thread ({@link Thread#currentThread()}) until the receiver finishes its execution and dies.
  481. */
  482. public final void join()
  483. {
  484. if (null != mThreadWorker)
  485. {
  486. mThreadWorker.join();
  487. }
  488. }
  489.  
  490. /**
  491. * Get the task manager state.
  492. *
  493. * @return
  494. */
  495. public State getState()
  496. {
  497. return mState;
  498. }
  499.  
  500. /**
  501. * Get the running task manager.
  502. *
  503. * @return HashMap<String, TaskManager>, the task manager's name is the key, and the
  504. * task manager object is the value.
  505. */
  506. public static HashMap<String, TaskManager> getTaskManagers()
  507. {
  508. return s_taskManagers;
  509. }
  510.  
  511. /**
  512. * @see java.lang.Object#toString()
  513. */
  514. @Override
  515. public String toString()
  516. {
  517. StringBuilder sb = new StringBuilder();
  518. sb.append("Name = ").append(mName).append(" ");
  519. sb.append("State = ").append(mState).append(" ");
  520. sb.append(super.toString());
  521.  
  522. return sb.toString();
  523. }
  524.  
  525. /**
  526. * print task execute status
  527. *
  528. * @param task
  529. */
  530. protected void printExecuteTaskState(Task task)
  531. {
  532. Log.d("TaskManager", " Executer the task: " + task.toString());
  533. }
  534.  
  535. /**
  536. * Set the state.
  537. *
  538. * @param state
  539. */
  540. private void setState(State state)
  541. {
  542. final State oldState = mState;
  543. final State newState = state;
  544. mState = state;
  545.  
  546. if (mState == State.FINISHED)
  547. {
  548. popTaskManager(this);
  549. }
  550. else
  551. {
  552. pushTaskManager(this);
  553. }
  554.  
  555. if (oldState != newState)
  556. {
  557. printTaskManagerState(oldState, newState);
  558. performStateChange(oldState, newState);
  559. }
  560. }
  561.  
  562. /**
  563. * Call this method to start the work thread if can.
  564. */
  565. private void startThread()
  566. {
  567. if (null == mThreadWorker)
  568. {
  569. String name = TextUtils.isEmpty(mName) ? this.toString() : mName;
  570. String threadName = "TaskManager_Thread_" + name;
  571. mThreadWorker = new ThreadWorker(threadName);
  572. mThreadHandler = new Handler(mThreadWorker.getLooper());
  573. }
  574. }
  575.  
  576. /**
  577. * This method is running in the background thread.
  578. */
  579. private void doInBackground()
  580. {
  581. mCurTask = null;
  582.  
  583. if (mTaskList.isEmpty())
  584. {
  585. return;
  586. }
  587.  
  588. Task task = mTaskList.get(0);
  589. mCurTask = task;
  590.  
  591. // Remove the first item in the list.
  592. synchronized (mTaskList)
  593. {
  594. mTaskList.remove(0);
  595. }
  596.  
  597. // If the task is allowed to be running in background thread, we execute the task
  598. // now, the doInBackground() method is running in the background thread.
  599. switch (task.getRunningStatus())
  600. {
  601. case WORK_THREAD:
  602. executeTask(task);
  603. // Try to run next task if possible.
  604. runNextTask();
  605. break;
  606.  
  607. case UI_THREAD:
  608. // Send a message to the UI handler to executer the task.
  609. mUIHandler.obtainMessage(MESSAGE_POST_EXECUTE, task).sendToTarget();
  610. break;
  611. }
  612. }
  613.  
  614. /**
  615. * Run the next task.
  616. */
  617. private void runNextTask()
  618. {
  619. // If run next, call the execute() method again.
  620. if (isRunNext())
  621. {
  622. execute();
  623. }
  624. }
  625.  
  626. /**
  627. * Check whether run the next task if has one.
  628. *
  629. * @return true if run next task, otherwise false.
  630. */
  631. private boolean isRunNext()
  632. {
  633. boolean isRunNext = true;
  634. boolean hasNext = false;
  635.  
  636. if (null != mTaskOperation)
  637. {
  638. isRunNext = (mTaskOperation.getTaskManagerStatus() == TaskManagerState.CONTINUE);
  639. }
  640.  
  641. if (null != mTaskList)
  642. {
  643. hasNext = mTaskList.size() > 0;
  644. }
  645.  
  646. // No next task, quit the thread.
  647. if (!hasNext)
  648. {
  649. quitLooper();
  650. }
  651.  
  652. return (isRunNext && hasNext);
  653. }
  654.  
  655. /**
  656. * Execute the task, if will call {@link Task#onExecute(TaskOperation)} method.
  657. *
  658. * @param task The task object.
  659. */
  660. private void executeTask(Task task)
  661. {
  662. if (null != task)
  663. {
  664.  
  665. // Set the status of the task.
  666. task.setStatus(Status.RUNNING);
  667.  
  668. // Print the task state.
  669. this.printExecuteTaskState(task);
  670.  
  671. try
  672. {
  673. // Avoid the exception from task interrupting the task manager works.
  674. mTaskOperation = task.onExecute(mTaskOperation);
  675. }
  676. catch (Exception e)
  677. {
  678. e.printStackTrace();
  679. }
  680.  
  681. // Set the status of the task.
  682. task.setStatus(Status.FINISHED);
  683.  
  684. // Print the task state.
  685. this.printExecuteTaskState(task);
  686. }
  687. }
  688.  
  689. /**
  690. * Post the progress, it will call {@link Task#onProgressUpdate(Object progresses)} method.
  691. *
  692. * @param progresses
  693. */
  694. private void postProgress(Object progresses)
  695. {
  696. if (null != mCurTask)
  697. {
  698. mCurTask.onProgressUpdate(progresses);
  699. }
  700. }
  701.  
  702. /**
  703. * Perform the state change.
  704. *
  705. * @param oldState
  706. * @param newState
  707. */
  708. private void performStateChange(final State oldState, final State newState)
  709. {
  710. if (null != mListener)
  711. {
  712. mUIHandler.post(new Runnable()
  713. {
  714. @Override
  715. public void run()
  716. {
  717. mListener.onStateChanged(TaskManager.this, oldState, newState);
  718. }
  719. });
  720. }
  721. }
  722.  
  723. /**
  724. * Print the task manager information.
  725. *
  726. * @param oldState
  727. * @param newState
  728. */
  729. private void printTaskManagerState(final State oldState, final State newState)
  730. {
  731. Log.d("TaskManager", "TaskManager state changed, task manager = " + this.toString());
  732. }
  733.  
  734. /**
  735. * Push the task manager to the list.
  736. *
  737. * @param taskManager
  738. */
  739. private static void pushTaskManager(TaskManager taskManager)
  740. {
  741. if (null != taskManager)
  742. {
  743. String name = taskManager.getName();
  744. if (!TextUtils.isEmpty(name))
  745. {
  746. s_taskManagers.put(name, taskManager);
  747. }
  748. }
  749. }
  750.  
  751. /**
  752. * Pop the task manager from the list.
  753. * @param taskManager
  754. */
  755. private static void popTaskManager(TaskManager taskManager)
  756. {
  757. if (null != taskManager)
  758. {
  759. String name = taskManager.getName();
  760. s_taskManagers.remove(name);
  761. }
  762. }
  763. }

Android 异步链式调用设计的更多相关文章

  1. 使用Promise链式调用解决多个异步回调的问题

    使用Promise链式调用解决多个异步回调的问题 比如我们平常经常遇到的一种情况: 网站中需要先获取用户名,然后再根据用户名去获取用户信息.这里获取用户名getUserName()和获取用户信息get ...

  2. Android总结之链式调用(方法链)

    前言: 最近在学习总结Android属性动画的时候,发现Android的属性动画设计采用了链式调用的方式,然后又回顾了一下了以前接触的开源框架Glide也是采用链式调用的方式,还有最近火的一塌糊涂的R ...

  3. 如何写 JS 的链式调用 ---》JS 设计模式《----方法的链式调用

    1.以$ 函数为例.通常返回一个HTML元素或一个元素集合. 代码如下: function $(){ var elements = []; ;i<arguments.length;i++){ v ...

  4. Android异步回调中的UI同步性问题

    Android程序编码过程中,回调无处不在.从最常见的Activity生命周期回调开始,到BroadcastReceiver.Service以及Sqlite等.Activity.BroadcastRe ...

  5. 【转】Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Andr ...

  6. Java基础知识强化之网络编程笔记15:Android网络通信之 Android异步任务处理(AsyncTask使用)

         AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的 ...

  7. Android 异步消息处理机制 让你在深入了解 Looper、Handler、Message之间的关系

    转载请注明出处:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 非常多人面试肯定都被问到过,请问And ...

  8. Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

    转自:http://blog.csdn.net/lmj623565791/article/details/38377229 ,本文出自[张鸿洋的博客] 很多人面试肯定都被问到过,请问Android中的 ...

  9. Android异步处理之AsyncTaskLoader简单使用

    简介 不管是在Android应用开发还是Android平台开发中,异步处理通常是最基本的coding要求.如果你还在主线程中写一些数据库,网络请求,读写本地文件等操作的话那说明你还不是一个合格的And ...

随机推荐

  1. 通过DWR简化AJAX开发

    DWR(Direct Web Remoting)是一个WEB远程调用框架,采取了一个类似AJAX的新方法来动态生成基于JAVA类的JavaScript代码.这样WEB开发人员就可以在JavaScrip ...

  2. [poj 1265]Area[Pick定理][三角剖分]

    题意: 给出机器人移动的向量, 计算包围区域的内部整点, 边上整点, 面积. 思路: 面积是用三角剖分, 边上整点与GCD有关, 内部整点套用Pick定理. S = I + E / 2 - 1 I 为 ...

  3. tcp/ip协议listen函数中backlog參数的含义

    listen函数的定义例如以下所看到的: #include <sys/socket.h> int accept(int sockfd, struct sockaddr * restrict ...

  4. IOS系统对fixed定位支持不好的解决方法

    问题: IOS 中所有浏览器,当页面上的输入框获得焦点时,呼出键盘. 页面底部的导航栏(position:fixed)会被键盘顶到页面的中间. 而当输入框失去焦点时,导航栏停留在页面中间,造成页面错乱 ...

  5. Android屏幕大小适配问题解决

    转载: 一.一些基本概念 1.长度(真实长度):英寸.inch 2.分辨率:density 每英寸像素数  dpi(密度) 3.像素:px 4.dip的公式:px /dip=dpi/160  所以 d ...

  6. PHP - 遍历文件夹下的所有文件名

    /** * * 函数名:myreaddir($dir) * 作用:读取目录所有的文件名 * 参数:$dir 目录地址 * 返回值:文件名数组 * * */ function myreaddir($di ...

  7. MFC 只启动一个程序实例

    问题描述: 我们开发过程中可能会经常遇到,只启动一个程序实例.即一个程序启动之后,如果再次执行该程序,将会恢复之前打开的程序,而不是打开一个新的程序. 实现原理:利用FindWindow/FindWi ...

  8. transition与animation

    以前,一直都知道,transition是animation的一个简化版,甚至不算是动画,而是一种过渡. transition的用法 早两天用transition写了一个按钮滑动的效果,类似于IOS的设 ...

  9. HDU1686——Oulipo

    Problem Description The French author Georges Perec (1936–1982) once wrote a book, La disparition, w ...

  10. DOM querySelector选择器

    原生的强大DOM选择器querySelector 在传统的 JavaScript 开发中,查找 DOM 往往是开发人员遇到的第一个头疼的问题,原生的 JavaScript 所提供的 DOM 选择方法并 ...