Un proveedor de contenido administra el acceso a un repositorio central de datos. Implementas un
como una o más clases en una aplicación para Android, junto con elementos en
el archivo de manifiesto. Una de tus clases implementa una subclase de
ContentProvider
, que es la interfaz entre tu proveedor y
otras aplicaciones.
Si bien el objetivo de los proveedores de contenido es poner los datos a disposición aplicaciones, puedes tener actividades en tu aplicación que permitan al usuario consultar y modificar los datos administrados por tu proveedor.
En esta página, se incluye el proceso básico para crear un proveedor de contenido y una lista de APIs que se pueden usar.
Antes de que empieces a crear
Antes de que empieces a crear un proveedor, haz lo siguiente:
-
Decide si necesitas un proveedor de contenido. Debes crear contenido
si quieres proporcionar una o más de las siguientes funciones:
- Te recomendamos que ofrezcas datos o archivos complejos para otras aplicaciones.
- Quieres permitir que los usuarios copien datos complejos de tu app a otras apps.
- Te recomendamos que proporciones sugerencias personalizadas usando el marco de trabajo de búsqueda.
- Quieres exponer los datos de tu aplicación a widgets.
- Quieres implementar
AbstractThreadedSyncAdapter
,CursorAdapter
oCursorLoader
.
No necesitas un proveedor para usar bases de datos ni otros tipos de el almacenamiento persistente si el uso es completamente dentro de su propia aplicación y no necesitas ninguna de las funciones anteriores que se mencionan. En cambio, puedes usar uno de los sistemas de almacenamiento descritos en Descripción general del almacenamiento de datos y archivos.
- Si aún no lo hiciste, lee Conceptos básicos sobre los proveedores de contenido para obtener más información sobre los proveedores y su funcionamiento.
A continuación, sigue estos pasos para crear un proveedor:
-
Diseña el almacenamiento sin formato para tus datos. Un proveedor de contenido ofrece datos de dos maneras:
- Datos de archivos
- Los datos que normalmente se presentan en archivos, como fotos, audios o videos. Guarda los archivos en la carpeta privada de tu aplicación espacio. En respuesta a una solicitud de un archivo por parte de otra aplicación, tu el proveedor puede ofrecer un handle para el archivo.
- "Estructurados" datos
- Son datos que normalmente se encuentran en una base de datos, un array o una estructura similar. Almacena los datos en un formato que sea compatible con tablas de filas y columnas. Una fila Representa una entidad, como una persona o un elemento en el inventario. Una columna representa algunos datos de la entidad, como el nombre de la persona o el precio de un artículo. Una forma común de este tipo de datos es en una base de datos SQLite, pero puedes usar cualquier tipo y el almacenamiento persistente. Para obtener más información sobre los tipos de almacenamiento disponibles en el sistema Android, consulta la Diseña el almacenamiento de datos.
-
Define una implementación concreta de la clase
ContentProvider
y los métodos requeridos. Esta clase es la interfaz entre tus datos y el resto de la Sistema Android Para obtener más información sobre esta clase, consulta el Implementa la sección de la clase ContentProvider. - Define la cadena de autoridad, los URI de contenido y los nombres de columnas del proveedor. Si quieres la aplicación del proveedor para manejar intents, además de definir acciones de intents, datos adicionales y marcas. Define también los permisos que necesitas para las aplicaciones que para acceder a tus datos. Considera definir todos estos valores como constantes en un clase Contract independiente. Más adelante, puedes exponer esta clase a otros desarrolladores. Para ver más información sobre URI de contenido, consulta la Sección Diseña URI de contenido. Para obtener más información sobre los intents, consulta el Sección Intents y acceso a datos.
-
Agrega otras piezas opcionales, como datos de muestra o una implementación
de
AbstractThreadedSyncAdapter
que pueden sincronizar datos entre el proveedor y los datos basados en la nube.
Diseña el almacenamiento de datos
Un proveedor de contenido es la interfaz para los datos guardados en un formato estructurado. Antes de crear la interfaz, decide cómo almacenar los datos. Puedes almacenar los datos de cualquier forma que y, luego, diseñar la interfaz para leer y escribir los datos según sea necesario.
Estas son algunas de las tecnologías de almacenamiento de datos disponibles en Android:
- Si trabajas con datos estructurados, considera una base de datos relacional como SQLite o un almacén de datos de clave-valor no relacional, como LevelDB. Si trabajas en con datos no estructurados, como medios de audio, imagen o video, considere almacenar el datos como archivos. Puedes mezclar y exponer diferentes tipos de almacenamiento. utilizando un único proveedor de contenido, si es necesario.
-
El sistema Android puede interactuar con la biblioteca de persistencias Room, que
proporciona acceso a la API de base de datos SQLite que los proveedores de Android
usar para almacenar datos orientados a tablas. Para crear una base de datos usando
biblioteca, crear una instancia de una subclase de
RoomDatabase
, como se describe en Cómo guardar contenido en una base de datos local con Room.No necesitas usar una base de datos para implementar tu repositorio. Un proveedor aparece externamente como un conjunto de tablas, similar a una base de datos relacional, pero es Esto no es un requisito para la implementación interna del proveedor.
- Para guardar datos de archivo, Android tiene una variedad de API orientadas a archivos. Para obtener más información sobre el almacenamiento de archivos, lee el Descripción general del almacenamiento de datos y archivos. Si estás diseñas un proveedor que ofrece datos relacionados con medios de comunicación, como música o videos, puedes tener un proveedor que combina archivos y datos de tablas.
- En casos excepcionales, podrías beneficiarte de implementar más de un proveedor de contenido para en una sola aplicación. Por ejemplo, tal vez quieras compartir algunos datos con un widget usando un proveedor de contenido y exponer un conjunto diferente de datos para compartir con otros aplicaciones.
-
Para trabajar con datos basados en la red, usa clases en
java.net
yandroid.net
También puedes sincronizar datos basados en la red con datos locales como una base de datos y, luego, ofrecer los datos como tablas o archivos.
Nota: Si realizas un cambio en tu repositorio que no retrocompatible, debes marcar el repositorio con una versión nueva de la fila. También debes aumentar el número de versión de tu app implementa el proveedor de contenido nuevo. Este cambio impide que el sistema regresa a una versión anterior para evitar que falle el sistema cuando intente reinstalar un que tiene un proveedor de contenido incompatible.
Consideraciones del diseño de datos
Aquí hay algunas sugerencias para diseñar la estructura de datos de tu proveedor:
-
Los datos de tabla siempre deben tener una "clave primaria" que el proveedor mantiene
como un valor numérico único para cada fila. Puedes usar este valor para vincular la fila a elementos relacionados
filas en otras tablas (lo que se usa como “clave externa”). Si bien puedes usar cualquier nombre
para esta columna, usar
BaseColumns._ID
es la mejor opción, ya que vincular los resultados de una consulta al proveedor con unaListView
requiere que una de las columnas recuperadas tenga el nombre_ID
-
Si quieres proporcionar imágenes de mapas de bits u otros datos muy grandes orientados a archivos, almacena
los datos en un archivo y luego proporcionarlos indirectamente, en lugar de almacenarlos directamente en un
desde una tabla de particiones. Si lo hace, debe indicarles a los usuarios de su proveedor que deben utilizar un
ContentResolver
para acceder a los datos. -
Usa el tipo de datos de objeto binario grande (BLOB) para almacenar datos que varían en tamaño o tienen una
estructura variable. Por ejemplo, puedes usar una columna BLOB para almacenar un
búfer de protocolo o
Estructura JSON:
También puedes usar un BLOB para implementar una tabla independiente del esquema. En este tipo de tabla, se define una columna de clave primaria, una columna de tipo de MIME, y una o y más genéricas como BLOB. El significado de los datos en las columnas BLOB se indica por el valor de la columna de tipo de MIME. Esto te permite almacenar diferentes tipos de filas la misma tabla. Los “datos” del proveedor de contactos tabla
ContactsContract.Data
es un ejemplo de un esquema independiente desde una tabla de particiones.
Diseña URI de contenido
Un URI de contenido es un URI que identifica datos de un proveedor. Los URI de contenido incluyen
el nombre simbólico de todo el proveedor (su autoridad) y una
nombre que apunta a una tabla o archivo (una ruta). La parte de ID opcional apunta a
una fila individual de una tabla. Cada método de acceso a los datos de
ContentProvider
tiene un URI de contenido como argumento. Esto te permite
determinar la tabla, la fila o el archivo al que se accederá.
Para obtener más información sobre los URI de contenido, consulta Conceptos básicos sobre el proveedor de contenido
Diseña una autoridad
Un proveedor generalmente tiene una sola autoridad, que sirve como su nombre interno en Android. Para Evitar conflictos con otros proveedores, usar la propiedad del dominio de Internet (a la inversa) como base de la autoridad de tu proveedor. Porque esta recomendación también se aplica para Android nombres de paquetes, puedes definir la autoridad de tu proveedor como una extensión del nombre del paquete que contiene el proveedor.
Por ejemplo, si el nombre de tu paquete de Android es
com.example.<appname>
, proporciona a tu proveedor
autoridad com.example.<appname>.provider
.
Diseña la estructura de una ruta de acceso.
Los desarrolladores suelen crear URI de contenido a partir de la autoridad agregando rutas de acceso que apuntan a
tablas individuales. Por ejemplo, si tienes dos tablas, table1 y
table2, puedes combinarlas con la autoridad del ejemplo anterior para obtener la
URI de contenido
com.example.<appname>.provider/table1
y
com.example.<appname>.provider/table2
Las rutas no se
se limita a un solo segmento, y no es necesario que haya una tabla para cada nivel de la ruta.
Cómo controlar los IDs de URI de contenido
Por convención, los proveedores ofrecen acceso a una sola fila de una tabla mediante la aceptación de un URI de contenido
con un valor de ID para la fila al final del URI. Además, por convención, los proveedores hacen coincidir
ID de salida a la columna _ID
de la tabla y realizar el acceso solicitado en el
que coincida.
Esta convención facilita un patrón de diseño común para aplicaciones que acceden a un proveedor. La app
realiza una consulta al proveedor y muestra el Cursor
resultante
en un ListView
con un CursorAdapter
.
La definición de CursorAdapter
requiere una de las columnas en el
Cursor
, _ID
Luego, el usuario elige una de las filas mostradas de la IU para observar o modificar la
de datos no estructurados. La app obtiene la fila correspondiente del Cursor
que respalda la
ListView
, obtiene el valor _ID
para esta fila y lo agrega a
el URI de contenido y envía la solicitud de acceso al proveedor. Luego, el proveedor puede hacer
una consulta o una modificación con respecto
a la fila exacta que eligió el usuario.
Patrones de URI de contenido
Para ayudarte a elegir qué acción realizar para un URI de contenido entrante, la API del proveedor incluye lo siguiente:
la clase de conveniencia UriMatcher
, que asigna patrones de URI de contenido a
de números enteros. Puedes usar los valores de número entero en una sentencia switch
que
selecciona la acción deseada para los URI de contenido que coincidan con un patrón en particular.
Un patrón de URI de contenido compara URI de contenido usando caracteres comodín:
-
*
coincide con una cadena de caracteres válidos de cualquier longitud. -
#
coincide con una cadena de caracteres numéricos de cualquier longitud.
Como ejemplo de diseño y programación de la manipulación de URI de contenido, considera un proveedor con el
autoridad com.example.app.provider
que reconoce los siguientes URI de contenido
que apunta a las tablas:
-
content://com.example.app.provider/table1
: Es una tabla llamadatable1
. -
content://com.example.app.provider/table2/dataset1
: una tabla llamadadataset1
-
content://com.example.app.provider/table2/dataset2
: una tabla llamadadataset2
-
content://com.example.app.provider/table3
: Es una tabla llamadatable3
.
El proveedor también reconoce estos URI de contenido si tienen un ID de fila anexado, como content://com.example.app.provider/table3/1
para la fila identificada por
1
en table3
.
Los siguientes patrones de URI de contenido son posibles:
-
content://com.example.app.provider/*
- Coincide con cualquier URI de contenido en el proveedor.
-
content://com.example.app.provider/table2/*
-
Coincide con un URI de contenido para las tablas
dataset1
ydataset2
, pero no coincide con los URI de contenido paratable1
nitable3
-
content://com.example.app.provider/table3/#
-
Coincide con un URI de contenido
para filas individuales en
table3
, comocontent://com.example.app.provider/table3/6
para la fila identificada por6
En el siguiente fragmento de código, se muestra cómo funcionan los métodos de UriMatcher
.
Este código maneja los URIs de una tabla completa de manera diferente a los URI de un
una sola fila mediante el patrón de URI de contenido
content://<authority>/<path>
para tablas y
content://<authority>/<path>/<id>
para las filas individuales.
El método addURI()
asigna un
autoridad y ruta de acceso a un valor de número entero. El método match()
muestra el valor entero de un URI. Una sentencia switch
decide entre consultar toda la tabla o consultar un solo registro.
Kotlin
private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used * in the path. */ addURI("com.example.app.provider", "table3", 1) /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ addURI("com.example.app.provider", "table3/#", 2) } ... class ExampleProvider : ContentProvider() { ... // Implements ContentProvider.query() override fun query( uri: Uri?, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor? { var localSortOrder: String = sortOrder ?: "" var localSelection: String = selection ?: "" when (sUriMatcher.match(uri)) { 1 -> { // If the incoming URI was for all of table3 if (localSortOrder.isEmpty()) { localSortOrder = "_ID ASC" } } 2 -> { // If the incoming URI was for a single row /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ localSelection += "_ID ${uri?.lastPathSegment}" } else -> { // If the URI isn't recognized, // do some error handling here } } // Call the code to actually do the query } }
Java
public class ExampleProvider extends ContentProvider { ... // Creates a UriMatcher object. private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { /* * The calls to addURI() go here for all the content URI patterns that the provider * recognizes. For this snippet, only the calls for table 3 are shown. */ /* * Sets the integer value for multiple rows in table 3 to one. No wildcard is used * in the path. */ uriMatcher.addURI("com.example.app.provider", "table3", 1); /* * Sets the code for a single row to 2. In this case, the # wildcard is * used. content://com.example.app.provider/table3/3 matches, but * content://com.example.app.provider/table3 doesn't. */ uriMatcher.addURI("com.example.app.provider", "table3/#", 2); } ... // Implements ContentProvider.query() public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... /* * Choose the table to query and a sort order based on the code returned for the incoming * URI. Here, too, only the statements for table 3 are shown. */ switch (uriMatcher.match(uri)) { // If the incoming URI was for all of table3 case 1: if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC"; break; // If the incoming URI was for a single row case 2: /* * Because this URI was for a single row, the _ID value part is * present. Get the last path segment from the URI; this is the _ID value. * Then, append the value to the WHERE clause for the query. */ selection = selection + "_ID = " + uri.getLastPathSegment(); break; default: ... // If the URI isn't recognized, do some error handling here } // Call the code to actually do the query }
Otra clase, ContentUris
, proporciona métodos prácticos para trabajar
con la parte id
de los URI de contenido Las clases Uri
y
Los Uri.Builder
incluyen métodos de conveniencia para analizar los existentes.
Uri
y crear nuevos.
Implementa la clase ContentProvider
La instancia ContentProvider
administra el acceso
a un conjunto estructurado de datos
mediante el manejo de solicitudes de otras aplicaciones. Todas las formas
de acceso finalmente llama a ContentResolver
, que luego llama a un elemento
método de ContentProvider
para obtener acceso.
Métodos obligatorios
La clase abstracta ContentProvider
define seis métodos abstractos que
que implementas como parte de tu subclase concreta. Todos estos métodos, excepto
Una aplicación cliente llama a onCreate()
que intenta acceder a tu proveedor de contenido.
-
query()
-
Recupera datos de tu proveedor. Usa los argumentos para seleccionar la tabla que quieres
la consulta, las filas y columnas que se devolverán, y el orden de clasificación del resultado.
Muestra los datos como un objeto
Cursor
. -
insert()
- Inserta una fila nueva en tu proveedor. Usa los argumentos para seleccionar en una tabla de destino y obtener los valores de columna que se usarán. Devuelve un URI de contenido para el fila recién insertada.
-
update()
- Actualiza las filas existentes en tu proveedor. Usa los argumentos para seleccionar la tabla y las filas para actualizar y obtener los valores de columna actualizados. Muestra el número de filas actualizadas.
-
delete()
- Borra filas de tu proveedor. Usa los argumentos para seleccionar la tabla y las filas a borrar. Muestra el número de filas borradas.
-
getType()
- Devuelve el tipo de MIME correspondiente a un URI de contenido. Este método se describe en más detalle consulta la sección Cómo implementar los tipos de MIME del proveedor de contenido.
-
onCreate()
-
Inicializa tu proveedor. El sistema Android llama a este método inmediatamente después de
crea tu proveedor. Tu proveedor no se crea hasta que se
ContentResolver
intenta acceder a ella.
Estos métodos tienen la misma firma que los métodos idénticos
ContentResolver
.
La implementación de estos métodos debe tener en cuenta lo siguiente:
-
Todos estos métodos, excepto
onCreate()
pueden llamarlos varios subprocesos a la vez, por lo que deben ser seguros para estos. Para aprender más información sobre varios subprocesos, consulta la Descripción general de los procesos y subprocesos. -
Evita realizar operaciones extensas en
onCreate()
. Aplaza tareas de inicialización hasta que sean necesarias. La sección sobre cómo implementar el método onCreate() lo analiza con más detalle. -
Si bien debes implementar estos métodos, tu código no necesita hacer nada,
devuelva el tipo de datos esperado. Por ejemplo, puedes evitar que otras aplicaciones
de insertar datos en algunas tablas ignorando la llamada a
insert()
y regreso 0.
Implementa el método query()
El
El método ContentProvider.query()
debe mostrar un objeto Cursor
o, si
falla, se arroja una Exception
. Si estás usando una base de datos SQLite como tus datos
almacenamiento, puedes mostrar el Cursor
devuelto por uno de los
Métodos query()
de la clase SQLiteDatabase
Si la consulta no coincide con ninguna fila, muestra un Cursor
.
instancia cuyo método getCount()
muestra 0.
Muestra null
solo si se produjo un error interno durante el proceso de consulta.
Si no estás usando una base de datos SQLite como almacenamiento de datos, usa una de las subclases concretas.
de Cursor
. Por ejemplo, la clase MatrixCursor
implementa un cursor en el que cada fila es un array de instancias de Object
. Con esta clase,
Usa addRow()
para agregar una fila nueva.
El sistema Android debe poder comunicar el Exception
.
más allá de los límites del proceso. Android puede hacer esto para las siguientes excepciones que resultan útiles
a la hora de manejar errores en las consultas:
-
IllegalArgumentException
. Puedes decidir arrojar esto si tu proveedor recibe un URI de contenido no válido. -
NullPointerException
Implementa el método insert()
El método insert()
agrega un
nueva fila a la tabla adecuada con los valores de ContentValues
argumento. Si el nombre de una columna no está en el argumento ContentValues
,
podrías querer proporcionar un valor predeterminado, ya sea en tu código de proveedor o en tu base de datos
.
Este método muestra el URI de contenido para la nueva fila. Para construir esto, agrega el nuevo
la clave primaria de la fila, por lo general, el valor _ID
, al URI de contenido de la tabla mediante
withAppendedId()
Implementa el método delete()
El método delete()
no tiene que borrar filas de tu almacenamiento de datos. Si usas un adaptador de sincronización
con tu proveedor, considera marcar una fila borrada
con "delete" marca en lugar de quitar la fila por completo. El adaptador de sincronización puede
verificar si hay filas borradas y quitarlas del servidor antes de borrarlas del proveedor
Cómo implementar el método update()
El método update()
toma el mismo argumento ContentValues
que usa
insert()
y las
los mismos argumentos selection
y selectionArgs
que usa
delete()
y
ContentProvider.query()
Esto podría permitirte reutilizar el código entre estos métodos.
Cómo implementar el método onCreate()
El sistema Android llama a onCreate()
.
cuando inicia el proveedor. Realiza solo la inicialización de ejecución rápida
tareas en este método y aplazar la creación de la base de datos y la carga de datos hasta que el proveedor
recibe una solicitud de los datos. Si realizas tareas largas en
onCreate()
, ralentizas el
inicio del proveedor. A su vez, esto ralentiza la respuesta del proveedor a otros
aplicaciones.
Los dos fragmentos siguientes demuestran la interacción entre
ContentProvider.onCreate()
y
Room.databaseBuilder()
La primera
Este fragmento muestra la implementación de
ContentProvider.onCreate()
donde el
se compila el objeto de base de datos y se crean los controladores de los objetos de acceso a datos:
Kotlin
// Defines the database name private const val DBNAME = "mydb" ... class ExampleProvider : ContentProvider() { // Defines a handle to the Room database private lateinit var appDatabase: AppDatabase // Defines a Data Access Object to perform the database operations private var userDao: UserDao? = null override fun onCreate(): Boolean { // Creates a new database object appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build() // Gets a Data Access Object to perform the database operations userDao = appDatabase.userDao return true } ... // Implements the provider's insert method override fun insert(uri: Uri, values: ContentValues?): Uri? { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Java
public class ExampleProvider extends ContentProvider // Defines a handle to the Room database private AppDatabase appDatabase; // Defines a Data Access Object to perform the database operations private UserDao userDao; // Defines the database name private static final String DBNAME = "mydb"; public boolean onCreate() { // Creates a new database object appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build(); // Gets a Data Access Object to perform the database operations userDao = appDatabase.getUserDao(); return true; } ... // Implements the provider's insert method public Cursor insert(Uri uri, ContentValues values) { // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc. } }
Implementa tipos de MIME ContentProvider
La clase ContentProvider
tiene dos métodos para mostrar tipos de MIME:
-
getType()
- Uno de los métodos obligatorios que implementas para cualquier proveedor.
-
getStreamTypes()
- Es un método que se espera que implementes si tu proveedor ofrece archivos.
Tipos de MIME para tablas
El método getType()
devuelve un
String
en formato MIME que describe el tipo de datos que muestra el contenido
argumento de URI. El argumento Uri
puede ser un patrón en lugar de un URI específico.
En este caso, muestra el tipo de datos asociados con los URI de contenido que coincidan con el
.
Para los tipos de datos comunes, como texto, HTML o JPEG,
getType()
devuelve el valor estándar
Es el tipo de MIME de esos datos. Puedes encontrar una lista completa de estos tipos estándar en la
Tipos de medios MIME de IANA
sitio web.
Para los URI de contenido que apuntan a una o más filas de datos de tabla,
Devoluciones por getType()
Un tipo de MIME en formato MIME específico del proveedor de Android:
-
Parte tipográfica:
vnd
-
Parte de subtipo:
-
Si el patrón del URI es para una sola fila:
android.cursor.item/
-
Si el patrón del URI es para más de una fila:
android.cursor.dir/
-
Si el patrón del URI es para una sola fila:
-
Parte específica del proveedor:
vnd.<name>
.<type>
Proporcionas el
<name>
y la<type>
. El valor<name>
es único a nivel global, y el valor<type>
es único para el URI correspondiente . Una buena opción para<name>
es el nombre de tu empresa o alguna parte del nombre del paquete de Android de tu aplicación. Una buena opción para el<type>
es una cadena que identifica la tabla asociada con el URI
Por ejemplo, si la autoridad de un proveedor es
com.example.app.provider
y expone una tabla llamada
table1
, el tipo de MIME para varias filas en table1
es el siguiente:
vnd.android.cursor.dir/vnd.com.example.provider.table1
Para una sola fila de table1
, el tipo de MIME es el siguiente:
vnd.android.cursor.item/vnd.com.example.provider.table1
Tipos de MIME para archivos
Si tu proveedor ofrece archivos, implementa
getStreamTypes()
El método devuelve un array String
de tipos de MIME para los archivos de tu proveedor.
para un URI de contenido determinado. Filtra los tipos de MIME que ofreces por tipo de MIME
filter, de modo que devuelvas solo los tipos de MIME que el cliente quiere manejar.
Por ejemplo, considera un proveedor que ofrece imágenes en formato JPG,
PNG y GIF.
Si una aplicación llama a ContentResolver.getStreamTypes()
con la cadena de filtro image/*
, para algo que
es una "imagen",
Luego, el método ContentProvider.getStreamTypes()
muestra el array:
{ "image/jpeg", "image/png", "image/gif"}
Si a la app solo le interesan los archivos JPG, puede llamar
ContentResolver.getStreamTypes()
por la cadena de filtro *\/jpeg
.
getStreamTypes()
muestra lo siguiente:
{"image/jpeg"}
Si tu proveedor no ofrece ninguno de los tipos de MIME solicitados en la cadena de filtro,
getStreamTypes()
muestra null
.
Cómo implementar una clase Contract
Una clase Contract es una clase public final
que contiene definiciones de constantes para el
URI, nombres de columnas, tipos de MIME y otros metadatos que pertenecen al proveedor. La clase
establece un contrato entre el proveedor y otras aplicaciones garantizando que el proveedor
se pueda acceder correctamente incluso si hay cambios en los valores reales de los URI, los nombres de las columnas
etcétera.
Una clase Contract también ayuda a los desarrolladores porque suele tener nombres mnemotécnicos para sus constantes. por lo que es menos probable que los desarrolladores usen valores incorrectos para los nombres de las columnas o los URI. Ya que es un esta clase puede contener documentación Javadoc. Entornos de desarrollo integrados, como Android Studio puede autocompletar nombres de constantes desde la clase Contract y mostrar Javadoc para las constantes.
Los desarrolladores no pueden acceder al archivo de clase de la clase Contract desde tu aplicación, pero pueden compilarla estáticamente en su aplicación desde un archivo JAR que tú proporciones.
La clase ContactsContract
y sus clases anidadas son ejemplos de lo siguiente:
Contract.
Cómo implementar permisos del proveedor de contenido
Los permisos y el acceso para todos los aspectos del sistema Android se describen en detalle en Sugerencias de seguridad. La descripción general del almacenamiento de datos y archivos también describe la seguridad y los permisos vigentes para varios tipos de almacenamiento. En resumen, los puntos importantes son los siguientes:
- De forma predeterminada, los archivos de datos guardados en el almacenamiento interno del dispositivo son privados para tu y el proveedor de servicios en la nube.
-
SQLiteDatabase
bases de datos que creas son privadas para tu y el proveedor de servicios en la nube. - De forma predeterminada, los archivos de datos que guardas en el almacenamiento externo son públicos y legible para todo el mundo. No puedes usar un proveedor de contenido para restringir el acceso a archivos en externo, ya que otras aplicaciones pueden usar otras llamadas a la API para leerlos y escribirlos.
- El método llama a abrir o crear archivos o bases de datos SQLite en la memoria interna de tu dispositivo. puede otorgar acceso de lectura y escritura a todas las demás aplicaciones. Si usar un archivo o una base de datos internos como repositorio de tu proveedor y le das “legible para todo el mundo” o que se puedan escribir en todo el mundo los permisos que estableces para tu proveedor en el manifiesto no protegen tus datos. El acceso predeterminado para archivos y bases de datos en el almacenamiento interno es "privado"; no cambies esto para el repositorio de tu proveedor.
Si quieres usar los permisos del proveedor de contenido para controlar el acceso a tus datos, entonces almacenar tus datos en archivos internos, bases de datos SQLite o en la nube, como en un servidor remoto, y mantener la privacidad de los archivos y las bases de datos.
Implementa permisos
Por defecto, todas las aplicaciones pueden leer de tu proveedor o escribir en él, incluso si los datos subyacentes se
privada, ya que, de forma predeterminada, tu proveedor no tiene permisos establecidos. Para cambiar esto,
establecer permisos para tu proveedor en tu archivo de manifiesto usando atributos o
del elemento
<provider>
. Puedes establecer permisos
que se apliquen a todo el proveedor
a ciertas tablas, a ciertos registros o a los tres.
Defines permisos para tu proveedor con uno o más
<permission>
en tu archivo de manifiesto. Para que el
permiso exclusivo de tu proveedor, usa el alcance de estilo Java para el
atributo
android:name
. Por ejemplo, asigna un nombre al permiso de lectura
com.example.app.provider.permission.READ_PROVIDER
La siguiente lista describe el alcance de los permisos del proveedor, empezando por el permisos que se aplican a todo el proveedor y que luego se vuelven más detallados. Los permisos más detallados tienen prioridad sobre los de mayor alcance.
- Permiso individual de lectura y escritura a nivel del proveedor
-
Un permiso que controla el acceso de lectura y escritura a todo el proveedor, especificado
con el atributo
android:permission
de la elemento<provider>
. - Separar permisos de lectura y escritura a nivel del proveedor
-
Un permiso de lectura y un permiso de escritura para todo el proveedor. Tú los especificas
con el
android:readPermission
y Atributosandroid:writePermission
del elemento<provider>
. Tienen prioridad sobre el permiso requerido porandroid:permission
- Permiso a nivel de ruta de acceso
-
Permiso de lectura, escritura o lectura/escritura para un URI de contenido en tu proveedor. Tú especificas
cada URI que quieres controlar con una
Elemento secundario
<path-permission>
del elemento<provider>
. Para cada URI de contenido que especifiques, puedes especificar una un permiso de lectura/escritura, un permiso de lectura, uno de escritura o los tres. Las funciones de lectura y los permisos de escritura tienen prioridad sobre el permiso de lectura y escritura. Además, el nivel de ruta el permiso tiene prioridad sobre los permisos a nivel del proveedor. - Permiso temporal
-
Un nivel de permiso que otorga acceso temporal a una aplicación, incluso si la aplicación
no cuenta con los permisos que se requieren normalmente. La herramienta
de acceso reduce la cantidad de permisos que una aplicación debe solicitar en
su manifiesto. Cuando activas los permisos temporales, las únicas aplicaciones que necesitan
los permisos permanentes de tu proveedor
son aquellos que acceden de forma continua
tus datos.
Por ejemplo, considera los permisos que necesitas si implementas un proveedor de correo electrónico quieres permitir que una aplicación externa de visualización de imágenes muestre archivos adjuntos de fotos de tu proveedor. Para otorgarle al visor de imágenes el acceso necesario sin solicitar permisos, puedes establecer permisos temporales para los URI de contenido para las fotos.
Diseña tu aplicación de correo electrónico para que, cuando el usuario quiera mostrar una foto, la app envíe un intent con el el URI de contenido de una foto y las marcas de permiso al visor de imágenes. El visor de imágenes puede luego consulta a tu proveedor de correo electrónico para recuperar la foto aunque el usuario no haya tener el permiso de lectura normal para tu proveedor.
Para activar los permisos temporales, configura Atributo
android:grantUriPermissions
del<provider>
elemento o agrega uno o más<grant-uri-permission>
elementos secundarios en tu<provider>
. LlamadaContext.revokeUriPermission()
cada vez que quites la compatibilidad con un URI de contenido asociado con un permiso temporal de tu proveedor.El valor del atributo determina la porción del proveedor a la que se puede acceder. Si el atributo se establece en
"true"
, el sistema otorga restricciones permiso a todo el proveedor para anular los demás permisos necesarios según tus permisos a nivel de proveedor o a nivel de ruta de acceso.Si esta marca está configurada en
"false"
, agrega<grant-uri-permission>
elementos secundarios en tu elemento<provider>
. Cada elemento secundario especifica el URI de contenido o Los URI para los que se otorga acceso temporal.Para delegar el acceso temporal a una aplicación, un intent debe contener la marca
FLAG_GRANT_READ_URI_PERMISSION
, elFLAG_GRANT_WRITE_URI_PERMISSION
o ambas. Estos se establecen con el métodosetFlags()
.Si el atributo
android:grantUriPermissions
no está presente, se supone que es"false"
El <provider> elemento
Al igual que los componentes Activity
y Service
,
una subclase de ContentProvider
se define en el archivo de manifiesto de su aplicación con el
<provider>
. El sistema Android obtiene la siguiente información de
el elemento:
-
Autoridad
(
android:authorities
) - Nombres simbólicos que identifican a todo el proveedor dentro del sistema. Esta se describe con más detalle en el Sección Diseña URI de contenido.
-
Nombre de clase del proveedor
(
android:name
) -
Es la clase que implementa
ContentProvider
. Esta clase es se describe con más detalle en el Implementa la sección de la clase ContentProvider. - Permisos
-
atributos que especifican los permisos que otras aplicaciones deben tener para acceder
datos del proveedor:
-
android:grantUriPermissions
: Marca de permiso temporal. -
android:permission
: Permiso de lectura/escritura único en todo el proveedor -
android:readPermission
: Es el permiso de lectura en todo el proveedor. -
android:writePermission
: Es el permiso de escritura en todo el proveedor.
Los permisos y sus atributos correspondientes se describen en más detalle en detalle en la Sección Implementa los permisos del proveedor de contenido.
-
- Atributos de inicio y control
-
Estos atributos determinan cómo y cuándo el sistema Android inicia el proveedor, el
características del proceso del proveedor y otras configuraciones del entorno de ejecución:
-
android:enabled
: Es una marca que permite que el sistema inicie el proveedor. -
android:exported
: Marca que permite que otras aplicaciones usen este proveedor -
android:initOrder
: Es el orden en el que se inicia este proveedor. en relación con otros proveedores en el mismo proceso -
android:multiProcess
: Es una marca que permite que el sistema inicie el proveedor. en el mismo proceso que el cliente que realiza la llamada -
android:process
: Es el nombre del proceso en el que se ejecuta el proveedor. -
android:syncable
: marca que indica que los datos del proveedor se sincronizada con datos en un servidor
Estos atributos están completamente documentados en la guía del
<provider>
. -
- Atributos informativos
-
Un ícono y una etiqueta opcionales para el proveedor:
-
android:icon
: Es un recurso de elementos de diseño que contiene un ícono para el proveedor. El ícono aparece junto a la etiqueta del proveedor en la lista de apps de Configuración > Apps > Todas. -
android:label
: Es una etiqueta informativa que describe el proveedor, su datos o ambos. La etiqueta aparecerá en la lista de apps de Configuración > Apps > Todas.
Estos atributos están completamente documentados en la guía del
<provider>
. -
Nota: Si tu app se orienta a Android 11 o versiones posteriores, consulta la documentación de visibilidad de paquetes para las necesidades de configuración adicionales.
Intents y acceso a datos
Las aplicaciones pueden acceder a un proveedor de contenido de forma indirecta con un Intent
.
La aplicación no llama a ninguno de los métodos de ContentResolver
.
ContentProvider
En su lugar, envía un intent que inicia una actividad,
que a menudo forma parte
de la aplicación del proveedor. La actividad de destino está a cargo de
recuperar y mostrar los datos en su IU.
Según la acción en el intent, el la actividad de destino también puede indicarle al usuario que realice modificaciones en los datos del proveedor. Un intent también puede contener "extras" datos que muestra la actividad de destino en la IU. El usuario tiene la opción de cambiar estos datos antes de usarlos para modificar la datos en el proveedor.
Puedes usar el acceso mediante intents para mejorar la integridad de los datos. Es posible que tu proveedor dependa para insertar, actualizar y borrar datos según una lógica empresarial bien definida. Si en este caso, permitir que otras aplicaciones modifiquen directamente tus datos puede generar datos no válidos.
Si quieres que los desarrolladores usen acceso mediante intents, asegúrate de documentarlo exhaustivamente. Explicar por qué el acceso mediante intents con la IU de tu aplicación es mejor que intentar acceder modificar los datos con su código.
Administrar un intent entrante que quiere modificar los datos de tu proveedor no es diferente de manejar otros intents. Para obtener más información sobre el uso de intents, lee Intents y filtros de intents.
Para obtener más información relacionada, consulta el Descripción general del proveedor de calendario