Un ContentProvider de Android es una especie de BD que una aplicación abre al resto de aplicaciones. Por ejemplo, si hacemos una aplicación con una BD SQLite está solamente estará disponible para la propia aplicación pero la podriamos abrir/ofrecer al resto del sistema a través de un content provider. Pero ojo¿solamente bases de datos? No, los content providers pretenden como su nombre indica proveer contenido y lo mismo puede ser una BD que unos ficheros, que unos datos arbitrarios. Lo peculiar del Content Provider es que la clase que ofrece para ello es prácticamente clavada a un CRUD de SQLite. Pero insisto no tiene por qué ser una base de datos lo que haya detrás.

ContentProvider en Android
¿Cómo monto esto?

Creas una aplicación normal con su activity normal. Dentro de la aplicación puedes añadir un ContentProvider, si estás en eclipse tira del asistente y te generará la entrada que hace falta en el Manifest de Android. En el cliente que vaya a usar el ContentProvider debes declararlo también, como un permiso. A partir de ahí, las peticiones del Cliente al Provider se hacen a través de... chan chan URIs, vamos, URL con parámetros. Un provider se asemeja algo a un recurso REST en la forma de acceder.

ContentProvider super simple

El ContentProvider que he desarrollado aquí no es más que una prueba de concepto muy simple para probar tres cosas:

  • Lo esencial: desde un cliente invocar a un ContentProvider.
  • Proveer datos ajenos a SQL: crear un cursor con mis datos arbitrarios.
  • UriMatcher: jugar un poco con los matcher para aplicar distintas queries
Un content provider básicamente tiene métodos para hacer: query, insert, update, delete. ¿Te suena? y como decía, los parámetros son muy similares a los que se utilizan en SQLite para Android. El caso del query es especial porque podemos aplicar distintas queries con un switch según el valor del URI. Se supone que las URI deben tener cierta coherencia. Deben comenzar por content:// seguido por el String de authority que identifica en cierto modo al Provider. Luego se indica la tabla y luego parámetros opcionales. Te recomiendo que al final del todo eches un ojo al manual oficial ya que lo explica con todo detalle, aunque sea raro a veces con Android es mejor ver algo simple y luego ampliarlo con toda la turrada que te sueltan los de google; solo Chuck Norris puede programar Android basándose solo en los manuales oficiales. Veamos el código que de paso está comentado.

package info.pello.android.contentprovider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

/**
 * A content provider to offer Student data to clients.
 * It extends ContentProvider class providing a common way
 * to manage data with a CRUD-like methods set.
 * @author Pello Xabier Altadill Izura
 * @greetz any
 */
public class StudentContentProvider extends ContentProvider {


	// We set uriMatcher to get params passed to URIs.
	// So we can give different values depending on those params
    private  UriMatcher uriMatcher;
    // Our data:
    private MatrixCursor mCursor;

	/**
	 * default constructor.
	 */
	public StudentContentProvider() {
	}

	/**
	 * called when provider is started, so we use it to initialize data.
	 */
	@Override
	public boolean onCreate() {
		Log.d("PELLODEBUG","CP> onCreate, init data.");
		initUris();
		initData();
		return true;
	}
	/**
	 * init content provider Uris
	 * we set some kind of uri patterns to route them to different queries
	 */
	private void initUris() {
		uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

		// This will match: content://info.pello.android.contentprovider.provider.Students/students/
		uriMatcher.addURI("info.pello.android.contentprovider.provider.Students", "students/", 1);

		// This will match: content://info.pello.android.contentprovider.provider.Students/students/2
	    uriMatcher.addURI("info.pello.android.contentprovider.provider.Students", "students/*/", 2);
	}

	/**
	 * we add some data to our "database"
	 */
	private void initData () {
		mCursor = new MatrixCursor(new String[] {"_id","name","description"});
		mCursor.addRow(new Object[] {2,"Velasco","A future iPhone developer"});
		mCursor.addRow(new Object[] {1,"JR","Android developer"});
		mCursor.addRow(new Object[] {3,"Vigor","VB6 developer"});
	}


