天天看點

Android網絡開發 請求隊列Android網絡開發 請求隊列

Android網絡開發 請求隊列

文章出處:Android那些事兒的部落格

因為之前參與的網絡開發項目都遇到一些相同的問題:

1.大量的并發請求造成堵塞,特别是遇上讓人無語的3G網絡,無限loading。。。

2.一般來說一個網絡請求都會用使用到一個異步線程,大量的線程建立、運作、銷毀又造成了系統資源的浪費

3.請求結束得到結果後,如果需要更新UI,一個不小心忘了傳回UI線程,各種崩潰。。。

前些日子跟同僚商量能不能做個請求隊列去進行控制,于是趁着熱度沒消退說幹就幹,建了個模型,以備日後使用。

在這個模型中,有高中低三個優先級信道如下:高優先級–1,中優先級–3,低優先級–2

規則:

1.正常情況下各個優先級使用各自信道(線程)

2.進階信道滿載、中、低級信道空置,則進階請求可使用低級信道

構思:

UI線程将期望的網絡請求url和參數通過一個封裝好的Runnable送出給Service處理(當然也可以交給一個Thread處理,本例使用Service),Service接收到請求,判斷優先級,加入到相應線程池中排隊。線程池啟動線程發起網絡請求,最後通過監聽器将結果傳回給Service,Service發送廣播通知UI線程,UI線程更新相關界面,結束。

廢話說完,上例子:

首先是封裝好的Runnable

public class HttpConnRunnable implements Runnable, Parcelable {

public static final int HIGH_LEVEL = 0; 
public static final int NORMAL_LEVEL = 1; 
public static final int LOW_LEVEL = 2; 

private int mPriority = NORMAL_LEVEL;//優先級,預設為普通 
private String mUrl = ""; 

private HttpConnListener mListener;//監聽器 


public HttpConnRunnable() { 
    super(); 
} 

public HttpConnRunnable(int priority) { 
    super(); 
    mPriority = priority; 
}    

@Override 
public void run() { 
    Log.i(Thread.currentThread().getName(), "----Start to connect:" + mUrl + ", priority:" + mPriority + "-----"); 
    try { 
        Thread.sleep(10000); 
        //TODO:進行網絡請求相關操作,并通過listener傳回結果 
        mListener.onSucceed("Connected to " + mUrl + " succeed!"); 
    } 
    catch (InterruptedException e) { 
        e.printStackTrace(); 
    } 
    Log.i(Thread.currentThread().getName(), "----Finish to connect:" + mUrl + ", priority:" + mPriority + "-----"); 
} 

public int getPriority() { 
    return mPriority; 
} 

public void setPriority(int priority) { 
    mPriority = priority; 
} 

public String getURL() { 
    return mUrl; 
} 

public void setURL(String url) { 
    mUrl = url; 
} 

public void setHttpConnListener(HttpConnListener listener) { 
    mListener = listener; 
} 

//序列化,為了傳遞給Service,如果是使用Thread處理本例,則無需序列化 
public static final Parcelable.Creator<HttpConnRunnable> CREATOR = new Creator<HttpConnRunnable>() { 
    @Override 
    public HttpConnRunnable createFromParcel(Parcel source) { 
        HttpConnRunnable data = null; 
        Bundle bundle = source.readBundle(); 
        if(bundle != null) { 
            data = new HttpConnRunnable(bundle.getInt("PRIORITY")); 
            data.mUrl = bundle.getString("URL"); 
        } 
        return data; 
    } 

    @Override 
    public HttpConnRunnable[] newArray(int size) { 
        return new HttpConnRunnable[size]; 
    } 
}; 

@Override 
public int describeContents() { 
    return 0; 
} 

@Override 
public void writeToParcel(Parcel dest, int flags) { 
    Bundle bundle = new Bundle(); 
    bundle.putInt("PRIORITY", mPriority); 
    bundle.putString("URL", mUrl); 
    dest.writeBundle(bundle); 
} 
           

}

Service的處理:

public class HttpConnService extends Service implements HttpConnListener {

public static final String HTTP_POOL_PARAM_KEYWORD = “HttpPoolParam”; //網絡參數傳遞的關鍵字

private final int HIGH_POOL_SIZE = 1; 
private final int NORMAL_POOL_SIZE = 3; 
private final int LOW_POOL_SIZE = 2; 

// 可重用固定線程數的線程池 
private ThreadPoolExecutor mHighPool; 
private ThreadPoolExecutor mNormalPool; 
private ThreadPoolExecutor mLowPool; 

@Override 
public void onCreate() { 
    //初始化所有 
    mHighPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(HIGH_POOL_SIZE); 
    mNormalPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(NORMAL_POOL_SIZE); 
    mLowPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(LOW_POOL_SIZE); 

    super.onCreate(); 
} 

@Override 
public int onStartCommand(Intent intent, int flags, int startId) { 
    //接受到來自UI線程的請求 
    //取出Runnable,并加入到相應隊列 
    Bundle bundle = intent.getExtras(); 
    HttpConnRunnable httpConnRunnable = bundle.getParcelable(HTTP_POOL_PARAM_KEYWORD); 
    if (httpConnRunnable != null) { 
        httpConnRunnable.setHttpConnListener(HttpConnService.this); 
        int level = httpConnRunnable.getPriority(); 
        switch (level) { 
            case HttpConnRunnable.HIGH_LEVEL: 
                //如果進階池滿而低級池未滿交由低級池處理 
                //如果進階池滿而普通池未滿交由普通池處理 
                //如果進階池未滿則交給進階池處理,否則,交由進階池排隊等候 
                if (mHighPool.getActiveCount() == HIGH_POOL_SIZE && mLowPool.getActiveCount() < LOW_POOL_SIZE) { 
                    mLowPool.execute(httpConnRunnable); 
                } 
                else if (mHighPool.getActiveCount() == HIGH_POOL_SIZE && mNormalPool.getActiveCount() < NORMAL_POOL_SIZE) { 
                    mNormalPool.execute(httpConnRunnable); 
                } 
                else { 
                    mHighPool.execute(httpConnRunnable); 
                } 
                break; 

            case HttpConnRunnable.NORMAL_LEVEL: 
                //如果普通池滿而低級池未滿交由低級池處理 
                //如果普通池未滿則交給普通池處理,否則,交由普通池排隊等候 
                if (mNormalPool.getActiveCount() == NORMAL_POOL_SIZE && mLowPool.getActiveCount() < LOW_POOL_SIZE) { 
                    mLowPool.execute(httpConnRunnable); 
                } 
                else { 
                    mNormalPool.execute(httpConnRunnable); 
                } 
                break; 

            case HttpConnRunnable.LOW_LEVEL: 
                mLowPool.execute(httpConnRunnable); 
                break; 
        } 
    } 
    return super.onStartCommand(intent, flags, startId); 
} 

@Override 
public void onDestroy() { 
    mHighPool.shutdownNow(); 
    mNormalPool.shutdownNow(); 
    mLowPool.shutdownNow(); 


    mNormalPool = null; 
    mLowPool = null; 
    super.onDestroy(); 
} 

@Override 
public IBinder onBind(Intent intent) { 
    return null; 
} 

@Override 
public void onSucceed(String result) { 
    Intent intent = new Intent(); 
    intent.setAction("com.ezstudio.connpool.HttpConnReceiver"); 

    // 要發送的内容 
    intent.putExtra("RESULT", result); 

    // 發送 一個無序廣播 
    sendBroadcast(intent); 
} 

@Override 
public void onFailed() { 
    // TODO Auto-generated method stub 
} 
           

}

Receiver的處理比較簡單:

public class HttpConnReceiver extends BroadcastReceiver {

private HttpConnListener mListener;

public void setHttpConnListener (HttpConnListener listener) { 
    mListener = listener; 
} 

@Override 
public void onReceive(Context context, Intent intent) { 
    String action = intent.getAction(); 
    if (action.equals("com.ezstudio.connpool.HttpConnReceiver")) { 
        String result = intent.getStringExtra("RESULT"); 
        mListener.onSucceed(result); 
    } 
} 
           

}

可能很多同學都看過這麼文章,不過由于沒有源碼可能還是雲裡霧裡,由于我也是個小菜鳥的關系就本着學習的心态對照原部落格,可新浪微網誌發送微網誌的效果自己實作了隊列請求的效果,便記錄下來,以便日後拿來用。也将源碼分享給大家

源碼

此源碼是as工程,不會在eclipse導入as工程的同學可以new eclipse工程,拷一下代碼和布局檔案。。。

菜鳥轉載,不喜勿噴