• No se han encontrado resultados

PLAN PARA LA PROMOCIÓN Y MEJORA DE LA CONVIVENCIA ESCOLAR

In document Sexismo y violencia de género (página 173-179)

Each ContentProvider exposes a unique CONTENT_URI that identifies the content type it’ll handle. This URI can query data in two forms, singular or plural, as shown in table 5.1.

A provider can offer as many types of data as it likes. By using these formats, your application can either iterate through all the content offered by a provider or retrieve a specific datum of interest.

The Activity class has a managedQuery method that makes calls into registered ContentProvider classes. When you create your own ContentProvider in section 5.4.2, we’ll show you how a provider is registered with the platform. Each provider is required to advertise the CONTENT_URI it supports. To query the contacts provider, you Table 5.1 ContentProvider URI variations for different purposes

URI Purpose

content://food/ingredients/ Return List of all ingredients from the provider registered to handle content://food

content://food/meals/ Return List of all meals from the provider registered to handle content://food

content://food/meals/1 Return or manipulate single meal with ID 1 from the provider registered to handle content://food

151

Working with ContentProvider classes

have to know this URI and then get a Cursor by calling managedQuery. When you have the Cursor, you can use it, as we showed you in listing 5.11.

A ContentProvider typically supplies all the details of the URI and the types it sup- ports as constants in a class. In the android.provider package, you can find classes that correspond to built-in Android content providers, such as the MediaStore. These classes have nested inner classes that represent types of data, such as Audio and Images. Within those classes are additional inner classes, with constants that represent fields or columns of data for each type. The values you need to query and manipulate data come from the inner classes for each type.

For additional information, see the android.provider package in the Javadocs, which lists all the built-in providers. Now that we’ve covered a bit about using a provider, we’ll look at the other side of the coin—creating a ContentProvider.

5.4.2 Creating a ContentProvider

In this section, you’ll build a provider that handles data responsibilities for a generic Widget object you’ll define. This simple object includes a name, type, and category; in a real application, you could represent any type of data.

To start, define a provider constants class that declares the CONTENT_URI and MIME_TYPE your provider will support. In addition, you can place the column names your provider will handle here.

DEFINING A CONTENT_URI AND MIME_TYPE

In the following listing, as a prerequisite to extending the ContentProvider class for a custom provider, we define necessary constants for our Widget type.

Managed Cursor

To obtain a Cursor reference, you can also use the managedQuery method of the

Activity class. The activity automatically cleans up any managed Cursor objects when your Activity pauses and restarts them when it starts. If you just need to retrieve data within an Activity, you’ll want to use a managed Cursor, as opposed to a ContentResolver.

What if the content changes after the fact?

When you use a ContentProvider to make a query, you get only the current state of the data. The data could change after your call, so how do you stay up to date? To receive notifications when a Cursor changes, you can use the ContentObserver

API. ContentObserver supports a set of callbacks that trigger when data changes. The Cursor class provides register and unregister methods for Content- Observer objects.

public final class Widget implements BaseColumns { public static final String MIME_DIR_PREFIX = "vnd.android.cursor.dir";

public static final String MIME_ITEM_PREFIX = "vnd.android.cursor.item";

public static final String MIME_ITEM = "vnd.msi.widget"; public static final String MIME_TYPE_SINGLE =

MIME_ITEM_PREFIX + "/" + MIME_ITEM;

public static final String MIME_TYPE_MULTIPLE = MIME_DIR_PREFIX + "/" + MIME_ITEM;

public static final String AUTHORITY = "com.msi.manning.chapter5.Widget";

public static final String PATH_SINGLE = "widgets/#"; public static final String PATH_MULTIPLE = "widgets"; public static final Uri CONTENT_URI =

Uri.parse("content://" + AUTHORITY + "/" + PATH_MULTIPLE); public static final String DEFAULT_SORT_ORDER = "updated DESC"; public static final String NAME = "name";

public static final String TYPE = "type";

public static final String CATEGORY = "category"; public static final String CREATED = "created"; public static final String UPDATED = "updated"; }

