Créer un fournisseur de contenu

Un fournisseur de contenu gère l'accès à un référentiel central de données. Vous implémentez un comme une ou plusieurs classes dans une application Android, ainsi que les éléments dans le fichier manifeste. L'une de vos classes implémente une sous-classe de ContentProvider, qui est l'interface entre votre fournisseur et d'autres applications.

Bien que les fournisseurs de contenu soient conçus pour mettre des données à la disposition d'autres des activités qui permettent à l'utilisateur interroger et modifier les données gérées par votre fournisseur.

Cette page décrit les étapes de base à suivre pour créer un fournisseur de contenu et une liste d'API disponibles.

Avant de commencer à créer

Avant de commencer à créer un fournisseur, tenez compte des points suivants:

  • Déterminez si vous avez besoin d'un fournisseur de contenu. Vous devez créer un contenu si vous souhaitez fournir une ou plusieurs des fonctionnalités suivantes: <ph type="x-smartling-placeholder">
      </ph>
    • Vous souhaitez proposer des données ou des fichiers complexes à d'autres applications.
    • Vous souhaitez autoriser les utilisateurs à copier des données complexes de votre application vers d'autres.
    • Vous souhaitez fournir des suggestions de recherche personnalisées à l'aide du framework de recherche.
    • Vous souhaitez exposer les données de votre application à des widgets.
    • Vous souhaitez implémenter AbstractThreadedSyncAdapter, CursorAdapter ou CursorLoader classes.

    Vous n'avez pas besoin d'un fournisseur pour utiliser des bases de données ou d'autres types stockage persistant si l'utilisation relève entièrement de votre propre application et vous n'avez besoin d'aucune des fonctionnalités mentionnées ci-dessus. À la place, vous pouvez utilisez l'un des systèmes de stockage décrits Présentation du stockage des données et des fichiers

  • Si vous ne l'avez pas déjà fait, consultez <ph type="x-smartling-placeholder"></ph> Principes de base des fournisseurs de contenu pour en savoir plus sur les fournisseurs et leur fonctionnement

Ensuite, procédez comme suit pour créer votre fournisseur:

  1. Concevez le stockage brut pour vos données. Un fournisseur de contenu propose des données de deux manières: <ph type="x-smartling-placeholder">
    </ph>
    Données de fichiers
    Les données qui sont normalement placées dans des fichiers, telles que des photos, de l'audio ou des vidéos. Stockez les fichiers dans l'espace de noms l'espace de stockage. En réponse à une demande de fichier provenant d'une autre application, votre peut proposer un handle vers le fichier.
    "Structuré" données
    Données qui sont normalement placées dans une base de données, un tableau ou une structure similaire. Stockez les données dans un format compatible avec les tables de lignes et de colonnes. Une ligne représente une entité, telle qu'une personne ou un article de l'inventaire. Une colonne représente certaines données pour l'entité, comme le nom d'une personne ou le prix d'un article. Un moyen courant de stocker ce type de données dans une base de données SQLite, mais vous pouvez utiliser n'importe quel type stockage persistant. Pour en savoir plus sur les types de stockage disponibles dans le système Android, consultez les section "Concevoir le stockage des données".
  2. Définissez une implémentation concrète de la classe ContentProvider. les méthodes requises. Cette classe constitue l'interface entre vos données et le reste des Système Android. Pour plus d'informations sur ce cours, consultez la Implémentez la classe ContentProvider.
  3. Définit la chaîne d'autorité du fournisseur, les URI de contenu et les noms de colonnes. Si vous voulez à l'application du fournisseur pour gérer les intents, définir des actions d'intent, des données supplémentaires, et les indicateurs. Définissez également les autorisations requises pour les applications pour accéder à vos données. Pensez à définir toutes ces valeurs comme des constantes dans une une classe de contrat distincte. Vous pourrez ensuite exposer cette classe à d'autres développeurs. Pour plus sur les URI de contenu, consultez la Concevoir des URI de contenu Pour en savoir plus sur les intents, consultez Intents et accès aux données.
  4. Ajouter d'autres éléments facultatifs, tels que des exemples de données ou une implémentation de AbstractThreadedSyncAdapter, qui peut synchroniser les données entre le fournisseur et les données dans le cloud.

Concevoir le stockage de données

Un fournisseur de contenu est l'interface permettant d'accéder aux données enregistrées dans un format structuré. Avant de créer l'interface, décidez comment stocker les données. Vous pouvez stocker les données sous la forme puis de concevoir l'interface pour lire et écrire les données si nécessaire.

