2016年9月5日 星期一

Creating a Background Service

Ref : https://developer.android.com/training/run-background-service/create-service.html

Creating a Background Service


The IntentService class provides a straightforward structure for running an operation on a single background thread. This allows it to handle long-running operations without affecting your user interface's responsiveness. Also, an IntentService isn't affected by most user interface lifecycle events, so it continues to run in circumstances that would shut down an AsyncTask


An IntentService has a few limitations:
  • It can't interact directly with your user interface. To put its results in the UI, you have to send them to an Activity.
  • Work requests run sequentially. If an operation is running in an IntentService, and you send it another request, the request waits until the first operation is finished.
  • An operation running on an IntentService can't be interrupted.
However, in most cases an IntentService is the preferred way to perform simple background operations.
This lesson shows you how to create your own subclass of IntentService. The lesson also shows you how to create the required callback method onHandleIntent(). Finally, the lesson describes shows you how to define the IntentService in your manifest file.

Create an IntentService

To create an IntentService component for your app, define a class that extends IntentService, and within it, define a method that overrides onHandleIntent(). For example:

public class RSSPullService extends IntentService {
    @Override
    protected void onHandleIntent(Intent workIntent) {
        // Gets data from the incoming Intent
        String dataString = workIntent.getDataString();
        ...
        // Do work here, based on the contents of dataString
        ...
    }
}

Notice that the other callbacks of a regular Service component, such as onStartCommand() are automatically invoked by IntentService. In an IntentService, you should avoid overriding these callbacks.

Define the IntentService in the Manifest


An IntentService also needs an entry in your application manifest. Provide this entry as a <service> element that's a child of the <application> element:

<application
        android:icon="@drawable/icon"
        android:label="@string/app_name">
        ...
        <!--
            Because android:exported is set to "false",
            the service is only available to this app.
        -->
        <service
            android:name=".RSSPullService"
            android:exported="false"/>
        ...
    <application/>




Sending Work Requests to the Background Service


The previous lesson showed you how to create an IntentService class. This lesson shows you how to trigger the IntentService to run an operation by sending it an Intent. This Intent can optionally contain data for the IntentService to process. You can send an Intent to an IntentService from any point in an Activity or Fragment

Create and Send a Work Request to an IntentService



To create a work request and send it to an IntentService, create an explicit Intent, add work request data to it, and send it to IntentService by calling startService().
The next snippets demonstrate this:

  1. Create a new, explicit Intent for the IntentService called RSSPullService.
    /*
     * Creates a new Intent to start the RSSPullService
     * IntentService. Passes a URI in the
     * Intent's "data" field.
     */
    mServiceIntent = new Intent(getActivity(), RSSPullService.class);
    mServiceIntent.setData(Uri.parse(dataUrl));
  2. Call startService()
    // Starts the IntentService
    getActivity().startService(mServiceIntent);


Reporting Work Status


This lesson shows you how to report the status of a work request run in a background service to the component that sent the request. This allows you, for example, to report the status of the request in anActivity object's UI. The recommended way to send and receive status is to use aLocalBroadcastManager, which limits broadcast Intent objects to components in your own app.

Report Status From an IntentService


To send the status of a work request in an IntentService to other components, first create an Intent  that contains the status in its extended data. As an option, you can add an action and data URI to this Intent.
Next, send the Intent by calling LocalBroadcastManager.sendBroadcast(). This sends the Intent to any component in your application that has registered to receive it. To get an instance of LocalBroadcastManager, call getInstance().
public final class Constants {
    ...
    // Defines a custom Intent action
    public static final String BROADCAST_ACTION =
        "com.example.android.threadsample.BROADCAST";
    ...
    // Defines the key for the status "extra" in an Intent
    public static final String EXTENDED_DATA_STATUS =
        "com.example.android.threadsample.STATUS";
    ...
}
public class RSSPullService extends IntentService {
...
    /*
     * Creates a new Intent containing a Uri object
     * BROADCAST_ACTION is a custom Intent action
     */
    Intent localIntent =
            new Intent(Constants.BROADCAST_ACTION)
            // Puts the status into the Intent
            .putExtra(Constants.EXTENDED_DATA_STATUS, status);
    // Broadcasts the Intent to receivers in this app.
    LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
...
}

