O Android permite armazenar informações de diversas formas diferentes utilizando o banco de dados, arquivos e o sistema de preferências. Contudo, geralmente essas informações ficam salvas dentro do pacote da aplicação, e somente a aplicação que criou o banco de dados ou o arquivo pode ter acesso às informações.

Por conta disso, foi criada a classe android.content.ContentProvider e o conceito de provedor de conteúdo para que determinadas informações sejam públicas para qualquer aplicação.

Provedores de conteúdo encapsulam o gerenciamento de dados para que outras partes do aplicativo, como a visão e o controlador, não tenham de participar da persistência de dados.

Com o auxílio de um provedor de conteúdo, aplicativos não têm de abrir suas próprias tabelas SQLite, uma vez que isso ocorrerá por trás da interface do provedor de conteúdo, em tabelas que são de sua propriedade. Antigamente, para compartilhar dados, aplicativos móveis tinham de armazená-los em arquivos no sistema de arquivos local, com um formato de configuração definido pelo aplicativo. Com o Android, por outro lado, aplicativos podem muitas vezes depender apenas do armazenamento do provedor de conteúdo.

O Android tem uma série de provedores de conteúdos nativos como, por exemplo, consultar os contatos da agenda, visualizar os arquivos, imagens e vídeos disponíveis no celular.

Independente de se esse provedor é nativo do Android ou se é criado pelo desenvolvedor, a forma como a aplicação se comunica com um provedor é padronizada.

Criar seu próprio provedor de conteúdo significa completar as seguintes tarefas:

  1. Criar uma API pública de provedor de conteúdo, para consumo pelo cliente: - Definindo o CONTENT_URI do seu provedor de conteúdo. - Criando nomes de colunas para se comunicar com clientes. - Declarando objetos String, estáticos e públicos, que os clientes utilizarão para especificar colunas. - Definindo tipos MIME para quaisquer novos tipos de dados.
  2. Implementar seu provedor de conteúdo : - Estendendo a principal APIdo provedor de conteúdo, a classe ContentProvider, para criar uma implementação personalizada de provedor de conteúdo. - Definindo um URI de provedor. - Criando um banco de dados SQLite e cursores associados para armazenar dados do provedor de conteúdo. - Utilizando cursores para se assegurar de que os dados estarão disponíveis aos clientes, mas que também aceitarão atualizações dinâmicas. - Definindo o processo pelo qual dados binários serão retornados ao cliente. - Implementando os métodos básicos de dados (query, insert, update e delete) de um cursor, para retornar ao cliente.
  3. Atualizar o arquivo AndroidManifest.xml para que declare seu .

Para demonstrar a criação e o uso do Provedor de Conteúdo no Android, criaremos um exemplo de tela que contém um campo para digitar o nome do estado, um campo para digitar a sigla do estado, um botão Incluir e um botão Listar. Ao pressionar o botão Incluir, o registro com o valor do estado e sigla será incluído no banco de dados, exibindo a mensagem Registro incluído, e ao pressionar o botão Listar será exibida a tela com a lista de estados cadastrados, conforme mostrado nas Figuras 1 e 2. Ao selecionar algum item da lista, o mesmo será exibido na tela que contém um campo para alterar o nome do estado, um campo para alterar a sigla do estado, um botão Excluir e um botão Atualizar. Ao pressionar o botão Atualizar, o registro com o valor do estado e sigla será atualizado no banco de dados, exibindo a mensagem Registro atualizado e ao pressionar o botão Excluir o registro com o valor do estado e sigla será excluído do banco de dados, exibindo a mensagem Registro excluído, conforme mostrado na Figura 3.

>Tela Principal
Figura 1. Tela Principal
Tela Listagem
Figura 2. Tela Listagem
Tela Manutenção
Figura 3. Tela Manutenção

A seguir, podemos visualizar os arquivos de layout de tela, conforme mostrado nas Listagens 1, 2 e 3.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"  >
    <TextView 
        android:id="@+id/stdform"
        android:text = "Cadastro - Unidade da Federação"           
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp" 
        android:textStyle="bold" 
        android:padding="10dip"
        android:layout_centerHorizontal="true"/>   
    <TextView
        android:id="@+id/estado_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Estado" 
        android:layout_below="@id/stdform" />
    <EditText
        android:id="@+id/estado_nome"
        android:layout_height="wrap_content"
        android:layout_width="250dp" 
        android:layout_below="@id/stdform"
        android:layout_toRightOf="@id/estado_view" />
    <TextView
        android:id="@+id/stdcode_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sigla" 
        android:layout_below="@id/estado_nome" />
    <EditText
        android:id="@+id/estado_sigla"
        android:layout_height="wrap_content"
        android:layout_width="100dp" 
        android:layout_marginLeft="20dip"
        android:layout_toRightOf="@id/stdcode_view"
        android:layout_below="@id/estado_nome" />
    <Button
        android:text="Incluir"
        android:id="@+id/add_uf"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_below="@id/estado_sigla"
        android:layout_marginLeft="20dip"  />
    <Button
        android:text="Listar"
        android:id="@+id/list_uf"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_toRightOf="@id/add_uf"
        android:layout_below="@id/estado_sigla"  />
