Pesquisar

Postagens populares

sexta-feira, 16 de dezembro de 2011

Content Provider: Compartilhando Informações no Android.

1.0 Compartilhando Informações no Android
Os Content Providers [1] (Provedor de Conteudo) são partes importantes em aplicações Android, são responsáveis por compartilhar dados entre aplicativos. A lógica é simples, como descrito na figura 1.1, um provedor de conteúdo funciona com uma camada entre as informações de um aplicativa e as diversas aplicações que desejam acessar suas informações. No exemplo uma aplicação de notas rápidas, acesa seus dados atraves de seu Contente Provider.


Figura 1.1 - Dinâmica de acesso com Content Provider.


Basicamente Content Providers funcionam como simples Adapters  para acessar bases de dados, sejam elas quais forem. Content Providers podem prover comunicação com os mais diversos tipos de  dados, sejam eles os tradicionais bancos de dados (SQLite) [2], imagens, vídeos , sons ou músicas, ou qualquer tipo de arquivo que for. Na figura 1.2 é exemplificado o esquema de interação de uma aplicação com seu Content Provider bem como outras APPs que usam sua base de dado.

Figura 1.2 - Interação de multipas aplicações a um Content Provider.

2.0 Acessando um Content Provider
Todo Content Provider expõe uma URI pública (Mascarado como um objeto URI) [1] pela qual qualquer aplicação pode manipular seus dados, como essas URI são públicas e devem ser únicas, é de boa prática seguir um padrão para garantir que nenhuma outra aplicação tenha um mesmo endereço de provedor de conteúdo que outra aplicação. Esse padrão implica na concatenação  do prefixo fixo que indica que estes dados são controlados por um Content Provider "content://" [3] mais o nome do pacote que compõe sua aplicação, seguido da palavra "provider" para indicar mais uma vez sua fonte e finalmente o  nome da aplicação a qual ela pertence, por fim, o nome da tabela ou arquivo que está sendo provido.

content://com.paad.provider.myapp/elements/5  

No caso de prover base de dados SQLite, é possivel especificar também qual linha da tupla deve ser acessada, como no exemplo abaixo onde a quinta linha da tabela elements está sendo referênciada.

content://com.paad.provider.myapp/elements/5

Como prova de conceito vamos analisar uma aplicação simples que acesa a URI pública de contatos, e exibe o nome e a lista de telefones de cada contato. Para fazer este serviço, apenas é necessário passar a URI pública "ContactsContract.Contacts.CONTENT_URI" [1][3] como parâmetro do método Activity.managedQuery() que igualmente ao método ContentResolver.query() servem para fazer consultas a Content Providers, ambos os métodos retornam um objeto do tipo Cursor. ContentResolver é uma interface provida para manipular dados, geralmente usado para consultas em Content Providers da própria aplicação. Por seguinte basta fazer as devidas interações e manipular os dados da forma que desejar.

// Obtém todos os contatos do celular  
     Cursor contacts = managedQuery(ContactsContract.Contacts.CONTENT_URI,  
         null, null, null, null);  
     // Se existe algum contato  
     if (contacts.moveToFirst()) {  
       String data = "<html><head><link href='css.css' rel='stylesheet' type='text/css' />"  
           + "<script type='text/javascript' src='jquery.js'></script>"  
           + "<script type='text/javascript' src='js.js'></script></head><body>";  
       data = data.concat("<ul>");  
       // Percorre cada contato  
       do {  
         // Iniciando a li (list item)  
         data = data.concat("<li class='contact_name'>");  
         // Id do contato  
         String contactID = contacts.getString(contacts  
             .getColumnIndex(ContactsContract.Contacts._ID));  
         // Nome do contato (FirstName + LastName)  
         String contactName = contacts.getString(contacts  
             .getColumnIndex(ContactsContract.Data.DISPLAY_NAME));  
         // exibir o nome do contato  
         data = data.concat(contactName);  
         data = data.concat("<div class='contact_numbers'><ul>");  
         // String que identifica se o contato atual tem um número  
         // cadastrado  
         String hasPhone = contacts  
             .getString(contacts  
                 .getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));  
         // Se tiver número irá buscar todos os números do contato  
         if (hasPhone.equals("1")) {  
           // Obtém todos os números do contato  
           Cursor phones = managedQuery(  
               ContactsContract.CommonDataKinds.Phone.CONTENT_URI,  
               null,  
               // Join com a tabela de contatos  
               ContactsContract.CommonDataKinds.Phone.CONTACT_ID  
                   + " = " + contactID, null, null);  
           // Percorre cada número do contato  
           while (phones.moveToNext()) {  
             String contactNumber = phones  
                 .getString(phones  
                     .getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));  
             data = data.concat("<li class='contact_number'>"  
                 + contactNumber + "</li>");  
           }  
           phones.close();  
         } else {  
           data = data  
               .concat("<li class='no_number'>----------------</li>");  
         }  
         data = data.concat("</ul></div></li>");  
       } while (contacts.moveToNext());  
       data = data.concat("</ul></body></html>");  
       // Exibe tudo na webView  
       webView.loadDataWithBaseURL("file:///android_asset/", data,  
           "text/html", "utf-8", null);  
       // Senão existir nenhum contato no telefone  
     } else {  
       webView.loadData("Nenhum contato encontrado", "text/html", "utf-8");  
     }  
     contacts.close();  