The next step is to handle the incoming broadcast Intent objects in the component that sent the original work request.

Receive Status Broadcasts from an IntentService


To receive broadcast Intent objects, use a subclass of BroadcastReceiver. In the subclass, implement the BroadcastReceiver.onReceive() callback method, which LocalBroadcastManager invokes when it receives an IntentLocalBroadcastManager passes the incoming Intent toBroadcastReceiver.onReceive().
For example:
// Broadcast receiver for receiving status updates from the IntentService
private class ResponseReceiver extends BroadcastReceiver
{
    // Prevents instantiation
    private DownloadStateReceiver() {
    }
    // Called when the BroadcastReceiver gets an Intent it's registered to receive
    @
    public void onReceive(Context context, Intent intent) {
...
        /*
         * Handle Intents here.
         */
...
    }
}

private class DownloadStateReceiver extends BroadcastReceiver {
    
    private DownloadStateReceiver() {
        
        // prevents instantiation by other packages.    }
    /**     *     * This method is called by the system when a broadcast Intent is matched by this class'     * intent filters     *     * @param context An Android context     * @param intent The incoming broadcast Intent     */    @Override    public void onReceive(Context context, Intent intent) {
        
        /*         * Gets the status from the Intent's extended data, and chooses the appropriate action         */        switch (intent.getIntExtra(Constants.EXTENDED_DATA_STATUS,
                Constants.STATE_ACTION_COMPLETE)) {
            
            // Logs "started" state            case Constants.STATE_ACTION_STARTED:
                if (Constants.LOGD) {
                    
                    Log.d(CLASS_TAG, "State: STARTED");
                }
                break;
            // Logs "connecting to network" state            case Constants.STATE_ACTION_CONNECTING:
                if (Constants.LOGD) {
                    
                    Log.d(CLASS_TAG, "State: CONNECTING");
                }
                break;
             // Logs "parsing the RSS feed" state             case Constants.STATE_ACTION_PARSING:
                if (Constants.LOGD) {
                    
                    Log.d(CLASS_TAG, "State: PARSING");
                }
                break;
            // Logs "Writing the parsed data to the content provider" state            case Constants.STATE_ACTION_WRITING:
                if (Constants.LOGD) {
                    
                    Log.d(CLASS_TAG, "State: WRITING");
                }
                break;
            // Starts displaying data when the RSS download is complete            case Constants.STATE_ACTION_COMPLETE:
                // Logs the status                if (Constants.LOGD) {
                    
                    Log.d(CLASS_TAG, "State: COMPLETE");
                }

                // Finds the fragment that displays thumbnails                PhotoThumbnailFragment localThumbnailFragment =
                        (PhotoThumbnailFragment) getSupportFragmentManager().findFragmentByTag(
                                Constants.THUMBNAIL_FRAGMENT_TAG);
                
                // If the thumbnail Fragment is hidden, don't change its display status                if ((localThumbnailFragment == null)
                        || (!localThumbnailFragment.isVisible()))
                    return;
                
                // Indicates that the thumbnail Fragment is visible
                localThumbnailFragment.setLoaded(true);
                break;
            default:
                break;
        }
    }
}