</RelativeLayout>
Listagem 1. mainactivity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
        <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:drawSelectorOnTop="false" />
</LinearLayout>
Listagem 2. showactivity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TextView
       android:id="@+id/estado_view"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Estado" />
    <EditText
       android:id="@+id/estado_nome"
       android:layout_height="wrap_content"
       android:layout_width="250dp" 
       android:layout_toRightOf="@id/estado_view" />
    <TextView
       android:id="@+id/stdcode_view"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Sigla"
       android:layout_below="@id/estado_nome" />
    <EditText
       android:id="@+id/estado_sigla"
       android:layout_height="wrap_content"
       android:layout_width="100dp"
       android:layout_marginLeft="20dip"
       android:layout_toRightOf="@id/stdcode_view"
       android:layout_below="@id/estado_nome" />
    <Button
        android:text="Excluir"
        android:id="@+id/delete_uf"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_below="@id/estado_sigla"
        android:layout_marginLeft="70dip"  />
    <Button
        android:text="Atualizar"
        android:id="@+id/update_uf"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
        android:layout_toRightOf="@id/delete_uf"
        android:layout_below="@id/estado_sigla" 
        android:layout_marginLeft="10dip"/>
</RelativeLayout>
Listagem 3. maintainactivity.xml

A seguir podemos visualizar a classe que atua como nosso provedor de conteúdo, conforme mostrado na Listagem 4.


package com.devmedia.contentproviderapp;
 
import android.content.ContentProvider;
import android.content.UriMatcher;
import android.net.Uri;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;
import android.content.ContentValues;
import android.content.ContentUris;
import android.database.SQLException;
import android.database.Cursor;
import android.text.TextUtils;
import android.database.sqlite.SQLiteQueryBuilder;
import android.content.ContentResolver;
 