In our Widget-related provider constants class, we first extend the BaseColumns class. Now our class has a few base constants, such as _ID. Next, we define the MIME_TYPE prefix for a set of multiple items and a single item. By convention, vnd.android. cursor.dir represents multiple items, and vnd.android.cursor.item represents a single item. We can then define a specific MIME item and combine it with the single and multiple paths to create two MIME_TYPE representations.

After we have the MIME details out of the way, we define the authority

B

and path for both single and multiple items that will be used in the CONTENT_URI that callers pass in to use our provider. Callers will ultimately start from the multiple-item URI, so we publish this one

C

.

After taking care of all the other details, we define column names that represent the variables in our Widget object, which correspond to fields in the database table we’ll use. Callers will use these constants to get and set specific fields. Now we’re on to the next part of the process, extending ContentProvider.

EXTENDING CONTENTPROVIDER

The following listing shows the beginning of our ContentProvider implementation class, WidgetProvider. In this part of the class, we do some housekeeping relating to the database we’ll use and the URI we’re supporting.

Listing 5.12 WidgetProvider constants, including columns and URI

Define authority

B

Define ultimate CONTENT_URI

C

153

Working with ContentProvider classes

public class WidgetProvider extends ContentProvider { private static final String CLASSNAME =

WidgetProvider.class.getSimpleName(); private static final int WIDGETS = 1; private static final int WIDGET = 2;

public static final String DB_NAME = "widgets_db"; public static final String DB_TABLE = "widget"; public static final int DB_VERSION = 1;

private static UriMatcher URI_MATCHER = null;

private static HashMap<String, String> PROJECTION_MAP; private SQLiteDatabase db;

static {

WidgetProvider.URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); WidgetProvider.URI_MATCHER.addURI(Widget.AUTHORITY,

Widget.PATH_MULTIPLE, WidgetProvider.WIDGETS); WidgetProvider.URI_MATCHER.addURI(Widget.AUTHORITY, Widget.PATH_SINGLE, WidgetProvider.WIDGET);

WidgetProvider.PROJECTION_MAP = new HashMap<String, String>(); WidgetProvider.PROJECTION_MAP.put(BaseColumns._ID, "_id"); WidgetProvider.PROJECTION_MAP.put(Widget.NAME, "name"); WidgetProvider.PROJECTION_MAP.put(Widget.TYPE, "type"); WidgetProvider.PROJECTION_MAP.put(Widget.CATEGORY, "category"); WidgetProvider.PROJECTION_MAP.put(Widget.CREATED, "created"); WidgetProvider.PROJECTION_MAP.put(Widget.UPDATED, "updated"); }

private static class DBOpenHelper extends SQLiteOpenHelper { private static final String DB_CREATE = "CREATE TABLE " + WidgetProvider.DB_TABLE

+ " (_id INTEGER PRIMARY KEY, name TEXT UNIQUE NOT NULL," + "type TEXT, category TEXT, updated INTEGER, created" + "INTEGER);";

public DBOpenHelper(Context context) {

super(context, WidgetProvider.DB_NAME, null, WidgetProvider.DB_VERSION);

}

@Override

public void onCreate(SQLiteDatabase db) { try {

db.execSQL(DBOpenHelper.DB_CREATE); } catch (SQLException e) {

// log and or handle }

}

@Override

public void onOpen(SQLiteDatabase db) { }

@Override

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

db.execSQL("DROP TABLE IF EXISTS " + WidgetProvider.DB_TABLE); onCreate(db);

}

Listing 5.13 The first portion of the WidgetProviderContentProvider

Define database constants

B

Use SQLiteDatabase reference

C

D

Create and open database

}

@Override

public boolean onCreate() {

DBOpenHelper dbHelper = new DBOpenHelper(getContext()); db = dbHelper.getWritableDatabase(); if (db == null) { return false; } else { return true; } } @Override

public String getType(Uri uri) {

switch (WidgetProvider.URI_MATCHER.match(uri)) { case WIDGETS: return Widget.MIME_TYPE_MULTIPLE; case WIDGET: return Widget.MIME_TYPE_SINGLE; default:

throw new IllegalArgumentException("Unknown URI " + uri); }

}