Once you've defined the BroadcastReceiver, you can define filters for it that match specific actions, categories, and data. To do this, create anIntentFilter. This first snippet shows how to define the filter:
// Class that displays photos
public class DisplayActivity extends FragmentActivity {
    ...
    public void onCreate(Bundle stateBundle) {
        ...
        super.onCreate(stateBundle);
        ...
        // The filter's action is BROADCAST_ACTION
        IntentFilter mStatusIntentFilter = new IntentFilter(
                Constants.BROADCAST_ACTION);

        // Adds a data filter for the HTTP scheme
        mStatusIntentFilter.addDataScheme("http");
        ...
sample code 的內容
// One filter is for the action ACTION_VIEW_IMAGEIntentFilter displayerIntentFilter = new IntentFilter(
        Constants.ACTION_VIEW_IMAGE);

// Adds a data filter for the HTTP schemedisplayerIntentFilter.addDataScheme("http");

To register the BroadcastReceiver and the IntentFilter with the system, get an instance of LocalBroadcastManager and call its registerReceiver()method. This next snippet shows how to register the BroadcastReceiver and its IntentFilter:
        // Instantiates a new DownloadStateReceiver
        DownloadStateReceiver mDownloadStateReceiver =
                new DownloadStateReceiver();
        // Registers the DownloadStateReceiver and its intent filters
        LocalBroadcastManager.getInstance(this).registerReceiver(
                mDownloadStateReceiver,
                mStatusIntentFilter);
        ...
// Instantiates a new broadcast receiver for handling Fragment state
private FragmentDisplayer mFragmentDisplayer = new FragmentDisplayer();

// Registers the receiverLocalBroadcastManager.getInstance(this).registerReceiver(
        mFragmentDisplayer,
        displayerIntentFilter);

private class FragmentDisplayer extends BroadcastReceiver {

   @Override
   public void onReceive(Context context, Intent intent) {

   }
}

A single BroadcastReceiver can handle more than one type of broadcast Intent object, each with its own action. This feature allows you to run different code for each action, without having to define a separate BroadcastReceiver for each action. To define another IntentFilter for the same BroadcastReceiver, create the IntentFilter and repeat the call to registerReceiver(). For example:
        /*
         * Instantiates a new action filter.
         * No data filter is needed.
         */
        statusIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE);
        ...
        // Registers the receiver with the new filter
        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
                mDownloadStateReceiver,
                mIntentFilter);
IntentFilter statusIntentFilter = new IntentFilter(
        Constants.BROADCAST_ACTION);

// Sets the filter's category to DEFAULTstatusIntentFilter.addCategory(Intent.CATEGORY_DEFAULT);

// Instantiates a new DownloadStateReceivermDownloadStateReceiver = new DownloadStateReceiver();

// Registers the DownloadStateReceiver and its intent filtersLocalBroadcastManager.getInstance(this).registerReceiver(
        mDownloadStateReceiver,
        statusIntentFilter);

/* * Creates intent filters for the FragmentDisplayer */
// One filter is for the action ACTION_VIEW_IMAGEIntentFilter displayerIntentFilter = new IntentFilter(
        Constants.ACTION_VIEW_IMAGE);

// Adds a data filter for the HTTP schemedisplayerIntentFilter.addDataScheme("http");

// Registers the receiverLocalBroadcastManager.getInstance(this).registerReceiver(
        mFragmentDisplayer,
        displayerIntentFilter);


Sending an broadcast Intent doesn't start or resume an Activity. The BroadcastReceiver for an Activity receives and processes Intent objects even when your app is in the background, but doesn't force your app to the foreground. If you want to notify the user about an event that happened in the background while your app was not visible, use a NotificationNever start an Activity in response to an incoming broadcast Intent.

Loading Data in the Background

Querying a ContentProvider for data you want to display takes time. If you run the query directly from an Activity, it may get blocked and cause the system to issue an "Application Not Responding" message. Even if it doesn't, users will see an annoying delay in the UI. To avoid these problems, you should initiate a query on a separate thread, wait for it to finish, and then display the results.
You can do this in a straightforward way by using an object that runs a query asynchronously in the background and reconnects to your Activity when it's finished. This object is a CursorLoader. Besides doing the initial background query, a CursorLoader automatically re-runs the query when data associated with the query changes.
This class describes how to use a CursorLoader to run a background query. Examples in this class use the v4 Support Library versions of classes, which support platforms starting with Android 1.6.

