当前位置:操作系统 > 安卓/Android >>

android网络业务的封装与调度

 

手机客户端程序由于网络宽带的约束,尤其在GPRS网络环境下,大数据量的网络交互很大程度上降低应用的响应,影响用户体验。比如,如果做一个手机网盘客户端,在后台上传文件时(大数据量的交互),获取文件列表(命令类的交互)这个过程就显得太别慢。而我们的要求是希望这些命令类操作能尽快得到响应。

 

通常,在手机客户端,我们设计一个网络操作的管理器,来统一管理这些需要联网的操作。

具体做法是把网络操作封装成一个Command(或者说是Task),管理器实现特定的调度规则来调度运行这些Task。

这样做的好处至少有三:

一. 用Command封装了网络操作,使得这些操作与上传的业务分离,解除了强耦合。

二. 可以根据网络情况来确定来采用不同的调度规则,提高用户体验。

三. 重用,这些Task和TaskManager的代码在别的手机应用上基本上能照搬过去。

四. 扩展,当应用需要扩展新的业务时,只有扩展一个新的Command(或者说是Task),接受调度即可,易于扩展。

 

例子:

还是以上文提到的微盘为例,可以概括我们对管理器的设计要求有:

在Wifi网络环境下:

一:各种网络操作可以并行运行。

在GPRS网络环境下:

二:支持优先级抢占调度,命令类操作的优先级比数据传输类的优先级高,当命令类的Task(获取文件列表)提交后,打断数据传输的Task(如上传,下载),等命令类的任务运行完毕,再接着运行数据类任务(断点上传,下载)。

二:同一个优先级的任务可以并行运行,如多个命令一起在网络上传输。

 

实现思路:

TaskManager :

1. TaskManager开辟一个后台线程进行调度工作。

2. 由于要支持多个优先级的抢占调度,我们需要两个队列来维护运行中的Task和等待中的Task。

3. 由于Task的调度是基于优先级的,我们可以使用优先级队列,运行队列采用PriorityQueue,等待队列使用PriorityBlockingQueue,当没有网络业务需要运行时,调度线程阻塞挂起,避免空转。

4. TaskManager设计为单一实例(单一模式)。

5. 每个Task被调度运行时,该Task被从等待队列移动运行队列,当Task执行完毕时,从运行队列删除,唤醒调度线程进行新的调度。

下面是简单的设计代码:

www.zzzyk.com

 

public final class TaskEngine implements Runnable{

 

private PriorityQueue<Task> runningQueue;//运行的task队列

 

private PriorityBlockingQueue<Task> readyQueue;//就绪的task队列,准备接受调度的task列表

 

private final AtomicLong taskIdProducer = new AtomicLong(1);//Task Id生成器

 

private Object sheduleLock = new Object();//同步锁

 

private static TaskEngine instance;

 

public long addTask(BusinessObject bo){

  Task task = new Task(bo);

  long newTaskId = taskIdProducer.incrementAndGet();

  task.setTaskId(newTaskId);

  if(this.isWifiNetWork()){ //WIFI网络

  synchronized(sheduleLock){

  runningQueue.add(task);

  }

  new Thread(task).start();

  }else{ //GPRS网络

  if(readyQueue.offer(task)){ //task入就绪队列

  final ReentrantLock lock = this.lock;

  lock.lock();

  try{

  needSchedule.signal(); //唤醒调度线程重新调度

  }finally{

  lock.unlock();}

  }

}

return newTaskId;

}

 

public final void run(){//task调度逻辑

....

....

}

 

//挂起调度线程 当不需要调度时

private void waitUntilNeedSchedule() throws InterruptedException

{

.....

}

}

 

Task:

 

1. 对要执行的网络操作的封装。

 

2. Task执行完毕时,发Task结束信号,唤醒调度线程进行新的调度

 

3. Task要实现Comparable接口,才能让TaskManager的两个队列自动其包含的Task排序。

 

下面是简单的设计代码:

 

public class Task implements Runnable,Comparable<Task>{

 

private long taskId;

 

private BusinessObject bo;//封装网络操作的业务对象的抽象父类,

//它封装了具体的Command,保证了业务扩展中Task的接口不变

 

@Override

public void run() {

  this.onTaskStart();

  this.bo.execute();

  this.onTaskEnd();

}

 

private voidonTaskStart()

{...}

 

public int getPriority()

{...}

 

public void setPriority(intpriority)

{...}

 

@Override

public int compareTo(Task object1) {

return this.getPriority()>object1.getPriority()?-1:1;

}

} 

 

 

 

小注意事项:

 

Android对PriorityQueue的实现和Jdk中的实现有点不一样。

 

(PriorityQueue.java在android中的代码路径是usr\dalvik\libcore\luni\src\main\java\java\util)

 

PriorityQueue.remove(Object o) ;//从PriorityQueue中删除一个元素。

 

对于完成这个删除操作android和jdk都是分两个过程实现,一,找出待删除元素的索引index,二,删除index所在元素。

 

在JDK中,是通过调用元素的equals方法来找到待删除元素的索引,

 

private int indexOf(Object o) {

if (o != null) {

for (int i = 0; i < size; i++)

if (o.equals(queue[i]))

return i;

}

return -1;

}

 

在android中,是间接调用元素的compareTo方法判断结果是否为0来找到待删除元素的索引,

 

int targetIndex;

for (targetIndex = 0; targetIndex < size; targetIndex++) {

if (0 == this.compare((E) o, elements[targetIndex])) {

break;

}

}

 

private int compare(E o1, E o2) {

if (null != comparator) {

return comparator.compare(o1, o2);

}

return ((Comparable<? super E>) o1).compareTo(o2);

}

 

所以为了Task能在执行完毕时从PriorityQueue找到这个Task并删除之,需要在compareTo方法里在优先级相等时

 

返回0。

 

@Override

public int compareTo(Task object1) {

if(this.getPriority()==object1.getPriority())

return 0;

return this.getPriority()>object1.getPriority()?-1:1;

}

 

当是这样运行PriorityQueue.remove(Object o) 逻辑上只能删除PriorityQueue里第一个优先级与被删除的元素

 

优先级相等的元素(可能是待删除的元素也可能不是),有误删的可能,需要做如下修改:

 

@Override

public int compareTo(Task object1) {

if(this.getPriority()==object1.getPriority() && this.equals(object1))

return 0;

return this.getPriority()>object1.getPriority()?-1:1;

}

 

这样才能正确执行remove(Object o),删除指定的对象o。

 

 

 

个人觉得android这样设计使得remove(Object o)复杂化了,不然JDK中那么简洁。语义上也不是那么好懂了,

 

因为按通常理解,equals是判断两个对象是否相等,compareTo可能是对象的某个属性的比较(类别数据库中的order by),

 

而现在执行PriorityQueue.remove(Object o),这个对象o明明在容器PriorityQueue中,却删除不了,除非去翻看android中PriorityQueue的实现代码,然后重写compareTo这个方法。这样的API使用时比较容易出错,应该不符号良好的API设计规范

 

吧。

 

完整的代码实现如下:

 

View Code

  1 * @类名:TaskEngine

  2  * @创建:baiyingjun (devXiaobai@gmail.com)

  3  * @创建日期:2011-7-7

  4  * @说明:task调度引擎

  5  ***************************************************/

  6 public final class TaskEngine implements Ru

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,