Home | History | Annotate | Download | only in threadsample
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.threadsample;
     18 
     19 import android.app.IntentService;
     20 import android.content.ContentValues;
     21 import android.content.Intent;
     22 import android.database.Cursor;
     23 
     24 import org.apache.http.HttpStatus;
     25 import org.xmlpull.v1.XmlPullParserException;
     26 
     27 import java.io.IOException;
     28 import java.net.HttpURLConnection;
     29 import java.net.MalformedURLException;
     30 import java.net.URL;
     31 import java.net.URLConnection;
     32 import java.util.Date;
     33 import java.util.Vector;
     34 
     35 /**
     36  * This service pulls RSS content from a web site URL contained in the incoming Intent (see
     37  * onHandleIntent()). As it runs, it broadcasts its status using LocalBroadcastManager; any
     38  * component that wants to see the status should implement a subclass of BroadcastReceiver and
     39  * register to receive broadcast Intents with category = CATEGORY_DEFAULT and action
     40  * Constants.BROADCAST_ACTION.
     41  *
     42  */
     43 public class RSSPullService extends IntentService {
     44     // Used to write to the system log from this class.
     45     public static final String LOG_TAG = "RSSPullService";
     46 
     47     // Defines and instantiates an object for handling status updates.
     48     private BroadcastNotifier mBroadcaster = new BroadcastNotifier(this);
     49 
     50     /**
     51      * An IntentService must always have a constructor that calls the super constructor. The
     52      * string supplied to the super constructor is used to give a name to the IntentService's
     53      * background thread.
     54      */
     55     public RSSPullService() {
     56 
     57         super("RSSPullService");
     58     }
     59 
     60     /**
     61      * In an IntentService, onHandleIntent is run on a background thread.  As it
     62      * runs, it broadcasts its current status using the LocalBroadcastManager.
     63      * @param workIntent The Intent that starts the IntentService. This Intent contains the
     64      * URL of the web site from which the RSS parser gets data.
     65      */
     66     @Override
     67     protected void onHandleIntent(Intent workIntent) {
     68         // Gets a URL to read from the incoming Intent's "data" value
     69         String localUrlString = workIntent.getDataString();
     70 
     71         // Creates a projection to use in querying the modification date table in the provider.
     72         final String[] dateProjection = new String[]
     73         {
     74             DataProviderContract.ROW_ID,
     75             DataProviderContract.DATA_DATE_COLUMN
     76         };
     77 
     78         // A URL that's local to this method
     79         URL localURL;
     80 
     81         // A cursor that's local to this method.
     82         Cursor cursor = null;
     83 
     84         /*
     85          * A block that tries to connect to the Picasa featured picture URL passed as the "data"
     86          * value in the incoming Intent. The block throws exceptions (see the end of the block).
     87          */
     88         try {
     89 
     90             // Convert the incoming data string to a URL.
     91             localURL = new URL(localUrlString);
     92 
     93             /*
     94              * Tries to open a connection to the URL. If an IO error occurs, this throws an
     95              * IOException
     96              */
     97             URLConnection localURLConnection = localURL.openConnection();
     98 
     99             // If the connection is an HTTP connection, continue
    100             if ((localURLConnection instanceof HttpURLConnection)) {
    101 
    102                 // Broadcasts an Intent indicating that processing has started.
    103                 mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);
    104 
    105                 // Casts the connection to a HTTP connection
    106                 HttpURLConnection localHttpURLConnection = (HttpURLConnection) localURLConnection;
    107 
    108                 // Sets the user agent for this request.
    109                 localHttpURLConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);
    110 
    111                 /*
    112                  * Queries the content provider to see if this URL was read previously, and when.
    113                  * The content provider throws an exception if the URI is invalid.
    114                  */
    115                 cursor = getContentResolver().query(
    116                         DataProviderContract.DATE_TABLE_CONTENTURI,
    117                         dateProjection,
    118                         null,
    119                         null,
    120                         null);
    121 
    122                 // Flag to indicate that new metadata was retrieved
    123                 boolean newMetadataRetrieved;
    124 
    125                 /*
    126                  * Tests to see if the table contains a modification date for the URL
    127                  */
    128                 if (null != cursor && cursor.moveToFirst()) {
    129 
    130                     // Find the URL's last modified date in the content provider
    131                     long storedModifiedDate =
    132                             cursor.getLong(cursor.getColumnIndex(
    133                                     DataProviderContract.DATA_DATE_COLUMN)
    134                             )
    135                     ;
    136 
    137                     /*
    138                      * If the modified date isn't 0, sets another request property to ensure that
    139                      * data is only downloaded if it has changed since the last recorded
    140                      * modification date. Formats the date according to the RFC1123 format.
    141                      */
    142                     if (0 != storedModifiedDate) {
    143                         localHttpURLConnection.setRequestProperty(
    144                                 "If-Modified-Since",
    145                                 org.apache.http.impl.cookie.DateUtils.formatDate(
    146                                         new Date(storedModifiedDate),
    147                                         org.apache.http.impl.cookie.DateUtils.PATTERN_RFC1123));
    148                     }
    149 
    150                     // Marks that new metadata does not need to be retrieved
    151                     newMetadataRetrieved = false;
    152 
    153                 } else {
    154 
    155                     /*
    156                      * No modification date was found for the URL, so newmetadata has to be
    157                      * retrieved.
    158                      */
    159                     newMetadataRetrieved = true;
    160 
    161                 }
    162 
    163                 // Reports that the service is about to connect to the RSS feed
    164                 mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_CONNECTING);
    165 
    166                 // Gets a response code from the RSS server
    167                 int responseCode = localHttpURLConnection.getResponseCode();
    168 
    169                 switch (responseCode) {
    170 
    171                     // If the response is OK
    172                     case HttpStatus.SC_OK:
    173 
    174                         // Gets the last modified data for the URL
    175                         long lastModifiedDate = localHttpURLConnection.getLastModified();
    176 
    177                         // Reports that the service is parsing
    178                         mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_PARSING);
    179 
    180                         /*
    181                          * Instantiates a pull parser and uses it to parse XML from the RSS feed.
    182                          * The mBroadcaster argument send a broadcaster utility object to the
    183                          * parser.
    184                          */
    185                         RSSPullParser localPicasaPullParser = new RSSPullParser();
    186 
    187                         localPicasaPullParser.parseXml(
    188                             localURLConnection.getInputStream(),
    189                             mBroadcaster);
    190 
    191                         // Reports that the service is now writing data to the content provider.
    192                         mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_WRITING);
    193 
    194                         // Gets image data from the parser
    195                         Vector<ContentValues> imageValues = localPicasaPullParser.getImages();
    196 
    197                         // Stores the number of images
    198                         int imageVectorSize = imageValues.size();
    199 
    200                         // Creates one ContentValues for each image
    201                         ContentValues[] imageValuesArray = new ContentValues[imageVectorSize];
    202 
    203                         imageValuesArray = imageValues.toArray(imageValuesArray);
    204 
    205                         /*
    206                          * Stores the image data in the content provider. The content provider
    207                          * throws an exception if the URI is invalid.
    208                          */
    209                         getContentResolver().bulkInsert(
    210                                 DataProviderContract.PICTUREURL_TABLE_CONTENTURI, imageValuesArray);
    211 
    212                         // Creates another ContentValues for storing date information
    213                         ContentValues dateValues = new ContentValues();
    214 
    215                         // Adds the URL's last modified date to the ContentValues
    216                         dateValues.put(DataProviderContract.DATA_DATE_COLUMN, lastModifiedDate);
    217 
    218                         if (newMetadataRetrieved) {
    219 
    220                             // No previous metadata existed, so insert the data
    221                             getContentResolver().insert(
    222                                 DataProviderContract.DATE_TABLE_CONTENTURI,
    223                                 dateValues
    224                             );
    225 
    226                         } else {
    227 
    228                             // Previous metadata existed, so update it.
    229                             getContentResolver().update(
    230                                     DataProviderContract.DATE_TABLE_CONTENTURI,
    231                                     dateValues,
    232                                     DataProviderContract.ROW_ID + "=" +
    233                                             cursor.getString(cursor.getColumnIndex(
    234                                                             DataProviderContract.ROW_ID)), null);
    235                         }
    236                         break;
    237 
    238                 }
    239 
    240                 // Reports that the feed retrieval is complete.
    241                 mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
    242             }
    243 
    244         // Handles possible exceptions
    245         } catch (MalformedURLException localMalformedURLException) {
    246 
    247             localMalformedURLException.printStackTrace();
    248 
    249         } catch (IOException localIOException) {
    250 
    251             localIOException.printStackTrace();
    252 
    253         } catch (XmlPullParserException localXmlPullParserException) {
    254 
    255             localXmlPullParserException.printStackTrace();
    256 
    257         } finally {
    258 
    259             // If an exception occurred, close the cursor to prevent memory leaks.
    260             if (null != cursor) {
    261                 cursor.close();
    262             }
    263         }
    264     }
    265 
    266 }
    267