Running a Query with a CursorLoader


CursorLoader runs an asynchronous query in the background against a ContentProvider, and returns the results to the Activity or FragmentActivity from which it was called. This allows the Activity or FragmentActivity to continue to interact with the user while the query is ongoing.

Define an Activity That Uses CursorLoader


To use a CursorLoader with an Activity or FragmentActivity, use the LoaderCallbacks<Cursor>interface. A CursorLoader invokes callbacks defined in this interface to communicate with the class; this lesson and the next one describe each callback in detail.
For example, this is how you should define a FragmentActivity that uses the support library version ofCursorLoader. By extending FragmentActivity, you get support for CursorLoader as well as Fragment:
public class PhotoThumbnailFragment extends FragmentActivity implements
        LoaderManager.LoaderCallbacks<Cursor> {
...
}

public class PhotoThumbnailFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {

}


Initialize the Query

To initialize a query, call LoaderManager.initLoader(). This initializes the background framework. You can do this after the user has entered data that's used in the query, or, if you don't need any user data, you can do it in onCreate() or onCreateView(). For example:
    // Identifies a particular Loader being used in this component
    private static final int URL_LOADER = 0;
    ...
    /* When the system is ready for the Fragment to appear, this displays
     * the Fragment's View
     */
    public View onCreateView(
            LayoutInflater inflater,
            ViewGroup viewGroup,
            Bundle bundle) {
        ...
        /*
         * Initializes the CursorLoader. The URL_LOADER value is eventually passed
         * to onCreateLoader().
         */
        getLoaderManager().initLoader(URL_LOADER, null, this);
        ...
    }
Note: The method getLoaderManager() is only available in the Fragment class. To get a LoaderManager in a FragmentActivity, callgetSupportLoaderManager().


// Identifies a particular Loader being used in this componentprivate static final int URL_LOADER = 0;
// Initializes the CursorLoadergetLoaderManager().initLoader(URL_LOADER, null, this);

Start the Query

As soon as the background framework is initialized, it calls your implementation of onCreateLoader(). To start the query, return a CursorLoader from this method. You can instantiate an empty CursorLoader and then use its methods to define your query, or you can instantiate the object and define the query at the same time:
/*
* Callback that's invoked when the system has initialized the Loader and
* is ready to start the query. This usually happens when initLoader() is
* called. The loaderID argument contains the ID value passed to the
* initLoader() call.
*/
@Override
public Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle)
{
    /*
     * Takes action based on the ID of the Loader that's being created
     */
    switch (loaderID) {
        case URL_LOADER:
            // Returns a new CursorLoader
            return new CursorLoader(
                        getActivity(),   // Parent activity context
                        mDataUrl,        // Table to query
                        mProjection,     // Projection to return
                        null,            // No selection clause
                        null,            // No selection arguments
                        null             // Default sort order
        );
        default:
            // An invalid id was passed in
            return null;
    }
}
@Overridepublic Loader<Cursor> onCreateLoader(int loaderID, Bundle bundle)
{
    /*     * Takes action based on the ID of the Loader that's being created     */    switch (loaderID) {
        case URL_LOADER:
        // Returns a new CursorLoader        return new CursorLoader(
                    getActivity(),                                     // Context                    DataProviderContract.PICTUREURL_TABLE_CONTENTURI,  // Table to query                    PROJECTION,                                        // Projection to return                    null,                                              // No selection clause                    null,                                              // No selection arguments                    null                                               // Default sort order        );
        default:
            // An invalid id was passed in            return null;

    }
    
}

Once the background framework has the object, it starts the query in the background. When the query is done, the background framework callsonLoadFinished(), which is described in the next lesson.

Handling the Results

