Tải bản đầy đủ - 0 (trang)
Hack 93. Code Models That Don't Block

Hack 93. Code Models That Don't Block

Tải bản đầy đủ - 0trang

ThestrategyinthishackistomaketheJTextArea'smodel

responsibleforitsownthreadedloading,sogetridofthe

loadURL()method.Thatcodewillmovetothemodels,which

subclassjavax.swing.text.PlainDocument.First,changetheactions

tousethesedocuments:























classBlockingLoadActionextendsAbstractAction{



publicvoidactionPerformed(ActionEvente){





BlockingURLDocumentbud=







newBlockingURLDocument(urlFie





progressBar.setEnabled(true);





progressBar.setValue(0);





contentArea.setDocument(bud);





progressBar.setValue(100);



}

}





















classNonBlockingLoadActionextendsAbstractAction{



publicvoidactionPerformed(ActionEvente){





NonBlockingURLDocumentnbud=







newNonBlockingURLDocument(url





contentArea.setDocument(nbud);





//makeProgressBarUpdaterFor(nbud);



}

}



Theactionsareprettymuchthesame,exceptforthefactthat

theblockingversionsetstheJProgressBarbeforeandaftersetting

theJTextArea'sdocument.Whydoesn'tthenon-blockingaction

touchtheprogressbar?We'llgetbacktothat.

Example12-9istheblockingdocument,sonamedbecauseit

loadsallthedatafromtheURLinitsconstructor,whichwill

causetheAction'sconstructortoblockuntileverythingis

loaded.



Example12-9.Adocumentthatwillblockwhile

loadingaURL



















classBlockingURLDocumentextendsPlainDocument{



publicBlockingURLDocument(StringurlString){





super();





try{







URLurl=newURL(urlField.get







BufferedReaderin=







newBufferedReader(







newInputStreamReader(url.open



























































































}



StringBuffersbuf=newStringBuffer(





char[]buffy=newchar[16*1





intbytesRead=0;





while((bytesRead=in.read(bu





sbuf.append(buffy,0,bytesRea





//ifyournetconnectionisto





//addthefollowinghere





//Thread.sleep(1000);





}





remove(0,getLength());





insertString(0,sbuf.toString(







}catch(Exceptione){





CharArrayWriterwriter=newCh





e.printStackTrace(newPrintWri





try{





remove(0,getLength());





insertString(0,writer.toStrin





}catch(Exceptione2){e2.prin



}

}



Theimplementationhereisprettysimple:readbytesandstuff

themintoaStringBufferuntilthestreamisexhausted,thenstuff

themintotheDocumentwithinsertString().Thismakesforclean

codebecausetheDocumentloadsitsowndata.Thedownsideis

thatthecaller,whichisontheevent-dispatchthread,blocks

untilallthedataisreadandinsertedintotheDocument.And,of

course,ablockedevent-dispatchthreadmeansnothinginyour

GUIgetsrepainted,mouseeventsaren'tprocessed…basically,

nobody'shappy.

AmoreenlightenedapproachrequiresaDocumenttoloaditsown

dataandyetnotblockthecaller.Inotherwords,itwillneedto

returnalmostimmediately,threadwhateverworkdoesn'tneed

tobeoneventdispatch,anduseaworkerthreadtodothe

Swingworkinathread-safemanner.Example12-10iswhata

non-blockingdocumentlookslike.



Example12-10.Documentthatwillnotblockon

URLloading





















classNonBlockingURLDocumentextendsPlainDocument



implementsRunnable{



protectedintlength=-1;



protectedinttotalBytesRead=0;



protectedStringurlString;



protectedThreadreadThread;



publicNonBlockingURLDocument(StringurlString





super();





this.urlString=urlString;







































}

publicvoidrun(){



//startthreadhere

readThread=newThread(this);

readThread.start();



















































try{















remove(0,getLength());

URLurl=newURL(urlField.get

URLConnectionconn=url.openCo

length=conn.getContentLength(

System.out.println("lengthis

BufferedReaderin=



















































































newBufferedReader(

newInputStreamReader(conn.get

char[]buffy=newchar[16*1

totalBytesRead=0;

intbytesRead=-1;

while((bytesRead=in.read(bu

finalStringstr=newString(

finalintfinalTBR=totalBytes

Runnableworker=newRunnable



























































































































































publicvoidrun(){

try{

insertString(finalTBR,str,nu

}catch(BadLocationExceptionb

ble.printStackTrace();}

}

};

SwingUtilities.invokeLater(wor

totalBytesRead+=bytesRead;

System.out.println("read"+t



"of"+length+



",progress=="+getP

//ifyournetconnectionisto

//makebuffysmallerabove(ma

//addthefollowinghere:

//Thread.sleep(500);

}

}catch(Exceptione){























































































}



CharArrayWriterwriter=newCh

e.printStackTrace(newPrintWri

try{

remove(0,getLength());

insertString(0,writer.toStrin

}catch(Exceptione2){e2.prin

}finally{

readThread=null;

}



















publicbooleanisAlive(){





return(readThread!=null)&&(readThr



}



publicfloatgetProgress(){





return(float)totalBytesRead/length;



}

}



Inthiscase,alltheloadingisdoneinarun()method,sothe

constructorsimplycreatesaTHReadcalledreadThreadtowrapthe

run(),startsthethread,andthenreturns,freeinguptheeventdispatchthreadalmostimmediately.

So,nowit'suptoreadThreadtoreadthestreamandloadits

contentsintotheDocument.Asintheblockingversion(Example

12-9),itreadsbytesintoabuffer,butinsteadofbuildingabig

StringBufferwithwhichtodoamass-insert,itusesDocument's

insertString()methodtoputeachbufferfulintothedocument.

SinceinsertString()willcausethemodeltofireoffevents,this

willprovideforaconstantupdatingoftheview;that'swhythis

versioncallsinsertString()eachtimethroughtheloopinstead

ofonceatthebottom,astheblockingversiondid.However,

insertString()isaSwingmethod,meaningit'sthread-unsafe,so

youcan'tcallitdirectlyfromthereadThread.Instead,yousetup



aworkeranduseSwingUtilities.invokeLater()toputthe

insertString()call,andonlythatcall,backontotheeventdispatchthread.



12.7.2.RunningtheCode

IfyouhaveafastInternetconnection,itispossiblethatyou'll

loadthedatasofastthatyoudon'tmindtheblockingoryou

can'tseetheincrementalupdatingofthenon-blockingversion.

Tomakethismoredramatic,increasethetimethateachmodel

sleep()s,andreducethesizeofthebufferusedtoreadbytes

fromtheInputStream.Toshowtheincremental,non-blocking

updateinFigure12-8,Isetthesleep()timeto500milliseconds

andthebuffersizetoamere80bytes.

NowyouhaveaURL-loadingdocumentandamodelfor

JTextComponentsthatwillhandleitsownthreadedloadingandcan

thusbedroppedintoanyJTextComponentwithoutneedingtoadd

anyothercodetomanageitsthreadedoperation.



Figure12-8.Progressiveself-updateofa

JTextArea'sdocument



12.7.3.ExposingtheThreading

Butwhatifacallerwantstomanagethreading?Youmightwant

toatleastexposethefactthatathreadisstillupdatingand

havetherestofyourcodebeawareofthatstate.

NoticethattheNonBlockingURLDocumentexposesapairofextra

methodsisAlive()andgetProgress()thataren'trequiredby

Documentoranythingelseinjavax.swing.text.Theseareextra

methodsItossedintothedemotosupporttheprogressbar

mentionedearlierinthishack.

Thestrategyhereistohaveanoutsidecallerperiodicallycheck

inontheNonBlockingURLDocument,getitsprogress,andupdatea

progressbar.NoticeIsaidperiodically:thisisajobfor

javax.swing.Timer!UsingtheSwingtimer,yougetregular

callbacks,andyourcodeisguaranteedtobeontheeventdispatchthread.



So,uncommentthemakeProgressBarUpdaterFor()callshownearlier,

andaddthisimplementationofthatmethod,alongwitha

helpermethod:

































privatevoidmakeProgressBarUpdaterFor(NonBlockingURLD



finalNonBlockingURLDocumentupdatingDoc=nbud



updateProgressBar(0);



ActionListenercallback=newActionListener()





publicvoidactionPerformed(ActionEven







progressBar.setEnabled(true);







intprogress=(int)(updatingD







updateProgressBar(progress);







if(!updatingDoc.isAlive())







progressBarUpdater.stop();





}



};



progressBarUpdater=newjavax.swing.Timer(200



progressBarUpdater.start();

}





















privatevoidupdateProgressBar(intprogress){



//System.out.println("updateprogressbar:"



if(progress>0){





progressBar.setValue(progress);



}



else





progressBar.setEnabled(false);



}



ThemakeProgressBarUpdaterFor()methodcreatesaTimerthatcalls

backtothegivenActionListenereverytwoseconds.The

ActionListenergetstheprogressfromtheNonBlockingURLDocument

andcallsupdateProgressBar(),whichupdatestheprogressbarif

thevalueispositive,anddisablesthebarifit'snegative.Also,if



thedocumentisnolongerloading,thecallbackstopstheTimer.

Youmightbethinking"whywouldIhavetocheckfornegative

progress?"Asitturnsout,it'sanunfortunateimplementation

detail:alotofwebserverssend-1,meaningunknown,asthe

HTTPcontent-lengthheader.Ifyoulookaround,you'llfindsites

thatdosendavalidcontentlength.Figure12-9showsan

incrementalloadofhttp://www.oreilly.com/.

ThisapproachusedURLsandDocuments,buttheapproachis

widelyapplicabletootherkindsofmodelslistmodels,tables

models,etc.andwouldbewellsuitedtootherkindsofslow-toloaddata.Itshouldbestraightforwardtoseehowyoucould

takethisapproachtocreate,say,aTableModelthat'spasseda

java.sql.Connectionandpopulatesitsrowsprogressivelywith

databasequeries,withoutblockingtheAWTandwithoutmaking

theuserwaittoseethefirstfewrowsofdata.



Figure12-9.Incrementalself-loadingwitha

progressbar



Hack94.FireEventsandStayBugFree



Mostdevelopersthinkthatwritinganevent-firing

methodistrivial.Mostdevelopersarewrong.

AsyoudevelopyourownSwingcomponents,it'slikelythat

you'lleventuallyneedtohavethemfireoffevents;thiscomes

upassoonasyouhaveamodelthatneedstoupdateaview.If

you'vestronglytypedeverythingbywritingnewclassesforthe

model,view,event,andlistener,thenyou'llhavetowriteyour

ownfiremethod.

Mostdevelopersassumethistobetrivial.Forexample,to

managealistofFooListeners,they'lltypicallymaintainaVector

orArrayListandfireofftheeventwithablocklikethis:







Iteratorit=listeners.iterator();

while(it.hasNext())











((FooListener).it.next()).handleEvent(fooEvent



Andthereyouhaveit.It'ssimple.It'sclean.It'selegant.It's

wrong.



12.8.1.TheProblem

Toillustratetheproblemanditsvarioussolutions,considerthe

listenerclassinExample12-11.



Example12-11.Asimpleeventlistener



















importjava.util.*;

publicclassTestEventListenerextendsObject



implementsEventListener{



Stringid;



publicTestEventListener(Stringid){





this.id=id;



}



publicvoidhandleEvent(EventObjecto){



























}











}



System.out.println(id+"called");

if(id.equals("C")){



((TestEventSource)o.getSource(

}



ThislistenerhangsontoaStringandprintsthatstringto

standardoutwhenhandleEvent()iscalled.Also,ifthestringisa

specificvalueCinthiscaseitremovesitselffromtheevent

source.Ifyoucanseewhythat'sgoingtobeabigdeal,

congratulations.Ifnot,readon.

Next,defineanabstractclasstoexercisevariousmeansof

firingtheevent.ThisisshowninExample12-12.



Example12-12.Abstractclassfortestingeventfiringtechniques









publicabstractclassTestEventSource{

publicabstractvoidaddListener(TestEventListenerl);

publicabstractvoidremoveListener(TestEventListener



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Hack 93. Code Models That Don't Block

Tải bản đầy đủ ngay(0 tr)

×