Voici quelques-unes des technologies de stockage de données disponibles sur Android:

  • Si vous travaillez avec des données structurées, envisagez d'utiliser soit une base de données relationnelle, comme SQLite ou un datastore non relationnel clé-valeur LevelDB : Si vous travaillez avec des données non structurées, telles que des fichiers audio, des images ou des contenus vidéo, puis envisagez de stocker données sous forme de fichiers. Vous pouvez combiner différents types de stockage et les exposer en faisant appel à un seul fournisseur de contenu, si nécessaire.
  • Le système Android peut interagir avec la bibliothèque de persistance Room, qui donne accès à l'API de la base de données SQLite fournie par les fournisseurs d'Android pour stocker des données orientées table. Pour créer une base de données à l'aide de cette instanciez une sous-classe de <ph type="x-smartling-placeholder"></ph> RoomDatabase, comme décrit dans Enregistrer des données dans une base de données locale à l'aide de Room

    Vous n'avez pas besoin d'utiliser une base de données pour implémenter votre dépôt. Un fournisseur apparaît en externe sous la forme d'un ensemble de tableaux, semblable à une base de données relationnelle, n'est pas obligatoire pour l'implémentation interne du fournisseur.

  • Pour stocker les données de fichiers, Android dispose de diverses API orientées fichiers. Pour en savoir plus sur le stockage de fichiers, consultez la Présentation du stockage des données et des fichiers Si vous utilisez concevoir un fournisseur qui propose des données liées aux médias, comme la musique ou les vidéos, vous pouvez ont un fournisseur qui combine les données de table et les fichiers.
  • Dans de rares cas, il peut être utile d'implémenter plusieurs fournisseurs de contenu pour pour une même application. Par exemple, vous pouvez partager des données avec un widget en utilisant un fournisseur de contenu, et un ensemble de données différent pour le partage applications.
  • Pour travailler avec des données basées sur le réseau, utilisez des classes dans java.net et android.net Vous pouvez également synchroniser des données réseau telles qu'une base de données, puis de proposer les données sous forme de tables ou de fichiers.

Remarque: Si vous apportez à votre dépôt une modification rétrocompatible, vous devez marquer le dépôt avec une nouvelle version numéro. Vous devez également augmenter le numéro de version de votre application met en œuvre le nouveau fournisseur de contenu. Cette modification empêche le système les rétrogradations ne provoquent pas le plantage du système lors de la tentative de réinstallation Application dont le fournisseur de contenu n'est pas compatible

Considérations relatives à la conception des données