Our provider extends ContentProvider, which defines the methods we’ll need to implement. We use several database-related constants to define the database name and table we’ll use

B

. After that, we include a UriMatcher, which we’ll use to match types, and a projection Map for field names.

We include a reference to a SQLiteDatabase object; we’ll use this to store and retrieve the data that our provider handles

C

. We create, open, or upgrade the data- base using a SQLiteOpenHelper in an inner class

D

. We’ve used this helper pattern before, when we worked directly with the database in listing 5.10. In the onCreate method, the open helper sets up the database

E

.

After our setup-related steps, we come to the first method ContentProvider requires us to implement, getType

F

. The provider uses this method to resolve each passed-in Uri to determine whether it’s supported. If it is, the method checks which type of data the current call is requesting. The data might be a single item or the entire set.

Next, we need to cover the remaining required methods to satisfy the Content- Provider contract. These methods, shown in the following listing, correspond to the CRUD-related activities: query, insert, update, and delete.

@Override

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

String sortOrder) {

SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); String orderBy = null;

switch (WidgetProvider.URI_MATCHER.match(uri)) {

Listing 5.14 The second portion of the WidgetProviderContentProvider

Override onCreate

E

Implement getType method

F

B

Set up query based on URI

C

155

Working with ContentProvider classes

case WIDGETS: queryBuilder.setTables(WidgetProvider.DB_TABLE); queryBuilder.setProjectionMap(WidgetProvider.PROJECTION_MAP); break; case WIDGET: queryBuilder.setTables(WidgetProvider.DB_TABLE); queryBuilder.appendWhere("_id=" + uri.getPathSegments().get(1)); break; default:

throw new IllegalArgumentException("Unknown URI " + uri); } if (TextUtils.isEmpty(sortOrder)) { orderBy = Widget.DEFAULT_SORT_ORDER; } else { orderBy = sortOrder; }

Cursor c = queryBuilder.query(db, projection, selection, selectionArgs, null, null, orderBy); c.setNotificationUri( getContext().getContentResolver(), uri); return c; } @Override

public Uri insert(Uri uri, ContentValues initialValues) { long rowId = 0L;

ContentValues values = null; if (initialValues != null) {

values = new ContentValues(initialValues); } else {

values = new ContentValues(); }

if (WidgetProvider.URI_MATCHER.match(uri) != WidgetProvider.WIDGETS) {

throw new IllegalArgumentException("Unknown URI " + uri); }

Long now = System.currentTimeMillis(); . . . omit defaulting of values for brevity

rowId = db.insert(WidgetProvider.DB_TABLE, "widget_hack", values);

if (rowId > 0) {

Uri result = ContentUris.withAppendedId(Widget.CONTENT_URI, rowId);

getContext().getContentResolver(). notifyChange(result, null); return result;

}

throw new SQLException("Failed to insert row into " + uri); }

@Override

public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; switch (WidgetProvider.URI_MATCHER.match(uri)) { Perform query to get Cursor

D

Set notification Uri on Cursor

E

Use ContentValues in insert method

F

Call database insert

G

Get Uri to return

H

Notify listeners data was inserted

I

Provide update method

case WIDGETS:

count = db.update(WidgetProvider.DB_TABLE, values, selection, selectionArgs);

break; case WIDGET:

String segment = uri.getPathSegments().get(1); String where = "";

if (!TextUtils.isEmpty(selection)) { where = " AND (" + selection + ")"; }

count = db.update(WidgetProvider.DB_TABLE, values, "_id=" + segment + where, selectionArgs); break;

default:

throw new IllegalArgumentException("Unknown URI " + uri); }

getContext().getContentResolver().notifyChange(uri, null); return count;

}

@Override

public int delete(

Uri uri, String selection, String[] selectionArgs) { int count;

switch (WidgetProvider.URI_MATCHER.match(uri)) { case WIDGETS:

count = db.delete(WidgetProvider.DB_TABLE, selection, selectionArgs);

break; case WIDGET:

String segment = uri.getPathSegments().get(1); String where = "";

if (!TextUtils.isEmpty(selection)) { where = " AND (" + selection + ")"; }

count = db.delete(WidgetProvider.DB_TABLE, "_id=" + segment + where, selectionArgs); break;

default:

throw new IllegalArgumentException("Unknown URI " + uri); }

getContext().getContentResolver().notifyChange(uri, null); return count;

} }