public class EstadosProvider extends ContentProvider
{
    static final String DB_NAME = "Estados.db";
    static final String DB_TABLE = "uf";
    static final int DB_VERSION = 1;
    static final String CREATE_TABLE ="CREATE TABLE " + DB_TABLE +
  " (_id  INTEGER PRIMARY KEY AUTOINCREMENT, estado TEXT not null, 
      sigla TEXT not null);"; 
    static final String ID = "_id";
    static final String ESTADO = "estado";
    static final String SIGLA = "sigla";
    static final String AUTHORITY="com.devmedia.provider.Estados";
    static final Uri CONTENT_URI =Uri.parse("content://"+AUTHORITY+"/uf");
    static final int ALLROWS = 1;
    static final int SINGLEROW = 2;
    
    private static final UriMatcher URIMATCHER;
    
    static
    {
        URIMATCHER = new UriMatcher(UriMatcher.NO_MATCH);
        URIMATCHER.addURI(AUTHORITY, "uf", ALLROWS);
        URIMATCHER.addURI(AUTHORITY, "uf/#", SINGLEROW);
    }
    
    SQLiteDatabase EstadosDB;
    
    public static final String CONTENT_ITEM_TYPE =   
   ContentResolver.CURSOR_ITEM_BASE_TYPE+"/uf";
 
    @Override
    public boolean onCreate()
    {
        Context context = getContext();
        SQHelper helper = new SQHelper(context);
        EstadosDB = helper.getWritableDatabase();
        return (EstadosDB == null)? false:true;
    }
 
    @Override
    public String getType(Uri uri)
    {
        switch (URIMATCHER.match(uri))
        {
            case ALLROWS:
                return "vnd.android.cursor.dir/vnd.devmedia.uf";
            case SINGLEROW:
                return "vnd.android.cursor.item/vnd.devmedia.uf";
            default:
                throw new IllegalArgumentException("URI inválida: " + uri);
        }
    } 
 
    @Override
    public Cursor query(Uri uri, String[] projection, String criteria,    
     String[] criteriaValues, String sortColumn)
    {        
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        queryBuilder.setTables(DB_TABLE);
        if (URIMATCHER.match(uri) == SINGLEROW)
            queryBuilder.appendWhere(ID + " = " +                               
          uri.getPathSegments().get(1));
        if (sortColumn==null || sortColumn=="")
            sortColumn = "estado";
        Cursor c =  queryBuilder.query(EstadosDB,projection,criteria,criteriaValues
       ,null,null,sortColumn);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c; 
    }
 
    @Override
    public Uri insert(Uri uri, ContentValues contentValues)
    {
       long rowID = EstadosDB.insert(DB_TABLE,null,contentValues);
       if (rowID >0)
       {
           Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID);
           getContext().getContentResolver().notifyChange(_uri, null);
           return _uri;
       }
       throw new SQLException("Erro: Nova linha não pode ser inserida ");
    }
 
    @Override
    public int update(Uri uri, ContentValues contentValues, String criteria,     
     String[] criteriaValues)
       {
        int count = 0;
        switch (URIMATCHER.match(uri))
        {
            case ALLROWS:
                count =  EstadosDB.update
                  (DB_TABLE,contentValues,criteria,criteriaValues);
                break;
            case SINGLEROW:
                count = EstadosDB.update(DB_TABLE, contentValues, ID +
                 " = " + uri.getPathSegments().get(1) +
                 (!    TextUtils.isEmpty(criteria) ? " AND (" +criteria +
                  ')': ""),criteriaValues);
                break;
            default: throw new IllegalArgumentException
               ("URI inválida: " + uri);
        }
        
        getContext().getContentResolver().notifyChange(uri, null);
        
        return count;
    }
 
    @Override
    public int delete(Uri rowUri, String criteria, String[] criteriaValues)
    {
        int count=0;
        switch (URIMATCHER.match(rowUri))
        {
            case ALLROWS:
                count = EstadosDB.delete(DB_TABLE, criteria, criteriaValues);
                break;
            case SINGLEROW: 
                String id = rowUri.getPathSegments().get(1);
                count = EstadosDB.delete(DB_TABLE, ID + " = " 
                + id +(!TextUtils.isEmpty(criteria) ? " AND 
                (" +criteria + ')':""),criteriaValues);
                break;
            default: throw new IllegalArgumentException
             ("URI inválida: " + rowUri);
        }
        
        getContext().getContentResolver().notifyChange(rowUri, null);
        
        return count;
    } 
 
    private static class SQHelper extends SQLiteOpenHelper
    {
        SQHelper(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 "+ DB_TABLE);
            onCreate(db);
        }
    }    
}
Listagem 4. EstadosProvider.java

A seguir podemos visualizar a classe que atua como tela principal da aplicação, conforme mostrado na Listagem 5.


package com.devmedia.contentproviderapp;
 
import com.devmedia.contentproviderapp.R;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.content.ContentValues;
import android.widget.Toast;
import android.widget.EditText;
import android.widget.Button;
import android.content.Intent;
 
public class MainActivity extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mainactivity);
        Button addStdButton = (Button)this.findViewById(R.id.add_uf);
        addStdButton.setOnClickListener(new Button.OnClickListener()
        { 
            @Override
            public void onClick(View v)
            {
                ContentValues contentValues = new ContentValues(); 
                contentValues.put(EstadosProvider.ESTADO, ((EditText)                            
                findViewById(R.id.estado_nome)).getText().toString());
                contentValues.put(EstadosProvider.SIGLA, ((EditText)                             
                  findViewById(R.id.estado_sigla)).getText().toString());
                getContentResolver().insert(EstadosProvider.CONTENT_URI,                   
                 contentValues);
                ((EditText) findViewById(R.id.estado_nome)).setText(""); 
                ((EditText) findViewById(R.id.estado_sigla)).setText(""); 
                Toast.makeText(MainActivity.this, "Registro incluído",                     
                Toast.LENGTH_SHORT).show();
            } 
        });           
 
        Button listStdButton = (Button)this.findViewById(R.id.list_uf);
        listStdButton.setOnClickListener(new Button.OnClickListener()
        { 
            public void onClick(View v)
            {
                startActivity(new Intent(MainActivity.this,                              
                 ShowActivity.class));
            } 
        });     
    }
}
Listagem 5. MainActivity.java

A seguir podemos visualizar a classe que atua como tela listagem da aplicação, conforme mostrado na Listagem 6.


package com.devmedia.contentproviderapp;
 
import com.devmedia.contentproviderapp.R;
 
import android.app.ListActivity;
import android.os.Bundle;
import android.widget.SimpleCursorAdapter;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.widget.ListView;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
 