As shown in the previous lesson, you should begin loading your data with a CursorLoader in your implementation of onCreateLoader(). The loader then provides the query results to your Activity or FragmentActivity in your implementation of LoaderCallbacks.onLoadFinished(). One of the incoming arguments to this method is a Cursor containing the query results. You can use this object to update your data display or do further processing.
Besides onCreateLoader() and onLoadFinished(), you also have to implement onLoaderReset(). This method is invoked when CursorLoader detects that data associated with the Cursor has changed. When the data changes, the framework also re-runs the current query.

Handle Query Results


To display Cursor data returned by CursorLoader, use a View class that implements AdapterView and provide the view with an adapter that implements CursorAdapter. The system then automatically moves data from the Cursor to the view.
You can set up the linkage between the view and adapter before you have any data to display, and then move a Cursor into the adapter in theonLoadFinished() method. As soon as you move the Cursor into the adapter, the system automatically updates the view. This also happens if you change the contents of the Cursor.
For example:
public String[] mFromColumns = {
    DataProviderContract.IMAGE_PICTURENAME_COLUMN};
public int[] mToFields = {
    R.id.PictureName
};
// Gets a handle to a List View
ListView mListView = (ListView) findViewById(R.id.dataList);
/*
 * Defines a SimpleCursorAdapter for the ListView
 *
 */
SimpleCursorAdapter mAdapter =
    new SimpleCursorAdapter(
            this,                // Current context
            R.layout.list_item,  // Layout for a single row
            null,                // No Cursor yet
            mFromColumns,        // Cursor columns to use
            mToFields,           // Layout fields to use
            0                    // No flags
    );
// Sets the adapter for the view
mListView.setAdapter(mAdapter);
...
/*
 * Defines the callback that CursorLoader calls
 * when it's finished its query
 */
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    ...
    /*
     * Moves the query results into the adapter, causing the
     * ListView fronting this adapter to re-display
     */
    mAdapter.changeCursor(cursor);
}

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {
// Sets the View's data adapter to be a new GridViewAdaptermAdapter = new GridViewAdapter(getActivity());

// Gets a handle to the GridView in the layoutmGridView = ((GridView) localView.findViewById(android.R.id.list));
// Sets the GridView's data adaptermGridView.setAdapter(mAdapter);
}

@Overridepublic void onLoadFinished(Loader<Cursor> loader, Cursor returnCursor) {
    
    /*     *  Changes the adapter's Cursor to be the results of the load. This forces the View to     *  redraw.     */        mAdapter.changeCursor(returnCursor);
}

/* * Invoked when the CursorLoader is being reset. For example, this is called if the * data in the provider changes and the Cursor becomes stale. */@Overridepublic void onLoaderReset(Loader<Cursor> loader) {
    
    // Sets the Adapter's backing data to null. This prevents memory leaks.    mAdapter.changeCursor(null);
}

Delete Old Cursor References



The CursorLoader is reset whenever its Cursor becomes invalid. This usually occurs because the data associated with the Cursor has changed. Before re-running the query, the framework calls your implementation of onLoaderReset(). In this callback, you should delete all references to the currentCursor in order to prevent memory leaks. Once onLoaderReset() finishes, CursorLoader re-runs its query.
For example:

/*
 * Invoked when the CursorLoader is being reset. For example, this is
 * called if the data in the provider changes and the Cursor becomes stale.
 */
@Override
public void onLoaderReset(Loader<Cursor> loader) {

    /*
     * Clears out the adapter's reference to the Cursor.
     * This prevents memory leaks.
     */
    mAdapter.changeCursor(null);
}

接下來看sample code 的例子

先看   AndroidManifest.xml

<activity    android:name=".DisplayActivity"    android:label="@string/activity_title" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

可以發現  DisplayActivity.java 是入口的class ...


<service    android:name=".RSSPullService"    android:exported="false"/>

同時註冊了一個Service...   RSSPullService


接下來看    DisplayActivity.java