The last part of our WidgetProvider class shows how to implement the Content- Provider methods. First, we use a SQLQueryBuilder inside the query method to append the projection map passed in

B

and any SQL clauses, along with the correct URI based on our matcher

C

, before we make the actual query and get a handle on a Cursor to return

D

.

At the end of the query method, we use the setNotificationUri method to watch the returned Uri for changes

E

. This event-based mechanism keeps track of when Cursor data items change, regardless of who changes them.

Provide delete method

157

Working with ContentProvider classes

Next, you see the insert method, where we validate the passed-in ContentValues object and populate it with default values, if the values aren’t present

F

. After we have the values, we call the database insert method

G

and get the resulting Uri to return with the appended ID of the new record

H

. After the insert is complete, we use another notification system, this time for ContentResolver. Because we’ve made a data change, we inform the ContentResolver what happened so that any registered listeners can be updated

I

.

After completing the insert method, we come to the update

J

and delete

1)

methods. These methods repeat many of the previous concepts. First, they match the Uri passed in to a single element or the set, and then they call the respective update and delete methods on the database object. Again, at the end of these methods, we notify listeners that the data has changed.

Implementing the needed provider methods completes our class. After we register this provider with the platform, any application can use it to query, insert, update, or delete data. Registration occurs in the application manifest, which we’ll look at next. PROVIDER MANIFESTS

Content providers must be defined in an application manifest file and installed on the platform so the platform can learn that they’re available and what data types they offer. The following listing shows the manifest for our provider.

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.msi.manning.chapter5.widget"> <application android:icon="@drawable/icon" android:label="@string/app_short_name"> <activity android:name=".WidgetExplorer" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name= "android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name="WidgetProvider" android:authorities= "com.msi.manning.chapter5.Widget" /> </application> </manifest>

The <provider> element

B

defines the class that implements the provider and associ- ates a particular authority with that class.

A completed project that supports inserting, retrieving, updating, and deleting records rounds out our exploration of using and building ContentProvider classes. And with that, we’ve also now demonstrated the ways to locally store and retrieve data on the Android platform.

Listing 5.15 WidgetProvider AndroidManifest.xml file

Declare provider’s authority

5.5

Summary

From a simple SharedPreferences mechanism to file storage, databases, and finally the concept of a ContentProvider, Android provides myriad ways for applications to retrieve and store data.

As we discussed in this chapter, several storage types can share data across applica- tion and process boundaries, and several can’t. You can create SharedPreferences with a permissions mode, allowing the flexibility to keep things private, or to share data globally with read-only or read-write permissions. The filesystem provides more flexible and powerful data storage for a single application.

Android also provides a relational database system based on SQLite. Use this light- weight, speedy, and capable system for local data persistence within a single applica- tion. To share data, you can still use a database, but you need to expose an interface through a ContentProvider. Providers expose data types and operations through a URI-based approach.

In this chapter, we examined each of the data paths available to an Android appli- cation. You built several small, focused sample applications to use preferences and the filesystem, and you expanded the WeatherReporter sample application that you began in the last chapter. This Android application uses a SQLite database to access and per- sist data. You also built your own custom content provider from the ground up.

To expand your Android horizons beyond data, we’ll move on to general network- ing in the next chapter. We’ll cover networking basics and the networking APIs Android provides. We’ll also expand on the data concepts we’ve covered in this chap- ter to use the network itself as a data source.

Additional ContentProvider manifest properties

The properties of a ContentProvider can configure several important settings beyond the basics, such as specific permissions, initialization order, multiprocess capability, and more. Though most ContentProvider implementations won’t need to delve into these details, you should still keep them in mind. For complete and up- to-date ContentProvider properties, see the SDK documentation.

159

Networking and

In document Sexismo y violencia de género (página 173-179)