Voici quelques conseils pour concevoir la structure de données de votre fournisseur:

  • Les données de la table doivent toujours avoir une "clé primaire" colonne que le fournisseur gère sous la forme d'une valeur numérique unique pour chaque ligne. Vous pouvez utiliser cette valeur pour associer la ligne lignes dans d'autres tables (en l'utilisant comme "clé étrangère"). Même si vous pouvez utiliser n'importe quel nom Pour cette colonne, il est préférable d'utiliser BaseColumns._ID car le fait d'associer les résultats d'une requête de fournisseur ListView exige que l'une des colonnes récupérées ait le nom _ID
  • Si vous souhaitez fournir des images bitmap ou d'autres éléments très volumineux de données orientées fichiers, stockez les données d'un fichier, puis les fournir indirectement au lieu de les stocker directement dans un tableau. Dans ce cas, vous devez indiquer aux utilisateurs de votre fournisseur qu'ils doivent utiliser un ContentResolver pour accéder aux données.
  • Utilisez le type de données BLOB (Binary Large Object) pour stocker des données de taille variable ou ayant une structure variable. Par exemple, vous pouvez utiliser une colonne BLOB pour stocker tampon de protocole ou Structure JSON :

    Vous pouvez également utiliser un BLOB pour implémenter une table indépendante du schéma. Dans ce type de table, vous définissez une colonne de clé primaire, une colonne de type MIME, et une ou des colonnes plus génériques que BLOB. La signification des données des colonnes BLOB est indiquée par la valeur figurant dans la colonne "Type MIME". Cela vous permet de stocker différents types de lignes le même tableau. Les "données" du fournisseur de contacts tableau ContactsContract.Data est un exemple d'instance de VM indépendante du schéma. tableau.

Concevoir des URI de contenu

Un URI de contenu est un URI qui identifie les données d'un fournisseur. Les URI de contenu incluent le nom symbolique de l'ensemble du fournisseur (son autorité) et un qui pointe vers une table ou un fichier (un chemin d'accès). La partie facultative de l'ID pointe vers une ligne individuelle dans un tableau. Chaque méthode d'accès aux données ContentProvider possède un URI de contenu comme argument. Cela vous permet déterminer la table, la ligne ou le fichier auquel accéder.

Pour en savoir plus sur les URI de contenu, consultez <ph type="x-smartling-placeholder"></ph> Principes de base des fournisseurs de contenu

Concevoir une autorité

Un fournisseur dispose généralement d'une autorité unique, qui sert de nom interne à Android. À éviter les conflits avec d'autres fournisseurs, utiliser la propriété du domaine Internet (à l'envers) sur la base de votre autorité de fournisseur. Comme cette recommandation s'applique aussi à Android, vous pouvez définir votre autorité de fournisseur en tant qu'extension du nom du package contenant le fournisseur.

Par exemple, si le nom de votre package Android est com.example.<appname>, donnez à votre fournisseur le autorité com.example.<appname>.provider.

Concevoir une structure de tracé

Les développeurs créent généralement des URI de contenu à partir de l'autorité en ajoutant des chemins qui pointent vers des tables individuelles. Par exemple, si vous avez deux tables, table1 et table2, vous pouvez les combiner avec l'autorité de l'exemple précédent pour obtenir le résultat URI de contenu com.example.<appname>.provider/table1 et com.example.<appname>.provider/table2 Les chemins d'accès ne sont pas est limité à un seul segment. De plus, il n'est pas nécessaire de créer un tableau pour chaque niveau du chemin.

Gérer les ID d'URI de contenu

Par convention, les fournisseurs permettent d'accéder à une seule ligne d'une table en acceptant un URI de contenu avec une valeur d'ID pour la ligne à la fin de l'URI. Par convention, les fournisseurs correspondent "ID" dans la colonne _ID de la table et effectuez l'accès demandé sur ligne qui correspond.

Cette convention facilite un modèle de conception courant pour les applications accédant à un fournisseur. L'application exécute une requête sur le fournisseur et affiche le résultat Cursor. dans un ListView à l'aide d'un CursorAdapter. La définition de CursorAdapter nécessite que l'une des colonnes de la propriété Cursor devient _ID

L'utilisateur sélectionne ensuite l'une des lignes affichées dans l'interface utilisateur pour consulter ou modifier la données. L'application obtient la ligne correspondante à partir du Cursor qui sauvegarde le ListView, obtient la valeur _ID pour cette ligne et l'ajoute à l'URI de contenu et envoie la demande d'accès au fournisseur. Le fournisseur peut alors effectuer requête ou modification par rapport à la ligne exacte sélectionnée par l'utilisateur.

Formats d'URI de contenu

Pour vous aider à choisir l'action à effectuer pour un URI de contenu entrant, l'API du fournisseur inclut La classe de commodité UriMatcher, qui mappe les modèles d'URI de contenu des valeurs entières. Vous pouvez utiliser les valeurs entières dans une instruction switch qui choisit l'action souhaitée pour le ou les URI de contenu qui correspondent à un format particulier.

Un modèle d'URI de contenu met en correspondance les URI de contenu à l'aide de caractères génériques:

  • * correspond à une chaîne de n'importe quel caractère valide de n'importe quelle longueur.
  • # correspond à une chaîne de caractères numériques de n'importe quelle longueur.

Pour un exemple de conception et de codage de la gestion d'URI de contenu, prenons l'exemple d'un fournisseur avec le l'autorité com.example.app.provider qui reconnaît les URI de contenu suivants pointant vers des tables:

  • content://com.example.app.provider/table1: une table appelée table1.
  • content://com.example.app.provider/table2/dataset1: une table appelée dataset1
  • content://com.example.app.provider/table2/dataset2: une table appelée dataset2
  • content://com.example.app.provider/table3: une table appelée table3.

Le fournisseur reconnaît également ces URI de contenu s'ils sont associés à un ID de ligne, par exemple content://com.example.app.provider/table3/1 pour la ligne identifiée par 1 dans table3.

Les formats d'URI de contenu suivants sont possibles:

content://com.example.app.provider/*
Correspond à n'importe quel URI de contenu du fournisseur.
content://com.example.app.provider/table2/*
Correspond à un URI de contenu pour les tables dataset1 et dataset2, mais ne correspond pas aux URI de contenu pour table1 ou table3
content://com.example.app.provider/table3/#
Correspond à un URI de contenu pour des lignes uniques dans table3, par exemple content://com.example.app.provider/table3/6 pour la ligne identifiée par 6

L'extrait de code suivant montre le fonctionnement des méthodes dans UriMatcher. Ce code ne gère pas les URI d'une table entière différemment des URI d'une ligne unique à l'aide du modèle d'URI de contenu content://<authority>/<path> pour les tables et content://<authority>/<path>/<id> pour des lignes individuelles.

La méthode addURI() mappe et le chemin d'accès à une valeur entière. La méthode match() renvoie la valeur entière d'un URI. Une instruction switch choisit entre interroger l'ensemble de la table et interroger un seul enregistrement.

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
    }

Une autre classe, ContentUris, fournit des méthodes pratiques pour travailler avec la partie id des URI de contenu. Les classes Uri et Uri.Builder incluent des méthodes pratiques pour analyser des Uri et en créer d'autres.

Implémenter la classe ContentProvider

L'instance ContentProvider gère l'accès à un ensemble structuré de données en traitant les requêtes provenant d'autres applications. Tous les formulaires d'accès finissent par appeler ContentResolver, qui appelle ensuite une instance de ContentProvider pour obtenir l'accès.

Méthodes requises

La classe abstraite ContentProvider définit six méthodes abstraites qui à implémenter dans votre sous-classe concrète. Toutes ces méthodes, sauf Les éléments onCreate() sont appelés par une application cliente. qui tente d'accéder à votre fournisseur de contenu.

query()
Récupérer des données auprès de votre fournisseur Utilisez les arguments pour sélectionner la table requête, les lignes et les colonnes à renvoyer et l'ordre de tri du résultat. Renvoyez les données en tant qu'objet Cursor.
insert()
Insérez une nouvelle ligne dans votre fournisseur. Utilisez les arguments pour sélectionner table de destination et obtenir les valeurs de colonne à utiliser. Renvoyez un URI de contenu pour ligne qui vient d'être insérée.
update()
Mettez à jour les lignes existantes de votre fournisseur. Utiliser les arguments pour sélectionner le tableau et les lignes de mettre à jour et d'obtenir les valeurs de colonne mises à jour. Renvoie le nombre de lignes mises à jour.
delete()
Supprimez des lignes de votre fournisseur. Utilisez les arguments pour sélectionner le tableau et les lignes à supprimer. Renvoie le nombre de lignes supprimées.
getType()
Renvoie le type MIME correspondant à un URI de contenu. Cette méthode est décrite consultez la section Implémenter les types MIME du fournisseur de contenu.
onCreate()
Initialisez votre fournisseur. Le système Android appelle cette méthode immédiatement après l'avoir crée votre fournisseur. Votre fournisseur n'est créé L'objet ContentResolver tente d'y accéder.

Ces méthodes ont la même signature que les méthodes portant un nom identique ContentResolver.

Votre implémentation de ces méthodes doit tenir compte des éléments suivants:

  • Toutes ces méthodes, sauf onCreate() peuvent être appelés par plusieurs threads à la fois, ils doivent donc être sécurisés. Pour apprendre sur les threads, consultez la <ph type="x-smartling-placeholder"></ph> Présentation des processus et des threads
  • Évitez d'effectuer des opérations longues dans onCreate(). Différez les tâches d'initialisation jusqu'à ce qu'elles soient réellement nécessaires. La section sur l'implémentation de la méthode onCreate() à ce sujet plus en détail.
  • Bien que vous deviez implémenter ces méthodes, votre code n'a rien à faire, sauf renvoient le type de données attendu. Par exemple, vous pouvez empêcher les autres applications d'insérer des données dans certaines tables en ignorant l'appel insert() et retour 0.

Implémenter la méthode query()

La La méthode ContentProvider.query() doit renvoyer un objet Cursor. Si tel est le cas, échoue, générez une Exception. Si vous utilisez une base de données SQLite comme base de données d'espace de stockage, vous pouvez renvoyer le Cursor renvoyé par l'un des Méthodes query() de la classe SQLiteDatabase.

Si la requête ne correspond à aucune ligne, renvoie un Cursor Une instance dont la méthode getCount() renvoie 0. Ne renvoie null que si une erreur interne s'est produite pendant le processus de requête.

Si vous n'utilisez pas de base de données SQLite comme stockage de données, utilisez l'une des sous-classes concrètes sur Cursor. Par exemple, la classe MatrixCursor implémente un curseur dans lequel chaque ligne est un tableau d'instances Object. Dans ce cours, utilisez addRow() pour ajouter une ligne.

Le système Android doit être en mesure de communiquer le Exception au-delà des limites des processus. Android peut le faire pour les exceptions suivantes, qui sont utiles dans la gestion des erreurs de requête:

Implémenter la méthode insert()

La méthode insert() ajoute nouvelle ligne dans la table appropriée, à l'aide des valeurs du ContentValues . Si un nom de colonne ne figure pas dans l'argument ContentValues, vous fournir une valeur par défaut dans le code de votre fournisseur ou dans votre base de données du schéma.

Cette méthode renvoie l'URI de contenu de la nouvelle ligne. Pour ce faire, ajoutez le code la clé primaire de la ligne (généralement la valeur _ID) à l'URI de contenu de la table, à l'aide de withAppendedId()

Implémenter la méthode delete()

La méthode delete() n'a pas besoin de supprimer des lignes de votre espace de stockage. Si vous utilisez un adaptateur de synchronisation avec votre fournisseur, pensez à marquer une ligne supprimée avec une requête "delete" au lieu de supprimer complètement la ligne. L'adaptateur de synchronisation rechercher les lignes supprimées et les retirer du serveur avant de les supprimer du fournisseur.

Implémenter la méthode update()

La méthode update() utilise le même argument ContentValues que celui utilisé par insert() et les les mêmes arguments selection et selectionArgs utilisés par delete() et ContentProvider.query() Cela peut vous permettre de réutiliser du code entre ces méthodes.

Implémenter la méthode onCreate()

Le système Android appelle onCreate() au démarrage du fournisseur. Effectuer uniquement une initialisation rapide de cette méthode, et différer la création de la base de données et le chargement des données jusqu'à ce que le fournisseur reçoit une requête de données. Si vous effectuez de longues tâches dans onCreate(), vous ralentissez votre la start-up d'un fournisseur. Cela ralentit la réponse du fournisseur à d'autres applications.

Les deux extraits de code suivants illustrent l'interaction entre ContentProvider.onCreate() et <ph type="x-smartling-placeholder"></ph> Room.databaseBuilder() Le premier l'extrait de code montre l'implémentation ContentProvider.onCreate(), où de base de données est créé et les traitements associés aux objets d'accès aux données sont créés:

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.
    }
}