繼承自  FragmentActivity...  同時也實做   OnBackStackChangedListener interface
public class DisplayActivity extends FragmentActivity implements OnBackStackChangedListener {

View mMainView;

@Overridepublic void onCreate(Bundle stateBundle) {

       // Calls the super method (required)
       super.onCreate(stateBundle);

       // Inflates the main View, which will be the host View for the fragments       mMainView = getLayoutInflater().inflate(R.layout.fragmenthost, null);

      }

}



fragmenthost.xml  的視覺畫圖形如下


其實就只有一個LinearLayout...  設定id 為  fragmentHost


<LinearLayout    android:orientation="horizontal"    android:id="@+id/fragmentHost"    android:layout_width="fill_parent" android:layout_height="fill_parent"    android:animateLayoutChanges="true"    xmlns:android="http://schemas.android.com/apk/res/android" />


接下來 看

Create and Send a Work Request to an IntentService



search keyword "startService"...  發現在  PhotoFragment.java 裡面

public class PhotoThumbnailFragment extends Fragment implements        LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {



@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup viewGroup, Bundle bundle) {

mServiceIntent =
        new Intent(getActivity(), RSSPullService.class)
                .setData(Uri.parse(PICASA_RSS_URL));

// If there's no pre-existing state for this Fragmentif (bundle == null) {
    // If the data wasn't previously loaded    if (!this.mIsLoaded) { //  還沒有開始這個service
        // Starts the IntentService to download the RSS feed data        getActivity().startService(mServiceIntent);
    }

     // If this Fragment existed previously, gets its state    } else if (bundle.getBoolean(STATE_IS_HIDDEN, false)) {
    
       // Begins a transaction        FragmentTransaction localFragmentTransaction =
            getFragmentManager().beginTransaction();
    
        // Hides the Fragment        localFragmentTransaction.hide(this);
    
         // Commits the transaction        localFragmentTransaction.commit();
    }

}

}


接下來看 RSSPullService  的實做