Para esta aplicação tivemos também que adicionar uma permisão aos contatos no Manifest.

Para melhor analisar este código vá a Github.com/zerokol/CP-App-1 e importe com o Eclipse IDE.

3.0 Criando seu próprio Content Provider
Para que sua aplicação compartilhe sua base de dados ou arquivos com um Content Provider três etapas devem ser seguidas.
  • Criar e configurar uma base de dados ou arquivos.
  • Extender a classe ContentProvider para acesso as informações e criar uma URI única.
  • Declarar o Content Privider no arquivo de Manifest

  • Para prova de conceito preparamos um aplicativo que serve para guardar as diversas senhas que um cliente possue, discriminando a qual domínio ela pertence, essa base de dados é disponibilizada em um Content Provider. Após configurar toda base de dados, foi preciso criar uma URI única a qual qualquer APP pode acessar-la.

    public static final String AUTHORITY = "br.com.bloum.contentprovider.app2.PasswordsContentProvider";

    Toda classe que instância a classe ContentProvider deve sobrescrever alguns métodos, são estes.

    public static final String AUTHORITY = "br.com.bloum.contentprovider.app2.PasswordsContentProvider";
    

    Toda classe que instância a classe ContentProvider deve sobrescrever alguns métodos, são estes.

    public class PasswordsContentProvider extends ContentProvider {  
       @Override  
       public int delete(Uri uri, String where, String[] whereArgs) {  
       }  
       @Override  
       public String getType(Uri uri) {  
       }  
       @Override  
       public Uri insert(Uri uri, ContentValues initialValues) {  
       }  
       @Override  
       public boolean onCreate() {  
       }  
       @Override  
       public Cursor query(Uri uri, String[] projection, String selection,  
           String[] selectionArgs, String sortOrder) {  
       }  
       @Override  
       public int update(Uri uri, ContentValues values, String where,  
       }  
       private static class DatabaseHelper extends SQLiteOpenHelper {  
         public DatabaseHelper(Context context) {  
         }  
         @Override  
         public void onCreate(SQLiteDatabase db) {  
         }  
         @Override  
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
         }  
       }  
     }  
    

    Ao implementar todos estes métodos, eles funcionarão com um Helper comum, no caso, criamos uma Inner Class que extende um SQLite Helper, e toda a classe funcionara como um Adapter, que ao contrario do que se fazia acesando diretamente o banco de dados, será acessado o Content Provider da aplicação.
    Por fim devemos declarar no arquivo de Manifest o nosso Content Provider, passando no atributo name o nome do nosso Privider, e em authorities a URI pública.

    Para melhor analisar este código vá a Github.com/zerokol/CP-App-2 e importe com o Eclipse IDE.

    4.0 Conclusão


    Content Providers foi uma excelente forma encontrada por os desenvolvedores do Android para trabalhar com o aproveitamento de informações, evitando que por exemplo você tenha que cadastrar todos os seus contatos para cada aplicativo que necessite usar estas informações. Manipular Content Providers difere-se muito pouco da forma de manipular dados quaisquer, a opção de compartilhar as informações de um aplicativo é de inteira responsabilidade de quem o desenvolve, pois apesar dos Content Providers proverem uma eficiente forma de re-uso de informação e minimiza o trabalho do cliente final, todo Content Provider está sujeito a alterações e até deleções.

    Um terceiro aplicativo está disponível para análise em Github.com/zerokol/CP-App-3 importe com o Eclipse IDE.

    Esta aplicação acessa o Content Provider da aplicação 2 e faz um update nas informações, no caso, encripta as senhas salvas.


    5.0 Referências
    1 Content Provider -  http://developer.android.com/guide/topics/providers/content-providers.html, Segunda, 18 de Julho de 2011
    2 SQLite Android - http://developer.android.com/reference/android/database/sqlite/package-summary.html, Segunda, 18 de Julho de 2011
    3 Reto Meier, Professional Android 2 Application Development, ISBN: 978-0-470-56552-0




    Licença Creative Commons

    O trabalho Aprendendo sobre Content Providers de Content Provider: Compartilhando Informações no Android. foi licenciado com uma Licença Creative Commons - Atribuição - CompartilhaIgual 3.0 Não Adaptada.
    Com base no trabalho disponível em www.zerokol.com.
    Podem estar disponíveis autorizações adicionais ao âmbito desta licença em zerokol.com.

    Nenhum comentário: