因为之前参与的网络开发项目都遇到一些相同的问题:
1.大量的并发请求造成堵塞,特别是遇上让人无语的3G网络,无限loading。。。
2.一般来说一个网络请求都会用使用到一个异步线程,大量的线程创建、运行、销毁又造成了系统资源的浪费
3.请求结束得到结果后,如果需要更新UI,一个不小心忘了返回UI线程,各种崩溃。。。
 
前些日子跟同事商量能不能做个请求队列去进行控制,于是趁着热度没消退说干就干,建了个模型,以备日后使用。
 
在这个模型中,有高中低三个优先级信道如下:高优先级--1,中优先级--3,低优先级--2
规则:
1.正常情况下各个优先级使用各自信道(线程)
2.高级信道满载、中、低级信道空置,则高级请求可使用低级信道
 
构思:
UI线程将期望的网络请求url和参数通过一个封装好的Runnable提交给Service处理(当然也可以交给一个Thread处理,本例使用Service),Service接收到请求,判断优先级,加入到相应线程池中排队。线程池启动线程发起网络请求,最后通过监听器将结果返回给Service,Service发送广播通知UI线程,UI线程更新相关界面,结束。
 
废话说完,上例子:
首先是封装好的Runnable
 
 
    
  1. public class HttpConnRunnable implements Runnable, Parcelable { 
  2.  
  3.     public static final int HIGH_LEVEL = 0
  4.     public static final int NORMAL_LEVEL = 1
  5.     public static final int LOW_LEVEL = 2
  6.      
  7.     private int mPriority = NORMAL_LEVEL;//优先级,默认为普通 
  8.     private String mUrl = ""
  9.      
  10.     private HttpConnListener mListener;//监听器 
  11.      
  12.      
  13.     public HttpConnRunnable() { 
  14.         super(); 
  15.     } 
  16.      
  17.     public HttpConnRunnable(int priority) { 
  18.         super(); 
  19.         mPriority = priority; 
  20.     }    
  21.  
  22.     @Override 
  23.     public void run() { 
  24.         Log.i(Thread.currentThread().getName(), "----Start to connect:" + mUrl + ", priority:" + mPriority + "-----"); 
  25.         try { 
  26.             Thread.sleep(10000); 
  27.             //TODO:进行网络请求相关操作,并通过listener返回结果 
  28.             mListener.onSucceed("Connected to " + mUrl + " succeed!"); 
  29.         } 
  30.         catch (InterruptedException e) { 
  31.             e.printStackTrace(); 
  32.         } 
  33.         Log.i(Thread.currentThread().getName(), "----Finish to connect:" + mUrl + ", priority:" + mPriority + "-----"); 
  34.     } 
  35.  
  36.     public int getPriority() { 
  37.         return mPriority; 
  38.     } 
  39.      
  40.     public void setPriority(int priority) { 
  41.         mPriority = priority; 
  42.     } 
  43.      
  44.     public String getURL() { 
  45.         return mUrl; 
  46.     } 
  47.      
  48.     public void setURL(String url) { 
  49.         mUrl = url; 
  50.     } 
  51.      
  52.     public void setHttpConnListener(HttpConnListener listener) { 
  53.         mListener = listener; 
  54.     } 
  55.      
  56.     //序列化,为了传递给Service,如果是使用Thread处理本例,则无需序列化 
  57.     public static final Parcelable.Creator CREATOR = new Creator() { 
  58.         @Override 
  59.         public HttpConnRunnable createFromParcel(Parcel source) { 
  60.             HttpConnRunnable data = null
  61.             Bundle bundle = source.readBundle(); 
  62.             if(bundle != null) { 
  63.                 data = new HttpConnRunnable(bundle.getInt("PRIORITY")); 
  64.                 data.mUrl = bundle.getString("URL"); 
  65.             } 
  66.             return data; 
  67.         } 
  68.  
  69.         @Override 
  70.         public HttpConnRunnable[] newArray(int size) { 
  71.             return new HttpConnRunnable[size]; 
  72.         } 
  73.     }; 
  74.      
  75.     @Override 
  76.     public int describeContents() { 
  77.         return 0
  78.     } 
  79.  
  80.     @Override 
  81.     public void writeToParcel(Parcel dest, int flags) { 
  82.         Bundle bundle = new Bundle(); 
  83.         bundle.putInt("PRIORITY", mPriority); 
  84.         bundle.putString("URL", mUrl); 
  85.         dest.writeBundle(bundle); 
  86.     } 
  87.  
 
Service的处理:
 
 
    
  1. public class HttpConnService extends Service implements HttpConnListener { 
  2.     public static final String HTTP_POOL_PARAM_KEYWORD = "HttpPoolParam";           //网络参数传递的关键字 
  3.      
  4.     private final int HIGH_POOL_SIZE = 1
  5.     private final int NORMAL_POOL_SIZE = 3
  6.     private final int LOW_POOL_SIZE = 2
  7.      
  8.     // 可重用固定线程数的线程池 
  9.     private ThreadPoolExecutor mHighPool; 
  10.     private ThreadPoolExecutor mNormalPool; 
  11.     private ThreadPoolExecutor mLowPool; 
  12.      
  13.     @Override 
  14.     public void onCreate() { 
  15.         //初始化所有 
  16.         mHighPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(HIGH_POOL_SIZE); 
  17.         mNormalPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(NORMAL_POOL_SIZE); 
  18.         mLowPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(LOW_POOL_SIZE); 
  19.  
  20.         super.onCreate(); 
  21.     } 
  22.  
  23.     @Override 
  24.     public int onStartCommand(Intent intent, int flags, int startId) { 
  25.         //接受到来自UI线程的请求 
  26.         //取出Runnable,并加入到相应队列 
  27.         Bundle bundle = intent.getExtras(); 
  28.         HttpConnRunnable httpConnRunnable = bundle.getParcelable(HTTP_POOL_PARAM_KEYWORD); 
  29.         if (httpConnRunnable != null) { 
  30.             httpConnRunnable.setHttpConnListener(HttpConnService.this); 
  31.             int level = httpConnRunnable.getPriority(); 
  32.             switch (level) { 
  33.                 case HttpConnRunnable.HIGH_LEVEL: 
  34.                     //如果高级池满而低级池未满交由低级池处理 
  35.                     //如果高级池满而普通池未满交由普通池处理 
  36.                     //如果高级池未满则交给高级池处理,否则,交由高级池排队等候 
  37.                     if (mHighPool.getActiveCount() == HIGH_POOL_SIZE && mLowPool.getActiveCount() < LOW_POOL_SIZE) { 
  38.                         mLowPool.execute(httpConnRunnable); 
  39.                     } 
  40.                     else if (mHighPool.getActiveCount() == HIGH_POOL_SIZE && mNormalPool.getActiveCount() < NORMAL_POOL_SIZE) { 
  41.                         mNormalPool.execute(httpConnRunnable); 
  42.                     } 
  43.                     else { 
  44.                         mHighPool.execute(httpConnRunnable); 
  45.                     } 
  46.                     break
  47.          
  48.                 case HttpConnRunnable.NORMAL_LEVEL: 
  49.                     //如果普通池满而低级池未满交由低级池处理 
  50.                     //如果普通池未满则交给普通池处理,否则,交由普通池排队等候 
  51.                     if (mNormalPool.getActiveCount() == NORMAL_POOL_SIZE && mLowPool.getActiveCount() < LOW_POOL_SIZE) { 
  52.                         mLowPool.execute(httpConnRunnable); 
  53.                     } 
  54.                     else { 
  55.                         mNormalPool.execute(httpConnRunnable); 
  56.                     } 
  57.                     break
  58.          
  59.                 case HttpConnRunnable.LOW_LEVEL: 
  60.                     mLowPool.execute(httpConnRunnable); 
  61.                     break
  62.             } 
  63.         } 
  64.         return super.onStartCommand(intent, flags, startId); 
  65.     } 
  66.  
  67.     @Override 
  68.     public void onDestroy() { 
  69.         mHighPool.shutdownNow(); 
  70.         mNormalPool.shutdownNow(); 
  71.         mLowPool.shutdownNow(); 
  72.  
  73.          
  74.         mNormalPool = null
  75.         mLowPool = null
  76.         super.onDestroy(); 
  77.     } 
  78.  
  79.     @Override 
  80.     public IBinder onBind(Intent intent) { 
  81.         return null
  82.     } 
  83.  
  84.     @Override 
  85.     public void onSucceed(String result) { 
  86.         Intent intent = new Intent(); 
  87.         intent.setAction("com.ezstudio.connpool.HttpConnReceiver"); 
  88.  
  89.         // 要发送的内容 
  90.         intent.putExtra("RESULT", result); 
  91.  
  92.         // 发送 一个无序广播 
  93.         sendBroadcast(intent); 
  94.     } 
  95.  
  96.     @Override 
  97.     public void onFailed() { 
  98.         // TODO Auto-generated method stub 
  99.     } 
 
 
Receiver的处理比较简单:

 
    
  1. public class HttpConnReceiver extends BroadcastReceiver { 
  2.     private HttpConnListener mListener; 
  3.      
  4.     public void setHttpConnListener (HttpConnListener listener) { 
  5.         mListener = listener; 
  6.     } 
  7.      
  8.     @Override 
  9.     public void onReceive(Context context, Intent intent) { 
  10.         String action = intent.getAction(); 
  11.         if (action.equals("com.ezstudio.connpool.HttpConnReceiver")) { 
  12.             String result = intent.getStringExtra("RESULT"); 
  13.             mListener.onSucceed(result); 
  14.         } 
  15.     } 
  16.  
 
 
ok,流程走完了,写个测试界面
 
 
看结果非常满意:
05-03 16:42:20.225: I/HighPollingThread(2318): ----HighQuqeue is empty-----
05-03 16:42:20.225: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:20.233: I/LowPollingThread(2318): ----LowQuqeue is empty-----
05-03 16:42:20.233: I/HighPollingThread(2318): ----HighQuqeue is empty-----
05-03 16:42:20.233: I/pool-1-thread-1(2318): ----Start to connect:www.0.com, priority:0-----
05-03 16:42:22.374: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:22.374: I/pool-2-thread-1(2318): ----Start to connect:www.1.com, priority:1-----
05-03 16:42:22.780: I/LowPollingThread(2318): ----LowQuqeue is empty-----
05-03 16:42:22.780: I/pool-3-thread-1(2318): ----Start to connect:www.2.com, priority:2-----
05-03 16:42:23.428: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:23.428: I/pool-2-thread-2(2318): ----Start to connect:www.3.com, priority:1-----
05-03 16:42:23.835: I/HighPollingThread(2318): ----HighQuqeue is empty-----
05-03 16:42:23.835: I/pool-3-thread-2(2318): ----Start to connect:www.4.com, priority:0-----
05-03 16:42:24.171: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:24.171: I/pool-2-thread-3(2318): ----Start to connect:www.5.com, priority:1-----
05-03 16:42:24.507: I/LowPollingThread(2318): ----LowQuqeue is empty-----
05-03 16:42:24.764: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:25.030: I/HighPollingThread(2318): ----HighQuqeue is empty-----
05-03 16:42:25.335: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:25.647: I/LowPollingThread(2318): ----LowQuqeue is empty-----
05-03 16:42:25.936: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:26.163: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:26.389: I/NormalPollingThread(2318): ----NormalQuqeue is empty-----
05-03 16:42:26.694: I/LowPollingThread(2318): ----LowQuqeue is empty----- 
 
总结:如果不算Service一共最多使用了3个线程池,6个线程,或许可以考虑将三个池合并为一个。但却也大量减少了单独发起请求时的线程创建和销毁的消耗。