Programming Java 2 Micro Edition for Symbian OS 2004 (779882), страница 51
Текст из файла (страница 51)
Theanimation is achieved by scheduling a java.util.TimerTask thatupdates the current image in the form. The timer task is implemented asan anonymous inner class.When the form is created the startSync() method, starts the workerthread and schedules the timer task. The timer task has two purposes: toanimate the image on the form by cycling through a set of images loadedduring the form creation and to check if the worker thread has terminated.Once the thread has terminated, its status is checked to determine thesuccess or failure of the operation. The animation timer task is scheduledFigure 5.8 Synchronization form.270MIDP 2.0 CASE STUDIESto trigger after 12 second and then every 12 second after that.
This can beseen at the end of the method where the timing parameters appear afterthe inner class definition.// SynchronizeForm startSync() methodprivate void startSync() {timer = new Timer();// start sync threadSynchronizer = new SynchronizeThread(settings);workerThread = new Thread(Synchronizer);workerThread.start();//schedule a timer for animation and to check if sync completetimer.schedule(new TimerTask() {public void run() {// animatecurrentImage = (currentImage + 1) % syncImages.length;syncAnimation.setImage(syncImages[currentImage]);// is sync operation complete??if (!workerThread.isAlive()) {timer.cancel();// inform the user of the sync result, use the next// displayable param to go back to the main form.// so was there a problem?if (Synchronizer.getErrorMessage() != null) {// let the user know about the problemExpenseMIDlet.alert("Failed to Synchronize: "+ Synchronizer.getErrorMessage(),AlertType.ERROR,MainForm.getInstance());} else {// update the last sync date informationsettings.syncCompleted(Synchronizer.getSyncId());ExpenseMIDlet.alert("Synchronization complete!",AlertType.INFO,MainForm.getInstance());}}}}, 500, 500);}5.2.6.2 Synchronization Worker ThreadThe synchronization thread implements the java.lang.Runnableinterface; the implementation of the run() method has been includedbelow.
Synchronization has three steps: check for change of user orserver, synchronize information and remove unneeded expenses fromthe device.// SynchronizationThread run() methodpublic void run() {try {THE EXPENSE APPLICATION//////if271are we changing user (sync required set)?sync expenses before clearing followed by normal sync of user& expenses from sync id of 0(settings.isNeedToSync()) {// sync the expense informationSynchronizeExpense();// remove all expenses from phoneExpenseDAO.getInstance().removeAll();// reset sync id to ensure complete info sentsyncId = oldSyncId = 0;}// Synchronize the person informationSynchronizePerson();// sync the expense informationSynchronizeExpense();// remove all expenses that are no longer required.// i.e. if we are not the owner & state != SUBMITTED then remove!Vector expenseList =ExpenseDAO.getInstance().getExpenses(null, false, null);for (int i = expenseList.size() - 1; i >= 0; i--) {Expense expense = (Expense)expenseList.elementAt(i);if ((expense.getOwnerId() != settings.getUserId())&& (expense.getState() != Expense.STATE_SUBMITTED)) {// remove this expense as we no longer need it!!ExpenseDAO.getInstance().remove(expense);}}} catch (Exception e) {// set error codeerrorMessage = e.getMessage();}}If the current user or synchronization server address has changedthen a complete synchronization must occur.
Any updated expenses onthe device should be uploaded and the device’s record store purged inpreparation for new information that will be received from a completesynchronization. As complete and partial synchronization use the samecode, a synchronization ID is used to differentiate between the twooperations. During a complete synchronization the last synchronizationID is set to 0; in a partial synchronization a unique ID received from theserver during the last synchronization is used so that only the updatedinformation is exchanged. In the run() method the isNeedToSync callis used to determine whether a complete synchronization is required.The exchange of information takes place in the SynchronizePersonand SynchronizeExpense() method calls.
Each method packagesand submits the information to the server, each request sent starts withthe current user ID and the last synchronization ID. The server responseincludes a new globally unique synchronization ID for use during thenext synchronization. The code that handles the request and response isdiscussed later.The final operation in the synchronization is to remove all expensesthat are no longer needed on the device; these include other users’272MIDP 2.0 CASE STUDIESexpenses that have been approved or rejected by the current user actingin the budget holder role.The synchronization thread communicates with the server using theHTTP protocol. XML formatted requests that contain all the informationthat the device wishes to exchange are sent.
After processing the request,the server formats an XML response containing updates.XML is space inefficient; many additional bytes are required to encodeinformation. So why use XML in an environment where memory is at apremium and the network connections are slow? XML is convenient, itis easy to validate and there are many existing tools and APIs to simplifyits use, allowing the expense application prototype to be created in theminimum of time.The javax.microedition.io.HttpConnection class is usedfor the server communication.
Requests to the server use a utility method,sendMessageToServer(), that has a single parameter containing theXML request to be sent. An HttpConnection is opened to the serverURL and the XML request sent via the connection’s OutputStream.The response is read into a StringBuffer before being returnedto the caller. If there is an error then an exception is thrown that willeventually find its way back to the synchronization form to be presentedto the user.// SynchronizationThread sendMessageToServer() methodprivate String sendMessageToServer(String message)throws IOException, ServerCommException {StringBuffer sb = new StringBuffer();HttpConnection connection = null;InputStream is = null;// open connectionconnection = (HttpConnection)Connector.open(settings.getSyncServer());// send message to serverOutputStream os = connection.openOutputStream();os.write(message.getBytes());os.close();// make sure we got a good response code, i.e.
>= 200 && < 300if ((connection.getResponseCode() >= 200)&& (connection.getResponseCode() < 300)) {is = connection.openInputStream();byte[] buffer = new byte[512];int bytesRead = 0;while (-1 != (bytesRead = is.read(buffer, 0, 512))) {sb.append(new String(buffer, 0, bytesRead));}} else {// error of some kindthrow new ServerCommException("Server communications error: "+ connection.getResponseCode()+ ", "+ connection.getResponseMessage());}// close connection.THE EXPENSE APPLICATION273connection.close();return sb.toString();}5.2.6.3 Format of Synchronization MessageAn XML schema was created for the interaction between the client deviceand the server.
This allowed the XML to be validated as well as allowingthe server side parsing code to be generated using JAXB, the Java API forXML Binding. The schema is shown below.<xsd:schema xmlns:xsd="www.w3.org/2001/XMLSchema"><xsd:element name="etsync" type="ExpenseTrackerSync"/><xsd:complexType name="ExpenseTrackerSync"><xsd:sequence><xsd:element name="req" type="ExpenseTrackerRequest"minOccurs="0" /><xsd:element name="resp" type="ExpenseTrackerResponse"minOccurs="0" /></xsd:sequence></xsd:complexType><xsd:complexType name="ExpenseTrackerRequest"><xsd:sequence><xsd:element name="userupdate" type="UpdateUser" minOccurs="0"maxOccurs="1" /><xsd:element name="expense" type="PhoneExpense" minOccurs="0"maxOccurs="unbounded" /></xsd:sequence><xsd:attribute name="lastSyncId" type="xsd:integer" /><xsd:attribute name="userId" type="xsd:integer" /></xsd:complexType><xsd:complexType name="ExpenseTrackerResponse"><xsd:sequence><xsd:element name="mainuser" type="User" minOccurs="0"maxOccurs="1" /><xsd:element name="subord" type="User" minOccurs="0"maxOccurs="unbounded"/><xsd:element name="expense" type="PhoneExpense" minOccurs="0"maxOccurs="unbounded"/></xsd:sequence><xsd:attribute name="sid" type="xsd:integer" /></xsd:complexType><xsd:complexType name="UpdateUser"><xsd:attribute name="name" type="xsd:string" use="required"/></xsd:complexType><xsd:complexType name="PhoneExpense"><xsd:sequence><xsd:element name="owner" type="xsd:integer" minOccurs="1"maxOccurs="1" /><xsd:element name="state" type="xsd:integer" minOccurs="1"maxOccurs="1" /><xsd:element name="type" type="xsd:integer" minOccurs="1"maxOccurs="1" /><xsd:element name="receiptdate" type="xsd:date" minOccurs="1"maxOccurs="1" /><xsd:element name="amount" type="xsd:integer" minOccurs="1"274MIDP 2.0 CASE STUDIESmaxOccurs="1" /><xsd:element name="currency" type="xsd:string" minOccurs="1"maxOccurs="1" /><xsd:element name="vatpercentage" type="xsd:integer"minOccurs="1" maxOccurs="1"/><xsd:element name="project" type="xsd:integer" minOccurs="1"maxOccurs="1" /><xsd:element name="dept" type="xsd:integer" minOccurs="1"maxOccurs="1" /><xsd:element name="createdate" type="xsd:dateTime"minOccurs="1" maxOccurs="1" /><xsd:element name="lastsyncid" type="xsd:integer"minOccurs="1" maxOccurs="1"/><xsd:element name="ownernotes" type="xsd:string" minOccurs="1"maxOccurs="1" /><xsd:element name="bhnotes" type="xsd:string" minOccurs="1"maxOccurs="1" /></xsd:sequence></xsd:complexType><xsd:complexType name="User"><xsd:attribute name="id" type="xsd:int" use="required"/><xsd:attribute name="name" type="xsd:string" use="required"/></xsd:complexType></xsd:schema>On the device, XML requests are encoded by appending to a stringbuffer.
There is no need to use an XML library: it overcomplicates theprocess and increases memory overhead. The kXML library was used toparse the XML responses on the device. It is designed to run under MIDPand has a small memory footprint. The library can be downloaded fromhttp://xmlpull.org.5.2.6.4 Parsing XML using kXMLThe kXML parser is simple to use, once an InputStream has been set.Parsing involves iterating through events in the XML stream. An eventincludes beginning and end tags, elements and attributes. While parsing,getName gets the tag name and getAttributeValue gets a namedattribute from the current tag. For tags that have text between a start andend tag, the getNext() method must be used – the text is consideredanother event.The code below shows the kXML parser in action. In this exampleall information is made up of attributes within tags, so the parser’sgetNext() method is not used for retrieving text (see processExpenseResponse in the midlet.sync.SynchronizeThread classfor an example of its usage).// SynchronizationThread processPersonResponse() methodprivate void processPersonResponse(String reply)throws IOException, XmlPullParserException, DAOException {KXmlParser parser = new KXmlParser();THE EXPENSE APPLICATION275// turn the reply into a input stream for the xml parser...ByteArrayInputStream bais = new ByteArrayInputStream(reply.getBytes());parser.setInput(bais, null);int eventType = parser.getEventType();while (eventType != KXmlParser.END_DOCUMENT) {if (eventType == KXmlParser.START_TAG) {// the information we want is always in the attributes, parsing// is simple: if it’s a tag we are interested in then get// the information, otherwise ignore!int tagId = 0;// id == 1 for main user, 2 == subordinateif (parser.getName().equals(TAG_PERSON_MAIN)) {// main user, ensure they are the first persontagId = 1;} else if (parser.getName().equals(TAG_PERSON_SUBORD)) {// subordinate person, ensure they are in the RMStagId = 2;} else if (parser.getName().equals(TAG_RESPONSE)) {syncId = Long.parseLong(parser.getAttributeValue("",TAG_RESPONSE_SYNCID));}// are we doing some processing??if (tagId != 0) {// get the attributes and do some more workString name = parser.getAttributeValue("", TAG_PERSON_NAME);short id = Short.parseShort(parser.getAttributeValue("",TAG_PERSON_ID));short bhId = -1;if (tagId == 1) {// remove current users...} else {// sub ord instead of main user.bhId = settings.getUserId();}// ensure the RMS is update to date......}}eventType = parser.next();}bais.close();}5.2.6.5 Message Exchange ExampleAn example of the XML request and response messages from a synchronization operation now follows.The first request and response is for a user that is using a device forthe first time.