public class RSSPullService extends IntentService {


// 建構子
public RSSPullService() {

    super("RSSPullService");
}


//  然後實做  onHandleIntent function
@Overrideprotected void onHandleIntent(Intent workIntent) {
    // Gets a URL to read from the incoming Intent's "data" value    String localUrlString = workIntent.getDataString();

    // Creates a projection to use in querying the modification date table in the provider.    final String[] dateProjection = new String[]
    {
        DataProviderContract.ROW_ID,
        DataProviderContract.DATA_DATE_COLUMN    };

    // A URL that's local to this method    URL localURL;

    // A cursor that's local to this method.    Cursor cursor = null;

    /*     * A block that tries to connect to the Picasa featured picture URL passed as the "data"     * value in the incoming Intent. The block throws exceptions (see the end of the block).     */    try {

        // Convert the incoming data string to a URL.        localURL = new URL(localUrlString);

        /*         * Tries to open a connection to the URL. If an IO error occurs, this throws an         * IOException         */        URLConnection localURLConnection = localURL.openConnection();

        // If the connection is an HTTP connection, continue        if ((localURLConnection instanceof HttpURLConnection)) {

            // Broadcasts an Intent indicating that processing has started.            mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_STARTED);

            // Casts the connection to a HTTP connection            HttpURLConnection localHttpURLConnection = (HttpURLConnection) localURLConnection;

            // Sets the user agent for this request.            localHttpURLConnection.setRequestProperty("User-Agent", Constants.USER_AGENT);

            /*             * Queries the content provider to see if this URL was read previously, and when.             * The content provider throws an exception if the URI is invalid.             */            cursor = getContentResolver().query(
                    DataProviderContract.DATE_TABLE_CONTENTURI,
                    dateProjection,
                    null,
                    null,
                    null);

            // Flag to indicate that new metadata was retrieved            boolean newMetadataRetrieved;

            /*             * Tests to see if the table contains a modification date for the URL             */            if (null != cursor && cursor.moveToFirst()) {

                // Find the URL's last modified date in the content provider                long storedModifiedDate =
                        cursor.getLong(cursor.getColumnIndex(
                                DataProviderContract.DATA_DATE_COLUMN)
                        )
                ;

                /*                 * If the modified date isn't 0, sets another request property to ensure that                 * data is only downloaded if it has changed since the last recorded                 * modification date. Formats the date according to the RFC1123 format.                 */                if (0 != storedModifiedDate) {
                    localHttpURLConnection.setRequestProperty(
                            "If-Modified-Since",
                            org.apache.http.impl.cookie.DateUtils.formatDate(
                                    new Date(storedModifiedDate),
                                    org.apache.http.impl.cookie.DateUtils.PATTERN_RFC1123));
                }

                // Marks that new metadata does not need to be retrieved                newMetadataRetrieved = false;

            } else {

                /*                 * No modification date was found for the URL, so newmetadata has to be                 * retrieved.                 */                newMetadataRetrieved = true;

            }

            // Reports that the service is about to connect to the RSS feed            mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_CONNECTING);

            // Gets a response code from the RSS server            int responseCode = localHttpURLConnection.getResponseCode();

            switch (responseCode) {

                // If the response is OK                case HttpStatus.SC_OK:

                    // Gets the last modified data for the URL                    long lastModifiedDate = localHttpURLConnection.getLastModified();

                    // Reports that the service is parsing                    mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_PARSING);

                    /*                     * Instantiates a pull parser and uses it to parse XML from the RSS feed.                     * The mBroadcaster argument send a broadcaster utility object to the                     * parser.                     */                    RSSPullParser localPicasaPullParser = new RSSPullParser();

                    localPicasaPullParser.parseXml(
                        localURLConnection.getInputStream(),
                        mBroadcaster);

                    // Reports that the service is now writing data to the content provider.                    mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_WRITING);

                    // Gets image data from the parser                    Vector<ContentValues> imageValues = localPicasaPullParser.getImages();

                    // Stores the number of images                    int imageVectorSize = imageValues.size();

                    // Creates one ContentValues for each image                    ContentValues[] imageValuesArray = new ContentValues[imageVectorSize];

                    imageValuesArray = imageValues.toArray(imageValuesArray);

                    /*                     * Stores the image data in the content provider. The content provider                     * throws an exception if the URI is invalid.                     */                    getContentResolver().bulkInsert(
                            DataProviderContract.PICTUREURL_TABLE_CONTENTURI, imageValuesArray);

                    // Creates another ContentValues for storing date information                    ContentValues dateValues = new ContentValues();

                    // Adds the URL's last modified date to the ContentValues                    dateValues.put(DataProviderContract.DATA_DATE_COLUMN, lastModifiedDate);

                    if (newMetadataRetrieved) {

                        // No previous metadata existed, so insert the data                        getContentResolver().insert(
                            DataProviderContract.DATE_TABLE_CONTENTURI,
                            dateValues
                        );

                    } else {

                        // Previous metadata existed, so update it.                        getContentResolver().update(
                                DataProviderContract.DATE_TABLE_CONTENTURI,
                                dateValues,
                                DataProviderContract.ROW_ID + "=" +
                                        cursor.getString(cursor.getColumnIndex(
                                                        DataProviderContract.ROW_ID)), null);
                    }
                    break;

            }

            // Reports that the feed retrieval is complete.            mBroadcaster.broadcastIntentWithState(Constants.STATE_ACTION_COMPLETE);
        }

    // Handles possible exceptions    } catch (MalformedURLException localMalformedURLException) {

        localMalformedURLException.printStackTrace();

    } catch (IOException localIOException) {

        localIOException.printStackTrace();

    } catch (XmlPullParserException localXmlPullParserException) {

        localXmlPullParserException.printStackTrace();

    } finally {

        // If an exception occurred, close the cursor to prevent memory leaks.        if (null != cursor) {
            cursor.close();
        }
    }
}

}