	/**
	 * we query the database, depending on uriMatcher we can execute
	 * different queries.
	 * The parameters of the query are the same of a SQLite helper query.
	 */
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {


		Log.d("PELLODEBUG","CP> query " + uri+ " match:" + uriMatcher.match(uri));
		switch (uriMatcher.match(uri)) {
			case 1:
				Log.d("PELLODEBUG","query to 1. ");
				break;
			case 2:
				Log.d("PELLODEBUG","query to 2. " + uri.getLastPathSegment());
				break;
			default:	break;
		}

		return mCursor;
	}


	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		Log.d("PELLODEBUG","CP> " + uri);
		// Implement this to handle requests to delete one or more rows.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public String getType(Uri uri) {
		Log.d("PELLODEBUG","CP> " + uri);
		// TODO: Implement this to handle requests for the MIME type of the data
		// at the given URI.
		throw new UnsupportedOperationException("Not yet implemented");
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		Log.d("PELLODEBUG","CP> insert " + uri);

		mCursor.addRow(new Object[]{
							values.getAsLong("_id"),
							values.getAsString("name"),
							values.getAsString("description")
						});
		Uri resultUri = Uri.parse("content://info.pello.android.contentprovider.provider.Students/students");
		return resultUri;

	}


	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		Log.d("PELLODEBUG","CP> " + uri);


		return 0;

	}
}

En el manifest debe quedar declarado el ContentProvider, y en mi caso en el activity principal he instanciado el ContentProvider y todo ha ido bien a la primera. Se pueden aplicar permisos y restricciones pero eso queda pendiente de probar. OJO, por defecto el permiso es READ WRITE cuando ofreces un ContentProvider.




    

    
        
            
                

                
            
        

        
            
            
        
    



El cliente

El cliente lo he simplificado al máximo ya que lo que me interesaba era mostrar un par de cosas, cómo hacer una query y como hacer un insert. La query la hace con un parámetro, en el provider lo distingue y lo saca sin más, luego hace la misma query, genera un cursor y lo retorna al cliente para que lo pinte. El cliente al final NO SABE que hay detrás del provider; el recibe un cursor.
En el caso del insert prepara una instancia especial para pasar los valores, una uri (que te puede servir o no) y a correr. El insert retorna un URI que identifica el nuevo registro. Ese uri lo generas con un ID para distinguir el nuevo registro ¿Qué de dónde sale el ID? tú mismo, si tienes un SQLite puedes hacer un autonumérico o como tú te lo montes. Al cliente le da igual.

package info.pello.android.contentproviderclient;


import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.TextView;

/**
 * Example of using a content provider.
 * @author Pello Xabier Altadill Izura
 * @greetz to any living real content provider.
 */
public class MainActivity extends Activity {

	private TextView textViewResult;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		textViewResult = (TextView) findViewById(R.id.textViewResult);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	/**
	 * called when button is clicked
	 * It will try to get data from Content Provider.
	 * @param v
	 */
	public void getData(View v) {
		Log.d("PELLODEBUG","Client> button pressed.");
		String result = "";
		String uriString = "content://info.pello.android.contentprovider.provider.Students/students/1";
		Uri uri = Uri.parse(uriString);

		// We finally make the request to the content provider
		Cursor cursor = getContentResolver().query(
			    uri,   // The content URI of the words table
			    new String[]{""},
			    "",                        // The columns to return for each row
			    new String[]{""},                     // Selection criteria
			    "");



		// We get results in the cursor instance.
		while (cursor.moveToNext()) {
			result += cursor.getLong(0) + ", ";
			result += cursor.getString(1) + ", ";
			result += cursor.getString(2) + "\n";
		}

		textViewResult.setText(result);
	}

	/**
	 * called when second button is clicked
	 * It will try to get data from Content Provider.
	 * @param v
	 */
	public void insertData (View v) {
		Log.d("PELLODEBUG","Client> button pressed.");
		String result = "";
		String uriString = "content://info.pello.android.contentprovider.provider.Students/students/insert";
		Uri uri = Uri.parse(uriString);
		ContentValues contentValues = new ContentValues();
		contentValues.put("_id", System.currentTimeMillis());
		contentValues.put("name","Example name");
		contentValues.put("description","Example Description");

		// We finally make the request to the content provider
		Uri resultUri = getContentResolver().insert(
			    uri,   // The content URI
			    contentValues
			    );
		Log.d("PELLODEBUG","Result Uri after insert: " + uri.toString());
		textViewResult.setText("Result uri created: " + uri.toString());
	}


}

Conviene ver la línea del manifest donde el cliente pide permiso para usar el content provider.




    

    ....
    ...

En la red tienes muchos más ejemplos, la mayoría suelen usar el tema de SQLite, en mi caso ńo quería más que mostrar una prueba de concepto.