关于Android的异步操作,我在文章中介绍了两种方法,一种是采用线程,另一种是采用AsyncTask,今天再深入探讨下另一种模型:命令式的异步任务管理。
一般而言,单一的异步操作,比如从指定的网址下载一个图片采用线程或者AsyncTask是很方便的;如果异步任务是需要循环处理,比如定时更新某个UI控件,那么,可以通过定时器或者线程+Sleep循环来实现。那么,还有这么一种需求,需要你通过命令来与你的异步线程进行交互,比如你要远程操作一台设备,可以执行:打开、获取参数、设置参数、发送命令、关闭等等,这样的需求下,你的确可以通过对每一个命令开启一个线程的方式来实现,然而还有更好地方法么?
针对这种需求,我们来一起设计了一个基于命令模式的异步任务线程,在该线程启动后,在你没有发送命令的情况下,休眠等待;你可以连续向该线程发送一系列的命令,该线程会唤醒,并依次执行你所发送的命令,并在需要的情况下回调通知命令的发生者。
1. 命令模式
根据需求,我们可以知道,这样的设计采用命令模式最好不过了,所谓命令模式,就是将一个请求或者操作封装到一个对象中,命令的管理者不需要知道每个命令的具体参数和执行方法,只需要通过队列将命令对象缓冲起来,并在需要的时候依次调用命令对象的执行接口即可。
在这里,我们的任务就可以抽象为命令模式中的“命令”,首先设计任务的接口:
public interface Tasker { public void execute();}
2. 任务的定义
根据命令模式的定义,每个任务都可以设计为一个类,实现该Tasker的接口,作为示例,我实现了Tasker接口的对象的两个简单的任务类:
(1)“空任务”类,啥都不做
public class EmptyTasker implements Tasker { @Override public void execute() { }}
(2)“延时任务”类,负责在指定的延时之后执行任务
public class DelayTasker implements Tasker { private final long mDelayTime; private final TimeArrivedListener mListener; //回调函数,通知外界延时时间到 public interface TimeArrivedListener { public void onDelayTimeArrived(); } //通过构造函数传递所需的参数 public DelayTasker( long delayTime, TimeArrivedListener listener){ mDelayTime = delayTime; mListener = listener; } @Override public void execute() { try { Thread.sleep(mDelayTime); mListener.onDelayTimeArrived(); } catch (InterruptedException e) { e.printStackTrace(); } }}
3. 任务管理者
任务管理者负责管理任务队列,实现任务的执行,同时向使用者提供可访问的接口,主要包括如下实现:
(1) 任务执行线程
负责从任务队列取任务对象,并且调用任务的执行函数执行任务代码。
(2) 任务队列
以先进先出的形式管理任务队列,并且要注意多线程的读写安全。
(3) 调用接口
一般包括:初始化(开启任务线程)、逆初始化(关闭任务线程)、添加命令
我设计的任务管理者代码如下,你可以根据自己的需求添加/修改/删除接口或者实现细节。
public class TaskExecutor { //控制线程执行/退出的标志变量 private volatile boolean mIsRunning = true; //锁和条件变量 private Lock mLock = new ReentrantLock(); private Condition mCondition = mLock.newCondition(); //任务列表 private QueuemTaskerQueue = new LinkedList (); //初始化函数,开启任务等待线程 public void initilize() { new Thread(new WorkRunnable()).start(); } //销毁函数,关闭任务等待线程 public void destroy() { //线程退出命令 mIsRunning = false; //添加一个任务,唤醒mCondition.await addTasker(new EmptyTasker()); } //添加一个新任务 public void addTasker( Tasker tasker ) { mLock.lock(); mTaskerQueue.add(tasker); mCondition.signal(); mLock.unlock(); } //获取下一个任务,如果任务列表为空,则阻塞 private Tasker getNextTasker() { mLock.lock(); try { //如果任务队列为空,则阻塞等待 while( mTaskerQueue.isEmpty() ) { mCondition.await(); } //返回队列首任务,并从队列删除掉 return mTaskerQueue.poll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { mLock.unlock(); } return null; } //任务等待/执行线程 private class WorkRunnable implements Runnable { @Override public void run() { while(mIsRunning) { Tasker tasker = getNextTasker(); tasker.execute(); } Log.d("TaskExecutor", "WorkRunnable run exit ! "); } }}
4. 使用案例
这里,我简单地应用上述异步任务线程完成了一个app示例,该app只有一个Button,每点击一次就延时1s弹出一个Toast消息,当你连续点击Button的时候,就会从1往后不断加1,连续的显示。
该app的MainActivity代码如下:
public class MainActivity extends Activity implements TimeArrivedListener { private TaskExecutor mTaskExecutor; private int mClickTimes = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTaskExecutor = new TaskExecutor(); mTaskExecutor.initilize(); } @Override protected void onDestroy() { mTaskExecutor.destroy(); super.onDestroy(); } public void onClickDelay(View v) { //每点击一次按钮,则延时1s,弹出消息提示 mTaskExecutor.addTasker(new DelayTasker(1000, this)); } @Override public void onDelayTimeArrived() { mClickTimes++; this.runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this,"This is the " + mClickTimes + " times click" , Toast.LENGTH_SHORT).show(); } }); }}
基于命令模式的异步任务线程就探讨到这里了,在你的Android工程中可以很容易地移植TaskExecutor类,本工程的项目代码见博文后面的附件,有任何疑问欢迎留言或者来信lujun.hust@gmail.com交流,一起探讨和完善这种异步任务线程。