Un ContentProvider es una interfaz que nos brinda Android para acceder a una base de datos que se encuentre en nuestro teléfono. Y hacer que las aplicaciones puedan compartir información fácilmente.

Los pasos a seguir para la implementación son los siguientes:

  1. Extender la clase ContentProvider
  2. Crear la base de datos
  3. Implementar getType
  4. Implementar onCreate
  5. Implementar los metodos CRUD
  6. Registar el ContentProvider en AndroidManifest.xml

Se va a explicar como crear un Content Provider que llevara un registro de los ficheros que abre un usuario.

Extender la clase ContentProvider

Al definir que nuestra clase extiende ContentProvider se nos crear el esqueleto básico de nuestra clase.
Que debería ser algo así:

Content provider
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class AcmeProvider extends ContentProvider {

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}

@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
}

@Override
public boolean onCreate() {
// TODO Auto-generated method stub
return false;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder)
{

// TODO Auto-generated method stub
return null;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs)
{

// TODO Auto-generated method stub
return 0;
}
}

Crear la base de datos

Ya con la estructura creada, tenemos que definir una URI para nuestra base de datos, para que el sistema pueda identificarla. En el futuro cuando llegue el momento que queramos realizar alguna consulta a la base de datos se hará a través del URI que vamos a definir. Se definen en la linea 2 y 3.

Ademas del URI, hay que definir una serie de propiedades, entre paréntesis se indica el numero de linea:

  • Tipos de URI, un ítem especifico o un conjunto de ellos (6 - 11)
  • La base de datos, nombre, versión y nombre de la tabla (14 - 20)
  • Columnas de la tabla y su respectivo indice (23 - 28)
  • Controlador de peticiones (30-36)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//Authority and ContentProvier URI
public static final String AUTHORITY = "com.acme.provider.myapp";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/logs");

//differentiate between the different URI requests
private static final int LOG_LIST = 1;
private static final int LOG_ITEM = 2;

//content types
private static final String CONTENT_TYPE_LIST = "vnd.android.cursor.dir/vnd.myapp.logs";
private static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/vnd.myapp.logs";

//database file and properties
private SQLiteDatabase dbLogs = null;

private static final String DB_NAME= "logs.db";

private static final int DB_VERSION = 1;

private static final String TABLE_NAME = "logs";

//table columns
public static final String KEY_ID ="_id";
public static final String KEY_FILENAME ="filename";

//table index
public static final int INDEX_ID = 0;
public static final int INDEX_FILENAME = 1;

private static final UriMatcher uriMatcher;

static{
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "logs", LOG_LIST);
uriMatcher.addURI(AUTHORITY, "logs/#", LOG_ITEM);
}

Ahora hay que pasar a la creación de la BD, para ello hay que implementar SQLiteOpenHelper como una clase privada. Y como pueden ver en la constante CREATE_TABLE se define la creación de la tabla con SQL usando las columnas definidas anteriormente. Mediante SQLiteOpenHelper el inicio de la base de datos se hace de manera perezosa, es decir hasta que no se necesita no se inicia. Evitando que nuestra aplicación se inicie mas lentamente, y que surjan errores relacionados con la base de datos al iniciar la aplicación.

Database helper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private static class AcmeDatabaseHelper extends SQLiteOpenHelper {

private static final String CREATE_TABLE =
"create table "
+ TABLE_NAME + " ("
+ KEY_ID + " integer primary key autoincrement, "
+ KEY_FILENAME + " TEXT"
+ ");";


public LogDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}

Implementar getType

Este metodo se encarga de analizar la consulta y decidir que tipo de datos se esta consultando. Los dos tipos de datos fueron definidos en CONTENT_TYPE_LIST y CONTENT_TYPE_ITEM, el tipo de datos debe empezar por vnd.android.cursor.dir/ y vnd.android.cursor.item/ para una lista y un item especifico, respectivamente.

1
2
3
4
5
6
7
8
@Override
public String getType(Uri uri) {
switch (uriMatcher.match(uri)) {
case LOG_LIST: return CONTENT_TYPE_LIST;
case LOG_ITEM: return CONTENT_TYPE_ITEM;
default: throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}

Implementar onCreate

Como estamos usando SQLiteOpenHelper, tan solo le indicamos que queremos crear la base de datos y este mismo ya se encarga de hacerlo cuando sea necesario.

onCreate
1
2
3
4
5
6
7
@Override
public boolean onCreate() {
Context context = getContext();
AcmeDatabaseHelper helper = new AcmeDatabaseHelper(context);
dbLogs = helper.getWritableDatabase();
return dbLogs == null ? false : true;
}

Implementar los metodos CRUD

Al definir como realizar las consultas a la base de datos tenemos que ver si se trata de un item especifico o de todos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count;
switch (uriMatcher.match(uri)) {
case LOG_LIST:
count = dbLogs.delete(TABLE_NAME, selection, selectionArgs);
break;

case LOG_ITEM:
String segment = uri.getPathSegments().get(1);
count = dbLogs.delete(TABLE_NAME,
KEY_ID + " = " + segment
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : "")
, selectionArgs);
break;

default: throw new IllegalArgumentException("Unsupported URI: " + uri);
}

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



@Override
public Uri insert(Uri uri, ContentValues values){
long rowID = dbLogs.insert(TABLE_NAME, null, values);

if (rowID > 0)
{
Uri result = ContentUris.withAppendedId(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;
switch (uriMatcher.match(uri)) {
case LOG_LIST:
count = dbLogs.update(TABLE_NAME, values, selection, selectionArgs);
break;

case LOG_ITEM:
String segment = uri.getPathSegments().get(1);
count = dbLogs.update(TABLE_NAME,
values,
KEY_ID + " = " + segment
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ")" : "")
, selectionArgs);
break;

default: throw new IllegalArgumentException("Unsupported URI: " + uri);
}

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

@Override
public Cursor query(Uri uri,
String[] projection,
String selection,
String[] selectionArgs,
String sortOrder)
{


SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(TABLE_NAME);

switch (uriMatcher.match(uri)) {
case LOG_ITEM:
builder.appendWhereEscapeString(KEY_ID + " = " + uri.getPathSegments().get(1));
break;
}

Cursor cursor = builder.query(
dbLogs,
projection,
selection,
selectionArgs,
null, null, sortOrder);

// Register the contexts ContentResolver to be notified if
// the cursor result set changes.
cursor.setNotificationUri(getContext().getContentResolver(), uri);

return cursor;
}

Registar el ContentProvider

Suponiendo que nuestro ContentProvider se encuentra en un paquete llamado provider, tenemos que registrarlo en el Manifest

AndroidManifest.xml
1
2
3
4
5
<provider
android:name=".provider.AcmeProvider"
android:authorities="com.acme.provider.myapp"
android:label="Log"
android:enabled="true"/>