public class ShowActivity extends ListActivity implements LoaderCallbacks<Cursor>
{
    private SimpleCursorAdapter adapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.showactivity);  
        String[] columns = new String[] { EstadosProvider.ESTADO};  
        int[] toIds = new int[] {android.R.id.text1};  
        getLoaderManager().initLoader(0, null,this);
        adapter = new SimpleCursorAdapter(this,             
        android.R.layout.simple_list_item_1, null, columns, toIds, 0);
        setListAdapter(adapter);
    }    
    
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args)
    {
        String[] projection = new String[] {EstadosProvider.ID,    
        EstadosProvider.ESTADO, EstadosProvider.SIGLA} ;  
        CursorLoader cursorLoader = new CursorLoader(this, 
        EstadosProvider.CONTENT_URI, projection, null, null, null);  
        return cursorLoader;
    }
 
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data)
    {
        adapter.swapCursor(data);
    }
 
    @Override
    public void onLoaderReset(Loader<Cursor> loader)
    {
        adapter.swapCursor(null);
    }
 
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id)
    {
        super.onListItemClick(l, v, position, id);
        Intent intent = new Intent(this, MaintainActivity.class);
        Uri uri = Uri.parse(EstadosProvider.CONTENT_URI + "/" + id);
        intent.putExtra(EstadosProvider.CONTENT_ITEM_TYPE, uri);
        startActivity(intent);
    }
}
Listagem 6.ShowActivity.java

A seguir podemos visualizar a classe que atua como tela manutenção da aplicação, conforme mostrado na Listagem 7.


package com.devmedia.contentproviderapp;
 
import com.devmedia.contentproviderapp.R;
 
import android.os.Bundle;
import android.app.Activity;
import android.widget.EditText;
import android.widget.Button;
import android.net.Uri;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.View;
import android.widget.Toast;
 
public class MaintainActivity extends Activity
{
    EditText estadoNome , estadoSigla ;
    Uri uri;
    
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.maintainactivity);  
        estadoNome  = (EditText) findViewById(R.id.estado_nome);
        estadoSigla  = (EditText) findViewById(R.id.estado_sigla);
        Bundle extras = getIntent().getExtras();
        uri = (extras == null) ? null: (Uri)   extras.getParcelable(EstadosProvider.CONTENT_ITEM_TYPE);
        if (extras != null)
        {
            uri = extras.getParcelable(EstadosProvider.CONTENT_ITEM_TYPE);
            String[] projection = new String[] {EstadosProvider.ID,              
            EstadosProvider.ESTADO, EstadosProvider.SIGLA} ;  
            Cursor cursor = getContentResolver().query(uri, projection, null,    null, null);
            if (cursor != null)
            {
                cursor.moveToFirst();
                estadoNome .setText(cursor.getString
                (cursor.getColumnIndexOrThrow(EstadosProvider.ESTADO)));
                estadoSigla .setText(cursor.getString
                 (cursor.getColumnIndexOrThrow(EstadosProvider.SIGLA)));
                cursor.close();
            }
        }
        Button deleteUf  = (Button) findViewById(R.id.delete_uf);
        Button updateUf = (Button) findViewById(R.id.update_uf);
        
        deleteUf.setOnClickListener(new Button.OnClickListener()
        { 
            @Override
            public void onClick(View v)
            {
                int count = getContentResolver().delete(uri, null, null);
                
                if(count > 0)
                {
                    Toast.makeText(MaintainActivity.this, 
                     "Registro excluído", Toast.LENGTH_SHORT).show(); 
                }
            } 
        });    
 
        updateUf.setOnClickListener(new Button.OnClickListener()
        { 
            @Override
            public void onClick(View v)
            {
                ContentValues contentValues = new ContentValues(); 
                contentValues.put(EstadosProvider.ESTADO, 
                 estadoNome .getText().toString());
                contentValues.put(EstadosProvider.SIGLA, 
                 estadoSigla.getText().toString());
                getContentResolver().update(uri, contentValues,null,null);
                Toast.makeText(MaintainActivity.this, 
                 "Registro atualizado", Toast.LENGTH_SHORT).show(); 
            } 
        });    
    } 
}
Listagem 7. MaintainActivity.java

A seguir podemos visualizar o arquivo AndroidManifest, conforme mostrado na Listagem 8.


<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.devmedia.contentproviderapp"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="19" />
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.devmedia.contentproviderapp.MainActivity"
            android:label="@string/title_main_activity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.devmedia.contentproviderapp.ShowActivity" 
         android:label="@string/app_name" />
        <activity android:name=
         "com.devmedia.contentproviderapp.MaintainActivity" 
         android:label="@string/app_name" />
              <provider android:name="EstadosProvider" android:
               authorities="com.devmedia.provider.Estados">
        </provider>
        
    </application>
 
</manifest>
Listagem 8. AndroidManifest.xml