• No se han encontrado resultados

SIMULACIÓN

In document TRABAJO FIN DE GRADO (página 164-190)

ANEXO VI: MODELADO Y SIMULACIÓN

6.4. SIMULACIÓN

content://authority/path

content://authority/path/id

The first part of the URI (content://) is referred to as the scheme. For content URIs, this is always content://.

The next part of the URI is referred to as the authority. The authority is specific to an individual content provider and allows Android to determine which content provider to route a request to. Because the authority for each content provider exists at the Android system level, it is important that all content providers use unique authorities to avoid naming collisions. A standard convention is to use an app’s package name with .provider

appended to the end to ensure uniqueness across the Android device.

The path portion of a content URI indicates the collection of data being targeted by a request. For example, content providers that are backed by a database may use the path to indicate a certain table in the database that a request is targeting.

The last part of a content URI is the ID. The ID can be used to uniquely identify an individual data member in a content URI path. For content providers that are backed by a database, the ID usually represents the primary key of the table that is defined by the path. The ID is optional, and when not used, a URI refers to the entire collection of data that is referred to by the path.

Exposing Data with a Content Provider

Content providers have the ability to expose multiple types of data to app components. In addition, the details of how the data is stored and retrieved can vary without these details being exposed to other app components. Content providers can expose data that is stored in a database or files stored on the file system, or even retrieve data from a remote Web server. The remainder of this chapter assumes a fairly common case for content providers, providing access to data that is stored in an SQLite database.

The next sections discuss the APIs that are used to communicate with a content provider and introduce some of the concepts that are necessary to work with content providers in Android.

The ContentProvider and the ContentResolver are the two major classes that apps interact with, both directly and indirectly, when working with the content provider API.

The details of how each class is used and how they interact are discussed in the following sections.

Implementing a Content Provider

android.content.ContentProvider is the base class for all content providers in Android.

Whether the concrete implementation that is provided by an app returns data from a database, provides access to a file on the file system, or exposes data from a Web service,

ptg18221911 Exposing Data with a Content Provider 103

it extends android.content.ContentProvider. All content providers must also implement, at minimum, the following abstract methods inherited from android.

content.ContentProvider:

boolean onCreate()

Uri insert(Uri uri, ContentValues values)

int delete(Uri uri, String selection, String[] selectionArgs)

String getType(Uri uri)

Cursor query(Uri uri, String[] projection, String selection, String[]

selectionArgs, String sortOrder)

int update(Uri uri, ContentValues values, String selection, String[]

selectionArgs)

onCreate()

The onCreate() method is called at the beginning of a content provider’s lifecycle and, like other Android components, can be a convenient place to perform initialization for the class. However, it is important to remember that the call to onCreate() happens on the main thread, so it is important to not perform any lengthy tasks in onCreate(). Also, unlike other Android components, the call to ContentProvider.onCreate() happens at app start-up as opposed to the first time the content provider is accessed. This means that any delays in finishing the method will cause the entire app to be delayed when starting up.

If a content provider is supported by an SQLite database, extra care should be used when initially accessing the database. It is important to remember that a database may be upgraded, if needed, when it is first accessed. This means that the onCreate() method of a content provider is not a good place to create a connection to a database as that could cause the database to be upgraded in the main thread while the app is started. Since the database is likely to reside on disk, it will almost certainly delay the initial app open routine.

ContentProvider.onCreate() returns a boolean indicating if the initialization was successful. A value of true indicates that the provider was successfully initialized, and a value of false indicates an error.

insert()

The insert() method is used to insert an entry into the database:

insert(Uri uri, ContentValues values)

The method takes two parameters: a URI declaring which table to perform the insertion on, and a ContentValues object that contains the values to be inserted into the table. After the row is inserted, this method should make a call to ContentResolver.

notifyChange() to make other parts of the app, or other apps, aware that the table has been updated if they are using a content observer.

ptg18221911 Recall from Chapter 5 that the content values used by the SQLiteDatabase. insert()

method contain name/value pairs for the row to be inserted. The ContentValues object passed to ContentProvider.insert() works the same way. In fact, the ContentValues

object passed to ContentProvider.insert() can be used as a parameter to

SQLiteDatabase.insert() without modification to insert data into a table.

ContentProvider.insert() finishes by returning a Uri object that references the newly created row in the provider. This URI should be constructed in a way that allows an external caller to retrieve the newly inserted row from the content provider.

This method can be called on any thread, so it must ensure that all logic can execute safely when called by multiple threads.

delete()

The ContentProvider.delete() method removes the row from the table that is specified by the uri parameter:

delete(Uri uri, String selection, String[] selectionArgs)

The uri parameter may refer either to a table in the database (content:// authority/

table) or to a specific row of a table to be deleted (content://authority/table/id).

When the URI refers to a table, the delete() method must use the selection and

selectionArgs parameters to determine which row(s) should be deleted from the table.

Like the ContentProvider.insert() method, the selection and selectionArgs

parameters can be passed to SQLiteDatabase.delete() without being manipulated by the ContentProvider.delete() method.

The ContentProvider.delete() method returns the number of rows that have been de-leted by the call. This is usually the same value that is returned by SQLiteDatabase.delete().

The ContentProvider.delete() method may also be called on any thread and needs to ensure that its operation happens in a thread-safe manner. It should call

ContentResolver.notifyChange() to inform any observers that the table has changed.

getType()

The ContentProvider.getType() method returns the MIME type for the given URI:

getType(Uri uri)

When the URI refers to a table (content://authority/table), the method should return a String that starts with vnd.android.cursor.dir/. When the URI refers to a single row in a table (content://authority/table/id), the method should return a

String that begins with vnd.android.cursor.item.

After the prefix that is dependent on the type of URI, the rest of the returned String

should contain the content provider’s authority and the table name from the URI. For exam-ple, if the input URI is content://myAuthority/tableName/32, the MIME type would be

vnd.android.cursor.item/myAuthority.tableName

This method can be called by any thread and must ensure thread safety.

ptg18221911 Exposing Data with a Content Provider 105

Note

The discussion of MIME types applies only to content providers that are returning data from a database. If the content provider is exposing files to a client, the getType() method should return the MIME type of the file.

query()

The query() method performs a query against the content provider:

query(Uri uri,

String[] projection, String selection, String[] selectionArgs, String sortOrder)

The URI in the parameter list specifies the table(s) against which to perform the query. All other parameters can be passed to an SQLiteDatabase.query() method, or used by an SQLiteQueryBuilder to build the query. The cursor resulting from either SQLiteDatabase.query() or SQLiteQueryBuilder should be returned by

ContentProvider.query().

The ContentProvider.query() method may be called from any thread.

update()

The update() method performs an update operation on the table specified by the URI:

update(Uri uri,

ContentValues values, String selection, String[] selectionArgs)

The ContentValues parameter contains the updated column/value pairs for the table.

The selection and selectionArgs parameters select which rows from the table the update should be applied to. The values, selection, and selectionArgs parameters can all be passed to an SQLiteDatabase.update() method without modification. The return value from SQLiteDatabase.update() should also be returned by ContentProvider.

update().

The ContentProvider.update() method should make a call to ContentResolver.

notifyChange() to notify observers that the table data has changed. The method can be called from any thread.

bulkInsert() and applyBatch()

The methods discussed in the previous sections must be implemented because they are abstract methods of android.content.ContentProvider and failure to implement them

ptg18221911 results in a compile error. There are, however, two additional methods that should be

overridden:

int bulkInsert(Uri uri, ContentValues[] values)

ContentProviderResult applyBatch(ArrayList<ContentProviderOperations operations)

Both methods are used to perform multiple operations on a database. The problem with their default implementations is that neither wraps the operations in a transaction, which means that neither method is atomic (individual operations can fail). Additionally, recall from Chapter 5 that when not wrapped in a transaction, every SQLiteDatabase

modification operation starts a new transaction for the individual operation. This has severe impacts on runtime performance and makes both calls much slower than if they wrapped all operations in a single transaction.

In both cases, each method should, at minimum, be overridden to wrap a call to super

in a transaction. Listing 6.1 shows the minimal override for both methods.

Listing 6.1 Adding Transaction Support to bulkInsert() and applyBatch()

@Override

public int bulkInsert(Uri uri, ContentValues[] values) { final SQLiteDatabase db = helper.getWritableDatabase();

db.beginTransaction();

try {

final int count = super.bulkInsert(uri, values);

db.setTransactionSuccessful();

return count;

} finally {

db.endTransaction();

} }

@Override public

ContentProviderResult[]

applyBatch(ArrayList<ContentProviderOperation> operations)

ptg18221911 Exposing Data with a Content Provider 107

throws OperationApplicationException {

final SQLiteDatabase db = helper.getWritableDatabase();

db.beginTransaction();

try {

final ContentProviderResult[] results = super.applyBatch(operations);

db.setTransactionSuccessful();

return results;

} finally {

db.endTransaction();

} }

In addition to inheriting from android.content.ContentProvider and implementing the required methods, a content provider needs to be listed in an app’s manifest,

specifically in the <application> element. Listing 6.2 shows the minimal entry for a content provider that is available only to the app containing it.

Listing 6.2 Content Provider Manifest Listing

<provider

android:name=".provider.MyProvider"

android:authorities="com.example.provider"

android:exported="false" />

The <provider> element in Listing 6.2 declares a class named MyProvider as an available content provider for the app with the android:name attribute of the

<provider> element. The android:authorities attribute lists the authorities that the content provider supports. Content authorities are used in URIs that are passed to a content resolver which will the send the request to the correct content provider. The

android:authorities element is what binds an authority to a content provider.

The third attribute in the minimal <provider> element is the android:export

attribute. This attribute defines whether the content provider can be used by other apps.

In Listing 6.2, the content provider is available only to the local app. Allowing a content provider to be accessed by other apps will be discussed in detail later in the chapter.

ptg18221911 With an implementation of an android.content.ContentProvider and an entry for

that content provider in the manifest, an app is ready to start using the content provider.

To use the content provider, an app must first access its content resolver.

In document TRABAJO FIN DE GRADO (página 164-190)