Implémenter les types MIME du ContentProvider

La classe ContentProvider comporte deux méthodes pour renvoyer les types MIME:

getType()
L'une des méthodes requises à implémenter pour n'importe quel fournisseur.
getStreamTypes()
Méthode que vous devez implémenter si votre fournisseur propose des fichiers.

Types MIME pour les tables

La méthode getType() renvoie une String au format MIME qui décrit le type de données renvoyé par le contenu un argument d'URI. L'argument Uri peut être un modèle plutôt qu'un URI spécifique. Dans ce cas, renvoyez le type de données associé aux URI de contenu qui correspondent aux du modèle.

Pour les types de données courants tels que le texte, HTML ou JPEG, getType() renvoie la valeur le type MIME de ces données. La liste complète de ces types de normes est disponible dans le Types de médias MIME de l'IANA sur votre site Web.

Pour les URI de contenu qui pointent vers une ou plusieurs lignes de données de table, Retours pour getType() Un type MIME au format MIME propre au fournisseur d'Android:

  • Partie du type: vnd
  • Partie du sous-type: <ph type="x-smartling-placeholder">
      </ph>
    • Si le format d'URI correspond à une seule ligne: android.cursor.item/
    • Si le format d'URI correspond à plusieurs lignes: android.cursor.dir/
  • Partie spécifique au fournisseur: vnd.<name>.<type>

    Vous fournissez <name> et <type>. La valeur <name> est unique. et la valeur <type> est unique dans l'URI correspondant du modèle. Un bon choix pour <name> est le nom de votre entreprise ou une partie du nom du package Android de votre application. Un bon choix pour les <type> est une chaîne qui identifie la table associée au URI.

