王晉東
摘 要:隨著移動(dòng)設(shè)備的普及,越來(lái)越多的互聯(lián)網(wǎng)應(yīng)用都對(duì)加載圖片的速度有很高的要求。在簡(jiǎn)要對(duì)比傳統(tǒng)的同步和多線程兩種加載圖片方式后,給出相應(yīng)地實(shí)現(xiàn)算法和評(píng)價(jià)。
關(guān)鍵詞:移動(dòng) 圖片 多線程 算法
中圖分類號(hào):TP316 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1007-3973(2013)009-102-03
1 前言
iOS、Android、Windows Phone及Symbian等主流的移動(dòng)操作系統(tǒng)現(xiàn)在均已支持加載jpg、png等常見格式的圖片,隨著應(yīng)用對(duì)網(wǎng)絡(luò)依賴程度的加強(qiáng),這些操作系統(tǒng)也已支持直接從網(wǎng)絡(luò)直接讀取圖片。而讀取只要給出該圖片的http地址(URL)即可。
對(duì)用戶界面響應(yīng)速度要求較高的應(yīng)用而言,圖片的加載速度不宜過(guò)慢。而隨著如今終端屏幕分辨率的增加,用戶對(duì)手機(jī)的圖片質(zhì)量也有了很高的要求。就單一服務(wù)器模式而言,以前流行的分布式算法顯然達(dá)不到要求。
2 同步方式
同步方式十分簡(jiǎn)單。由客戶端向服務(wù)器提交請(qǐng)求,服務(wù)器對(duì)此做出應(yīng)答——應(yīng)答的結(jié)果就是從遠(yuǎn)程向客戶端返回符合要求的圖片代碼,圖片接收完畢后再由客戶端作解碼、還原。同步方式的算法如下。
客戶端:
BEGIN
Activate webservice
Send request to server
Wait for response
Download complete
END
服務(wù)器:
BEGIN
Receive reqests
Create response queue
While queue is not empty
dequeue
handle every request
END
這種方式的優(yōu)點(diǎn)是簡(jiǎn)單,缺點(diǎn)也很明顯:客戶端增多時(shí),服務(wù)器壓力會(huì)陡然增大,而此種方式要求圖片必須是連續(xù)加載,即客戶端需要等待自己的加載要求出服務(wù)器隊(duì)列時(shí)才會(huì)收到應(yīng)答。圖片一般比較大,所以它們都是在基本框架加載后才逐漸加載上的,整個(gè)加載的過(guò)程非常不雅觀,或者是從模糊逐漸變清晰,或者是從上往下拓展開(當(dāng)然你也可以認(rèn)為這些都是不錯(cuò)的特效)。
3 多線程方式
3.1 統(tǒng)一需求
除了不能滿足快速加載網(wǎng)絡(luò)圖片的要求以外,同步方式還存在諸多缺點(diǎn)。多線程方式通過(guò)采用不同的機(jī)制,不僅保證了快速加載圖片的基本要求,更從流量上等方面具有較大的優(yōu)勢(shì)。
3.2 多線程與線程池
由于程序的代碼中存在著數(shù)據(jù)和控制依賴關(guān)系,單線程只能很有限地滿足當(dāng)今處理能力的要求。為了增加處理器的處理能力而一味地強(qiáng)化指令的執(zhí)行順序和細(xì)化分支,有時(shí)也不見得能事半功倍。因此,現(xiàn)代微處理器多采用硬件多線程技術(shù)來(lái)發(fā)掘線程之間的線程級(jí)并行潛力。移動(dòng)終端所采用的處理器多為ARM架構(gòu),很好地滿足了多線程的處理要求。
多線程技術(shù)主要解決處理器單元內(nèi)多個(gè)線程并行執(zhí)行的問(wèn)題,它可以顯著減少處理器單元的閑置時(shí)間,增加處理器單元的吞吐能力。但如果對(duì)多線程應(yīng)用不當(dāng),會(huì)增加對(duì)單個(gè)任務(wù)的處理時(shí)間??梢耘e一個(gè)簡(jiǎn)單的例子:
假設(shè)在一臺(tái)服務(wù)器完成一項(xiàng)任務(wù)的時(shí)間為T,它包括創(chuàng)建線程T1、執(zhí)行任務(wù)T2、線程同步T3以及線程銷毀的時(shí)間T4。顯然,在理想狀況下,T必然是這幾部分的時(shí)間之和。
可以看出T1,T4是多線程本身的帶來(lái)的開銷,我們渴望減少T1,T4,從而減少T的時(shí)間。但是如果在程序中頻繁地創(chuàng)建或銷毀線程,會(huì)導(dǎo)致T1和T4在T中占有相當(dāng)大比例,從而使整個(gè)執(zhí)行的時(shí)間變長(zhǎng)。這顯然并沒有很好地利用線程的并發(fā)性。
線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T4時(shí)間的技術(shù),從而提高服務(wù)器程序性能的。它把T1,T4分別安排在服務(wù)器程序的啟動(dòng)和結(jié)束的時(shí)間段或者一些空閑的時(shí)間段,這樣在服務(wù)器程序處理客戶請(qǐng)求時(shí),不會(huì)有T1,T4的開銷了。另外,線程池不僅調(diào)整T1,T4產(chǎn)生的時(shí)間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目。
3.3 常見系統(tǒng)的線程類
Java中線程類Thread的繼承關(guān)系為java.lang.Thread,在C#中是System.Threading.Thread,而iOS 支持NSThread等多個(gè)層次的多線程編程,層次越高的抽象程度越高,使用起來(lái)也越方便,也是蘋果最推薦使用的方法。
這些系統(tǒng)的線程類使用起來(lái)都非常方便,它將作為我們快速加載圖片的基礎(chǔ)。
3.4 多線程模式的算法
為了不失一般性,我們約定,采用一種類似C#(當(dāng)然也可以是Java)的語(yǔ)言來(lái)實(shí)現(xiàn)算法。
3.4.1 線程池的構(gòu)造
class ThreadPool
{
Assume:isClosed,Queue,poolId and wait;
//是否關(guān)閉,隊(duì)列,id,是否等待狀態(tài)
//構(gòu)造函數(shù),參數(shù)為線程池大小
ThreadPool(size)
Initialize Queue as a queue; //創(chuàng)建新隊(duì)列
For i:= 0 to size
Create and start a new thread; //啟動(dòng)一個(gè)新線程
//是否等待狀態(tài)
bool isWait return wait
//設(shè)置為等待
void setWait(_wait) wait := _wait;
// 向隊(duì)列里加入一個(gè)新的任務(wù),由工作線程去執(zhí)行任務(wù)
synchronized void execute(task)
//如果線程池已關(guān)閉,拋出異常,否則
if(task != null) then
While wait=true
Try
wait;
Catch
//Throw an exception
Add task to Queue;
Notify Queue;
synchronized Runnable getTask(threadId)
While Queue is not null
If isClosed then return null;
wait();
return First element in Queue // 返回隊(duì)列中第一個(gè)元素
synchronized void closePool()
If !isClosed then
waitFinish(); // 等待工作線程執(zhí)行完畢
isClosed := true;
Queue.clear();
interrupt();
//讓線程池處于等待狀態(tài)
synchronized void waitPool() this.wait := true;
//喚醒線程池
synchronized void notifyPool() this.wait := false;
// 等待工作線程把所有任務(wù)執(zhí)行完畢
void waitFinish
synchronized;
isClosed := true;
notifyAll;
threads[] := new Thread[activeCount()]; // activeCount() 返回該線程組中活動(dòng)線程的估計(jì)值
count := number of active threads
for I := 0 to count
if thread[i] is not interrupted then
set thread[i] to interrupted
}
3.4.2 工作線程類
class WorkThread
{
Assume:id;
WorkThread(_id)
Create a thread;
Id := _id;
void run()
While thread is not interrupted
Task := null;
Try
task = getTask(id);
catch
//Throw an exception
// 如果getTask()返回null或者線程執(zhí)行g(shù)etTask()時(shí)中斷,則結(jié)束此線程
If task is not null then
try
Run the task;
catch
Throw an exception;
}
3.4.3 加載圖片類
class LoadImage
{
Assume size;
void run()
Try
while true
//wait until images come into queue
if size = 0 then
wait for photoqueue;
else
create and initialize an instance;
Create a BitMapLoad instance;
threadPool.execute(bdd);
if Thread is interrupted then break
Catch
// Exit the Thread
}
3.4.4 BitMapLoad 類
// 本類用來(lái)支持多線程
class BitmapDownAndDisplay
{
Assume photoToLoad,Activity;
BitmapDownAndDisplay(photoToLoad, activity)
this.photoToLoad := photoToLoad;
this.activity := activity;
oid run()
create a bitmap;
synchronized (this)
bitmap := getBitmap(photoToLoad.url);
if bitmap is not null then
add bitmap to system cache;
if photoToLoad.imageView.getTag() is not null &&
photoToLoad.imageView.getTag() =photoToLoad.url then
set bitmap to UI thread to show
else
if wait=false
// 非阻塞線程則加會(huì)隊(duì)列末尾
Add photoLoad to photoQueue;
}
4多線程與同步方式的比較
我們采用相同的實(shí)驗(yàn)平臺(tái):Windows Phone 7.5系統(tǒng)的模擬器,運(yùn)行兩段代碼,一段為同步加載圖片,另一段為多線程方式,在系統(tǒng)內(nèi)部均沒有緩存的情況下進(jìn)行測(cè)試。網(wǎng)絡(luò)圖片來(lái)自http://pivotstudio.org,加載的效果如圖2。
很明顯,多線程方式的加載時(shí)間要大大優(yōu)于同步方式。
5 多線程方式的優(yōu)點(diǎn)總結(jié)
(1)在加載圖片時(shí)使UI不至于阻塞太久,縮短了加載時(shí)間;
(2)多個(gè)圖片控件同時(shí)加載,速度更快;
(3)實(shí)現(xiàn)算法具有通用性,主流平臺(tái)均受支持;
(4)運(yùn)用線程池以防止開啟過(guò)多的線程,從而增大CPU的壓力;
(5)可擴(kuò)展性:在此基礎(chǔ)上加入內(nèi)存管理的機(jī)制,可以提高終端內(nèi)存的使用率。
6 總結(jié)
在實(shí)際的應(yīng)用中,越來(lái)越多的應(yīng)用和游戲需要從網(wǎng)絡(luò)獲取很多的圖片,本文所討論的多線程加載圖片的機(jī)制就很好地滿足了上述要求。當(dāng)然,盡管算法是通用的,在具體的項(xiàng)目和開發(fā)環(huán)境中還需要根據(jù)語(yǔ)言和平臺(tái)的特點(diǎn)靈活編寫程序,方能發(fā)揮出算法最佳地性能。
參考文獻(xiàn):
[1] 黃天柱,涂時(shí)亮.iOS開發(fā)UITableView加載圖片的內(nèi)存管理[J].計(jì)算機(jī)系統(tǒng)應(yīng)用,2012(09).
[2] 沙博.基于Android手機(jī)平臺(tái)的應(yīng)用研究[D].吉林大學(xué),2012.
[3] (美)馬克,拉馬赫.著.iPhone 3開發(fā)基礎(chǔ)教程(第1版)[M].漆振,等.譯.北京:人民郵電出版社,2009.
[4] 伏英娜.Windows Phone 7應(yīng)用開發(fā)指南(第1版)[M].北京:電子工業(yè)出版社,2011.
[5] 佘志龍,陳昱勛,鄭名杰,等.Google Android SDK開發(fā)范例大全(第3版)[M].北京:人民郵電出版社,2011.