Olá,
Esse sera o primeiro post que escrevo sobre android, estou começando a “brincar” com ele e resolvi compartilhar um pouco do pouco que descobri. ![]()
Nesse post vou dar uma ideia de como implementar o seu próprio ContentProvider.
SQLite :
Uma das grandes facilidades do android e o suporte ao SQLite.
As aplicações poderiam acessar diretamente o banco porem é uma boa pratica encapsular isso em um ContentProviders
de forma que isso fique acessível para sua aplicação ou qualquer outra que precise acessar esses dados.
ContentProviders :
O ContentProviders é uma parte muito importante da arquitetura de um sistema android.
São eles quem provem os dados para a aplicação.
Uri :
No Android a comunicação entre aplicações e providers é feita através de Uri.
Cada ContentProviders expõe URIs públicas que identificam o seu conjunto de dados
e controla uma ou mais tabelas e expondo uma URI diferente para cada uma delas.
Uma Uri segue o formato: “content://{authority}/{table}/parametroN”
{authority} é o “nome” do ContentProviders
{table} é a tabela que esta acessando
{parametroN} são os parâmetros esperados pelo provider.
EX :
Todos os registros da tabela (Diretório): content://br.com.flexria.jobsdroid.jobsdroidprovider/jobs
Um único registro com id 1 (Item) : content://br.com.flexria.jobsdroid.jobsdroidprovider/jobs/1
Escrevendo seu ContentProvider :
Bom, vajamos na pratica com isso funciona.
Para escrever seu ContentProviders vc precisa basicamente de quatro coisas.
- BaseColumns
- ContentProviders
- SQLiteOpenHelper
- Registrar o provider no AndroidManifest.xml :
ContentProvider :
E a classe que provera os dados a nossa aplicação,
A classe abstrata ContentProvider exige a implementação dos métodos :
* query(Uri, String[], String, String[], String)- Recuperar dados.
* insert(Uri, ContentValues) – Insere um novo registro.
* update(Uri, ContentValues, String, String[]) – Atualiza um registro existente.
* delete(Uri, String, String[]) – Deleta um registro.
* getType(Uri) – Obter o MIME type do dado.
Dentro desses métodos iremos implementar a logica que encapsula esses comportamentos.
Isso é valido para cada uma das tabelas que o provider esteja acessando,
Como aqui só tenho uma tabela e esse é apenas um exemplo didático esta tudo na mesma classe,
porem isso pode ficar meio confuso com quando temos varias tabelas e comportamentos diferentes.
Uma solução para isso seria implementar o Pattern Strategy para encapsular cada comportamento.
O exemplo é bem básico porem gostaria de salientar três pontos
* Método getType – Informa que tipo de dado esta sendo requisitado, se é um item ou o diretório.
* Variável mMatcher – Usado para verificar qual Uri esta sendo acessada, se é um item ou o diretório.
* Constante AUTHORITY – Mesma identificação do provider utilizado no AndroidManifest.xml
Bom, como uma Imagem diz mais que mil palavras ai vai o código:
public class JobsDroidProvider extends ContentProvider { /** * Provider Authority */ public static final String AUTHORITY = "br.com.flexria.jobsdroid.jobsdroidprovider"; /** * DataBase Name */ private static final String DATABASE_NAME = "jobsdroid.db"; /** * DataBase Version */ private static final int DATABASE_VERSION = 1; /** * match for directory of jobs. */ private static final int MATCH_JOBS = 1; /** * match for sub-directory of a single jobs. */ private static final int MATCH_JOBS_ID = 2; /** * Log Tag */ public static final String TAG = "JobsDroidProvider"; /** * DBHelper instance */ private DBHelper mHelper; /** * UriMatcher instance */ private static final UriMatcher mMatcher; /** * Projection Map instance */ private static HashMap<String, String> mProjection; ? static { mProjection = new HashMap<String, String>(); mProjection.put(Jobs._ID, Jobs._ID); mProjection.put(Jobs.NAME, Jobs.NAME); } ? static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(AUTHORITY, Jobs.TABLE, MATCH_JOBS); mMatcher.addURI(AUTHORITY, Jobs.TABLE+"/#", MATCH_JOBS_ID); } ? ? @Override public String getType(Uri uri) { ? switch (mMatcher.match(uri)) { case MATCH_JOBS: return Jobs.CONTENT_TYPE; case MATCH_JOBS_ID: return Jobs.CONTENT_ITEM; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } ? ? @Override public boolean onCreate() { mHelper = new DBHelper(getContext()); return (mHelper != null); } ? ? @Override public Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) { String type = this.getType(uri); Log.i(TAG, "query " + uri.toString()); ? Cursor cursor; SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); SQLiteDatabase database = mHelper.getReadableDatabase(); builder.setTables(Jobs.TABLE); builder.setProjectionMap(mProjection); ? if (type.equals(Jobs.CONTENT_ITEM)) { builder.appendWhere(this.whereItem(uri, selection)); } ? if (sortOrder == null || sortOrder.length() == 0) { sortOrder = Jobs.NAME; } ? cursor = builder.query(database, projection, selection, selectionArgs,null, null, sortOrder); cursor.setNotificationUri(getContext().getContentResolver(), uri); ? return cursor; } ? ? @Override public Uri insert(Uri uri, ContentValues values) { this.getType(uri); Log.i(TAG, "insert " + uri.toString()); ? SQLiteDatabase db = mHelper.getWritableDatabase(); long rowId = db.insert(Jobs.TABLE, Jobs.NAME, values); ? if (rowId > 0) { Uri itemUri = ContentUris.withAppendedId(Jobs.CONTENT_URI, rowId); getContext().getContentResolver().notifyChange(itemUri, null); return itemUri; } ? throw new RuntimeException("Failed to insert row into : " + uri); } ? ? @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { String type = this.getType(uri); SQLiteDatabase db = mHelper.getWritableDatabase(); ? Log.i(TAG, "update " + uri.toString()); ? if(type.equals(Jobs.CONTENT_ITEM)){ selection = this.whereItem(uri, selection); } ? int count = db.update(Jobs.TABLE, values, selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); ? return count; } ? ? @Override public int delete(Uri uri, String selection, String[] selectionArgs) { String type = this.getType(uri); SQLiteDatabase db = mHelper.getWritableDatabase(); ? Log.i(TAG, "delete " + uri.toString()); ? if(type.equals(Jobs.CONTENT_ITEM)){ selection = this.whereItem(uri, selection); } ? int count = db.delete(Jobs.TABLE, selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); return count; } ? private String whereItem(Uri uri, String selection) { if(this.getType(uri).equals(Jobs.CONTENT_ITEM)){ String where = Jobs._ID + " = " + uri.getPathSegments().get(1); if(!TextUtils.isEmpty(selection)){ return where + " AND (" + selection + ")" ; }else{ return where; } } return selection; } ? /** * Inner class Jobs */ public static final class Jobs implements BaseColumns { .... } ? /** * Inner class DBHelper */ private static class DBHelper extends SQLiteOpenHelper { .... } }
BaseColumns :
Inner class que representa a tabela do banco de dados,
estende a interface BaseColumns que possue as constantes _ID e _COUNT.
A classe Jobs possuí as constantes que representam as colunas da tabela e a Uri que a identifica
/** * Inner class Jobs */ public static final class Jobs implements BaseColumns { public static final Uri CONTENT_URI = Uri.parse("content://"+ JobsDroidProvider.AUTHORITY + "/" + Jobs.TABLE); ? /** * The MIME type of a directory. */ private static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.flexria." + Jobs.TABLE; ? ? /** * The MIME type of a single Jobs. */ private static final String CONTENT_ITEM = "vnd.android.cursor.item/vnd.flexria." + Jobs.TABLE; ? /** * Table name */ private static final String TABLE = "jobs"; ? /** * Column name */ public static final String NAME = "name"; }
SQLiteOpenHelper :
Inner class que é responsável pela criação e alteração do banco de dados,
A classe abstrata SQLiteOpenHelper exige a implementação dos métodos :
* onCreate() – Chamado quando o banco de dados for aberto pela primeira vez. Aqui que criaremos o banco de dados.
* onUpdate() – Chamado quando a versão do banco de dados muda. Quando uma nova versão da app for instalada.
Como nesse caso temos a primeira versão não tenho nada no método onUpdate.
/** * Inner class DBHelper */ private static class DBHelper extends SQLiteOpenHelper { private static final String SQL_CREATE_TASKS = "CREATE TABLE " + Jobs.TABLE + " (" + Jobs._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + Jobs.NAME + " TEXT" + " );"; ? DBHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } ? @Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_TASKS); } ? @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ? } }
AndroidManifest :
Depois disso podemos registrar o provider no AndroidManifest.xml
android:authorities="br.com.flexria.jobsdroid.jobsdroidprovider"
android:name=".JobsDroidProvider" />
Utilizando o Provider :
Feito isso ja podemos utilizar o provider na Activity,
quando utilizamos o método getContentResolver da Activity e passamos como parâmetro a Uri do conteúdo
o nosso provider é chamado e os métodos que implementamos podem ser utilizados.
EX :
* getContentResolver().query(JobsDroidProvider.Jobs.CONTENT_URI, null, null, null, null);
ContentValues values = new ContentValues(); values.put(JobsDroidProvider.Jobs.NAME, "Fabio B. Silva"); ? getContentResolver().query(JobsDroidProvider.Jobs.CONTENT_URI, null, null, null, null); getContentResolver().insert(JobsDroidProvider.Jobs.CONTENT_URI, values); ? ? Uri uri = ContentUris.withAppendedId(JobsDroidProvider.Jobs.CONTENT_URI, 1); // uri = content://br.com.flexria.jobsdroid.jobsdroidprovider/jobs/1 getContentResolver().update(uri, values,null,null); getContentResolver().delete(uri, null, null);
Em um próximo momento volto a falar sobre isso, mais por hora é só.
Espero que essa dicas possam ajudar aos que como eu estão começando com o android
Abraço e até a próxima….