Par exemple, si l'autorité d'un fournisseur est com.example.app.provider et expose une table nommée table1, le type MIME de plusieurs lignes dans table1 est le suivant:

vnd.android.cursor.dir/vnd.com.example.provider.table1

Pour une seule ligne de table1, le type MIME est le suivant:

vnd.android.cursor.item/vnd.com.example.provider.table1

Types MIME pour les fichiers

Si votre fournisseur propose des fichiers, implémentez getStreamTypes() La méthode renvoie un tableau String des types MIME pour les fichiers que votre fournisseur peut renvoyer pour un URI de contenu donné. Filtrer les types MIME que vous proposez par type MIME l'argument filter, de sorte que vous ne renvoyez que les types MIME que le client souhaite gérer.

Prenons l'exemple d'un fournisseur qui propose des photos sous forme de fichiers JPG, PNG et GIF. Si une application appelle ContentResolver.getStreamTypes() avec la chaîne de filtre image/*, pour un élément qui est une « image », la méthode ContentProvider.getStreamTypes() renvoie alors le tableau:

{ "image/jpeg", "image/png", "image/gif"}

Si l'application ne s'intéresse qu'aux fichiers JPG, elle peut appeler ContentResolver.getStreamTypes() avec la chaîne de filtre *\/jpeg ; getStreamTypes() renvoie:

{"image/jpeg"}

Si votre fournisseur ne propose aucun des types MIME demandés dans la chaîne de filtre, getStreamTypes() renvoie null.

Implémenter une classe de contrat

Une classe de contrat est une classe public final qui contient des définitions constantes pour le URI, noms de colonne, types MIME et autres métadonnées relatives au fournisseur. La classe établit un contrat entre le fournisseur et d'autres applications en s'assurant que le fournisseur sont accessibles correctement, même en cas de modification des valeurs réelles des URI, des noms de colonnes, et ainsi de suite.

Une classe de contrat aide également les développeurs, car elle porte généralement des noms mnémotechniques pour ses constantes, les développeurs sont donc moins susceptibles d'utiliser des valeurs incorrectes pour les noms de colonne ou les URI. Puisqu'il s'agit d'un elle peut contenir de la documentation Javadoc. Les environnements de développement intégrés, Android Studio peut compléter automatiquement les noms des constantes de la classe de contrat et afficher Javadoc pour les constantes.

Les développeurs ne peuvent pas accéder au fichier de classe de la classe de contrat à partir de votre application, mais ils peuvent le compilent de manière statique dans leur application à partir d'un fichier JAR que vous fournissez.

La classe ContactsContract et ses classes imbriquées sont des exemples de classes contractuelles.

Implémenter des autorisations de fournisseur de contenu

Les autorisations et les accès pour tous les aspects du système Android sont décrits en détail dans Conseils de sécurité La présentation du stockage des données et des fichiers décrit la sécurité et les autorisations en vigueur pour les différents types de stockage. En résumé, les points importants sont les suivants:

  • Par défaut, les fichiers de données stockés dans la mémoire de stockage interne de l'appareil sont privés de l'application et du fournisseur.
  • Les bases de données SQLiteDatabase que vous créez sont privées de l'application et du fournisseur.
  • Par défaut, les fichiers de données que vous enregistrez sur un espace de stockage externe sont publics et lisible au monde entier. Vous ne pouvez pas utiliser un fournisseur de contenu pour restreindre l'accès aux fichiers dans stockage externe, car d'autres applications peuvent utiliser d'autres appels d'API pour les lire et les écrire.
  • La méthode appelle l'ouverture ou la création de fichiers ou de bases de données SQLite dans l'environnement interne de votre appareil. le stockage peut potentiellement donner un accès en lecture et en écriture à toutes les autres applications. Si vous utilisez un fichier ou une base de données interne comme référentiel, et vous lui donnez "lisible au monde entier" ou "inscriptible à tous" ; les autorisations que vous avez définies pour votre fournisseur son fichier manifeste ne protègent pas vos données. L'accès par défaut aux fichiers et aux bases de données dans la mémoire de stockage interne est "privée". ne le modifiez pas pour le dépôt de votre fournisseur.

Si vous souhaitez utiliser les autorisations du fournisseur de contenu pour contrôler l'accès à vos données, stocker vos données dans des fichiers internes, dans des bases de données SQLite ou dans le cloud, sur un serveur distant, et préserver la confidentialité des fichiers et des bases de données dans votre application.

Implémenter des autorisations

Par défaut, toutes les applications peuvent lire ou écrire sur votre fournisseur, même si les données sous-jacentes sont privé, car par défaut votre fournisseur n’a pas d’autorisations définies. Pour changer cela, définissez les autorisations pour votre fournisseur dans votre fichier manifeste, à l'aide d'attributs ou d'éléments de l'élément <provider>. Vous pouvez définir des autorisations qui s'appliquent à l'ensemble du fournisseur, à certaines tables, à certains enregistrements, ou aux trois.

Vous définissez des autorisations pour votre fournisseur <permission> dans votre fichier manifeste. Pour que le autorisation propre à votre fournisseur, utilisez un champ d'application de style Java pour android:name. Par exemple, nommez l’autorisation de lecture com.example.app.provider.permission.READ_PROVIDER

La liste suivante décrit le champ d'application des autorisations de fournisseur, en commençant par les autorisations qui s'appliquent à l'ensemble du fournisseur, puis de devenir plus précises. Les autorisations plus précises prévalent sur celles dont le champ d'application est plus étendu.

Autorisation unique au niveau du fournisseur en lecture/écriture
Une autorisation qui contrôle à la fois l'accès en lecture et en écriture à l'ensemble du fournisseur, spécifiée avec l'attribut android:permission de <provider>.
Séparer les autorisations de lecture et d'écriture au niveau du fournisseur
Une autorisation de lecture et une autorisation d'écriture pour l'ensemble du fournisseur. Vous les spécifiez avec les android:readPermission et Attributs android:writePermission de <provider>. Elles prévalent sur l'autorisation requise par android:permission
Autorisation au niveau du chemin d'accès
Autorisation de lecture, d'écriture ou de lecture/écriture pour un URI de contenu de votre fournisseur. Vous indiquez chaque URI que vous souhaitez contrôler Élément enfant <path-permission> de Élément <provider>. Pour chaque URI de contenu que vous spécifiez, vous pouvez spécifier un une autorisation de lecture/écriture, une autorisation de lecture, une autorisation d’écriture, ou les trois. L'interface de lecture et les autorisations d'écriture prévalent sur l'autorisation de lecture/écriture. De plus, au niveau du chemin l'autorisation prévaut sur les autorisations définies au niveau du fournisseur.
Autorisation temporaire
Un niveau d'autorisation qui accorde un accès temporaire à une application, même si l'application ne dispose pas des autorisations normalement requises. L'option "Temporaire" réduit le nombre d'autorisations qu'une application doit demander dans dans son fichier manifeste. Lorsque vous activez les autorisations temporaires, les seules applications qui ont besoin des autorisations permanentes pour votre fournisseur sont celles qui accèdent en permanence vos données.

Par exemple, tenez compte des autorisations dont vous avez besoin si vous implémentez un fournisseur de messagerie et une application autoriser une application externe de visionneuse d'images à afficher les photos jointes à partir de votre un fournisseur de services agréé. Pour accorder au lecteur d'images l'accès nécessaire sans exiger d'autorisations, vous pouvez configurer des autorisations temporaires pour les URI de contenu des photos.

Concevez votre application de messagerie de sorte que que lorsque l'utilisateur souhaite afficher une photo, l'application envoie un intent contenant l'URI de contenu de la photo et les indicateurs d'autorisation associés à la visionneuse d'images. La visionneuse d'images puis demandez à votre fournisseur de messagerie de récupérer la photo, disposer de l'autorisation de lecture normale pour votre fournisseur.

Pour activer les autorisations temporaires, définissez le paramètre Attribut android:grantUriPermissions de <provider> élément ou ajoutez-en un ou plusieurs <grant-uri-permission> éléments enfants à votre <provider>. Appeler Context.revokeUriPermission() chaque fois que vous supprimez la prise en charge d'un URI de contenu associé à une autorisation temporaire dans votre un fournisseur de services agréé.

La valeur de l'attribut détermine dans quelle mesure votre fournisseur est rendu accessible. Si l'attribut est défini sur "true", le système accorde une autorisation temporaire l'autorisation pour l'ensemble de votre fournisseur, ignorant ainsi toutes les autres autorisations requises par les autorisations définies au niveau du fournisseur ou du chemin d'accès.

Si cet indicateur est défini sur "false", ajoutez <grant-uri-permission> éléments enfants à votre <provider>. Chaque élément enfant spécifie l'URI de contenu URI pour lesquels un accès temporaire est accordé.

Pour déléguer un accès temporaire à une application, un intent doit contenir l'option FLAG_GRANT_READ_URI_PERMISSION, l'option FLAG_GRANT_WRITE_URI_PERMISSION, ou les deux. Ces sont définies avec la méthode setFlags().

Si l'attribut android:grantUriPermissions n'est pas présent, il est considéré comme "false"

<provider> élément

Comme les composants Activity et Service, Une sous-classe de ContentProvider est défini dans le fichier manifeste de son application, à l'aide de la méthode <provider>. Le système Android obtient les informations suivantes l'élément:

Autorité (android:authorities)
Noms symboliques qui identifient l'ensemble du fournisseur au sein du système. Ce est décrit plus en détail dans la Concevoir des URI de contenu
Nom de classe du fournisseur (android:name)
La classe qui implémente ContentProvider. Ce cours est décrit plus en détail dans le Implémentez la classe ContentProvider.
Autorisations
Attributs qui spécifient les autorisations que d'autres applications doivent avoir pour accéder les données du fournisseur: <ph type="x-smartling-placeholder">

Les autorisations et les attributs correspondants sont décrits consultez la documentation Section Implémenter les autorisations du fournisseur de contenu

Attributs de démarrage et de contrôle
Ces attributs déterminent quand et comment le système Android lance le fournisseur, les caractéristiques de processus du fournisseur, ainsi que d'autres paramètres d'exécution: <ph type="x-smartling-placeholder">
    </ph>
  • android:enabled: option permettant au système de démarrer le fournisseur
  • android:exported: option permettant à d'autres applications d'utiliser ce fournisseur
  • android:initOrder: ordre de démarrage du fournisseur par rapport à d'autres fournisseurs dans le même processus
  • android:multiProcess: option permettant au système de démarrer le fournisseur dans le même processus que le client appelant
  • android:process: nom du processus dans lequel le fournisseur exécute
  • android:syncable: indicateur indiquant que les données du fournisseur doivent être synchronisées avec des données sur un serveur

Ces attributs sont décrits en détail dans le guide Élément <provider>.

Attributs informatifs
Icône et libellé facultatifs pour le fournisseur: <ph type="x-smartling-placeholder">
    </ph>
  • android:icon: ressource drawable contenant une icône pour le fournisseur. L'icône s'affiche à côté du libellé du fournisseur dans la liste des applications dans Paramètres > Applications > Tout :
  • android:label: libellé informatif décrivant le fournisseur, ses données, ou les deux. Le libellé s'affiche dans la liste des applications dans Paramètres > Applications > Tout :

Ces attributs sont décrits en détail dans le guide Élément <provider>.

Remarque:Si vous ciblez Android 11 ou une version ultérieure, consultez le documentation sur la visibilité des packages pour les besoins de configuration supplémentaires.

Intents et accès aux données

Les applications peuvent accéder indirectement à un fournisseur de contenu avec un Intent. L'application n'appelle aucune des méthodes de ContentResolver ou ContentProvider Au lieu de cela, il envoie un intent qui démarre une activité, qui fait souvent partie de l'application propre au fournisseur. L'activité de destination est responsable de récupérer et afficher les données dans son interface utilisateur.

Selon l'action de l'intent, l'activité de destination peut également inviter l'utilisateur à apporter des modifications aux données du fournisseur. Un intent peut aussi contenir des "extras" données affichées par l'activité de destination dans l'UI. L'utilisateur a alors la possibilité de modifier ces données avant de les utiliser pour modifier le des données dans le fournisseur.

Vous pouvez utiliser l'accès aux intents pour favoriser l'intégrité des données. Votre fournisseur peut dépendre sur l'insertion, la mise à jour et la suppression des données selon une logique métier strictement définie. Si En effet, laisser d'autres applications modifier directement vos données peut entraîner des données non valides.

Si vous souhaitez que les développeurs utilisent l'accès aux intents, veillez à le documenter minutieusement. Expliquer pourquoi il est préférable d'accéder à l'intent à l'aide de l'UI de votre application plutôt que d'essayer de et modifier les données avec leur code.

Le traitement d'un intent entrant qui souhaite modifier les données de votre fournisseur est identique à celui pour gérer d'autres intents. Pour en savoir plus sur l'utilisation des intents, consultez Intents et filtres d'intents :

Pour en savoir plus, consultez les Présentation du fournisseur d'agenda