再回來看  onCreate()

@Overridepublic void onCreate(Bundle stateBundle) {
    // Sets fullscreen-related flags for the display    getWindow().setFlags(
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
            WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN                    | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
    
    // Calls the super method (required)    super.onCreate(stateBundle);
    
    // Inflates the main View, which will be the host View for the fragments    mMainView = getLayoutInflater().inflate(R.layout.fragmenthost, null);
    
    // Sets the content view for the Activity    setContentView(mMainView);
            
    /*     * Creates an intent filter for DownloadStateReceiver that intercepts broadcast Intents     */        // The filter's action is BROADCAST_ACTION    IntentFilter statusIntentFilter = new IntentFilter(
            Constants.BROADCAST_ACTION);
    
    // Sets the filter's category to DEFAULT    statusIntentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    
    // Instantiates a new DownloadStateReceiver    mDownloadStateReceiver = new DownloadStateReceiver();
    
    // Registers the DownloadStateReceiver and its intent filters    LocalBroadcastManager.getInstance(this).registerReceiver(
            mDownloadStateReceiver,
            statusIntentFilter);
    
    /*     * Creates intent filters for the FragmentDisplayer     */        // One filter is for the action ACTION_VIEW_IMAGE    IntentFilter displayerIntentFilter = new IntentFilter(
            Constants.ACTION_VIEW_IMAGE);
    
    // Adds a data filter for the HTTP scheme    displayerIntentFilter.addDataScheme("http");
    
    // Registers the receiver    LocalBroadcastManager.getInstance(this).registerReceiver(
            mFragmentDisplayer,
            displayerIntentFilter);
   
    // Creates a second filter for ACTION_ZOOM_IMAGE    displayerIntentFilter = new IntentFilter(Constants.ACTION_ZOOM_IMAGE);
    
    // Registers the receiver    LocalBroadcastManager.getInstance(this).registerReceiver(
            mFragmentDisplayer,
            displayerIntentFilter);
    
    // Gets an instance of the support library FragmentManager    FragmentManager localFragmentManager = getSupportFragmentManager();
    
    /*     * Detects if side-by-side display should be enabled. It's only available on xlarge and     * sw600dp devices (for example, tablets). The setting in res/values/ is "false", but this     * is overridden in values-xlarge and values-sw600dp.     */    mSideBySide = getResources().getBoolean(R.bool.sideBySide);
    
    /*     * Detects if hiding navigation controls should be enabled. On xlarge andsw600dp, it should     * be false, to avoid having the user enter an additional tap.     */    mHideNavigation = getResources().getBoolean(R.bool.hideNavigation);
    
    /*     * Adds the back stack change listener defined in this Activity as the listener for the     * FragmentManager. See the method onBackStackChanged().     */    localFragmentManager.addOnBackStackChangedListener(this);

    // If the incoming state of the Activity is null, sets the initial view to be thumbnails    if (null == stateBundle) {
        
        // Starts a Fragment transaction to track the stack        FragmentTransaction localFragmentTransaction = localFragmentManager
                .beginTransaction();
        
        // Adds the PhotoThumbnailFragment to the host View        localFragmentTransaction.add(R.id.fragmentHost,
                new PhotoThumbnailFragment(), Constants.THUMBNAIL_FRAGMENT_TAG);
        
        // Commits this transaction to display the Fragment        localFragmentTransaction.commit();
        
    // The incoming state of the Activity isn't null.    } else {
        
        // Gets the previous state of the fullscreen indicator        mFullScreen = stateBundle.getBoolean(Constants.EXTENDED_FULLSCREEN);
        
        // Sets the fullscreen flag to its previous state        setFullScreen(mFullScreen);
        
        // Gets the previous backstack entry count.        mPreviousStackCount = localFragmentManager.getBackStackEntryCount();
    }
}





沒有留言:

張貼留言