Caso prefira, você encontrará todo esse material, em inglês, no site do Developer Android. A tradução e comentários dos materiais eu faço livremente para ajudar a comunidade que fala português.

sexta-feira, 8 de abril de 2011

1 comentários

Services - Parte 03 - Bound Service

Um bound service é um servidor em uma interface cliente servidor. Um bound service permite que componentes, como atividades, façam o bind para o serviço, enviem requisições, recebam respostas e realizem comunicações entre processos (IPC). Um bound service tipicamente vive apenas enquanto ele serve outra aplicação e não roda em background indefinidamente (como outros tipos de serviço).

O básico

Um bound service é uma implementação de uma classe de serviço que permite que outras aplicações façam o bind para si mesmo e interajam umas com as outras. Para prover o binding para um serviço, você deve implementar o método callback onBind(). Esse método retorna um objeto IBinder que define uma interface de programação que o cliente usa para interagir com o serviço.

Um cliente pode fazer o bind para o serviço chamando bindService(). Quando ele o faz, ele deve prover uma implementação de ServiceConnection, que monitora a conexão com o serviço. O método bindService() retorna imediatamente sem um valor mas quando o sistema Android cria uma conexão entre o cliente e o serviço ele chama onServiceConnected() no ServiceConnection, para entregar o IBinder que o cliente usa para comunicar com o serviço.

Multiplos clientes podem conectar-se ao serviço de uma vez. Contudo, o sistema chama o método onBind() do serviço para retornar o IBinder apenas quando o primeiro cliente faz o bind. O sistema então entrega o mesmo IBinder para quaisquer clientes adicionais que façam o bind, sem chamar o onBind() novamente.

Quando o último cliente finaliza o bind com o cliente, o sistema destrói o serviço (a não ser que o serviço tenha também inicializado startService()).

Quando você implementa seu bound service, a parte mais importante é definir a interface que seu método callback onBind() retornará. Existem maneiras diferentes de definir a interface IBinder.

Criando um Bound Service

Quando criar um serviço que prove o binding, você deve prover um IBinder que prove a interface de programação que o cliente pode usar para interagir com o serviço. Existem três maneiras onde você pode definir a interface:

Extendendo a classe Binder
Se o seu serviço é privado e apenas disponível para sua aplicação e roda no mesmo processo que o cliente (o que é comum), você deve criar sua interface extendendo a classe IBinder e retornando uma instância dele a partir do onBind(). O cliente recebe o Binder e pode usá-lo para acessar diretamente os métodos públicos disponíveis ou na implementação do Binder ou mesmo do serviço.

Essa é a técnica preferida quando seu serviço é meramente um job que roda em background para sua própria aplicação. A única razão pela qual não se deve criar uma interface é se o seu serviço é usado por outra aplicação ou através de processos separados.

Usando um Messenger
Se você precisa que sua interface trabalhe através de diferentes processos você pode criar uma interface para o serviço com um Messenger. Dessa maneira o serviço define um Handler que responde a diferentes tipos de objetos Messenger. Esse Handler é a base para um Messenger que pode então compartilhar um IBinder com o cliente, permitindo ao cliente enviar comandos para o serviço usando os objetos message. Adicionalmente, o cliente pode definir um Messenger dele mesmo para que o serviço possa enviar mensagens de volta.

Essa é simplesmente a forma de comunicação usada em IPC já que o messenger enfilera todas as requisições em uma única thread para que você naõ tenha que fazer o design do serviço para ser thread-safe.

Usando AIDL
AIDL (Android Interface Definition Language) realiza todo o trabalho para decompor objetos em tipos primitivos que o sistema operacional pode entender e ordená-los através dos processos que realizam IPCs. A técnica anterior, usando messenger, é tem a AIDL como estrutura primária. Como mencionado acima, o Messenger cria uma fila de todas as requisições do cliente em uma única thread, para que o serviço receba requisições uma de cada vez. Se, contudo, você quiser que seu serviço manuseie multiploas requisições simultâneamente, então você pode usar AIDL diretamente. Nesse caso, seu serviço deve ser capaz de ser multi-threading e ser construído em modo thread-safe.

Para usar AIDL diretamente você deve criar um arquivo .aidl que define a interface de programação. As ferramentas de SDK do Android usam esse arquivo para gerar uma classe abstrata que implementa a interface e manuseia o IPC que você pode então extender dentro do seu serviço.

Nota: A maioria das aplicações não usam AIDL para criar um bound service, já que ela requer capacidades multithreading e podem resultar uma implementação mais complexa. Como tal, AIDL não é recomendado para a maioria das aplicações e esse documento não discutirá como usá-los em seu serviço. Mas, caso queira saber mais sobre a AIDL, vá até esse link.

Extendendo uma classe Binder

Se o serviço é usado apenas por sua aplicação local e não precisa trabalhar através de diversos processos, então você pode implementar sua própria classe Binder que prove a seu cliente acesso direto para métodos públicos no serviço.

Como fazê-lo:
  • No seu serviço, crie uma instância de Binder que:
    • Contém métodos públicos que o cliente pode chamar ou;
    • Retorna a instância do serviço corrente que tem métodos públicos que o cliente pode chamar ou;
    • Retorna uma instância de uma outra classe hospedada pelo serviço com métodos públicos que o cliente pode chamar.
  • Retorna essa instância de Binder através do onBind()
  • No cliente, recebe o Binder a partir de onServiceConnected() e faz chamadas para o bound service usando os métodos providos.
Por exemplo:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = new LocalBinder();
    // Random number generator
    private final Random mGenerator = new Random();


    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods
            return LocalService.this;
        }
    }


    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }


    /** method for clients */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}
O LocalBind prove o método getService() para os clientes capturarem uma instância de LocalService. Isso permite ao cliente chamada métodos públicos no serviço. Por exemplo, os clientes podem chamar getRandomNumber() a partir do serviço.

Aqui está uma atividade que faz um bind para o LocalService e chama getRandomNumber() quando um botão é clicado:

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }
    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute) */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call were something that might hang, then this request should
            // occur in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }
    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }
        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}


O exemplo acima mostra como o cliente faz o bind para o serviço usando uma implementação de ServiceConnection e o callback onServiceConnected.



Usando um Messenger

Se você precisa de um serviço para comunicar com processos remotos, então você pode usar um Messenger para prover uma interface para seu serviço. Essa técnica permite que você realize IPCs sem a necessidade de AIDL.

Aqui está um sumário de como usar um Messenger:

  • O serviço implementa um Handler que recebe um callback para cada chamada a partir de um cliente.
  • O Handler é usado para criar um objeto Messenger (que é uma referência para um Handler)
  • O Messenger cria um IBinder que o serviço retorna para o cliente a partir de onBind()
  • Clientes usam o IBinder para instanciar o Messenger (que referencia para o serviço do Handler), que o cliente usa para enviar objetos de Message para o serviço.
  • O serviço recebe cada Message em seu Handler - especificamente, no método handleMessage.
Dessa maneira, não existem métodos para o cliente chamar no serviço. Ao invés disso, o cliente entrega "mensagens" que o serviço receber em seu Handler.

Exemplo:

public class MessengerService extends Service {
    /** Command to the service to display a message */
    static final int MSG_SAY_HELLO = 1;


    /**
     * Handler of incoming messages from clients.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }


    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());


    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        return mMessenger.getBinder();
    }
}
Note que o handleMessage() no Handler é onde o serviço recebe o Message de entrada e decide o que fazer, baseado no membro what.

Tudo que o cliente precisa fazer é criar um Messenger baseado no IBinder retornado pelo serviço e enviar uma mensagem usando send(). Por exemplo, aqui abaixo está uma atividade simples para o serviço e que entrega a mensagem MSG_SAY_HELLO no serviço.

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;


    /** Flag indicating whether we have called bind on the service. */
    boolean mBound;


    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            mBound = true;
        }


        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            mService = null;
            mBound = false;
        }
    };


    public void sayHello(View v) {
        if (!mBound) return;
        // Create and send a message to the service, using a supported 'what' value
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }


    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }


    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}
Note que esse exemplo não mostra como o serviço pode responder ao cliente. Se você quer que o serviço responda então você precisa também criar um Messenger em um cliente. Então quando o cliente recebe o callback onServiceConnected(), ele envia uma mensagem para o serviço que inclui o Messenger do cliente no parâmetro replyTo no método send().

Fazendo o binding para o serviço

Componentes de aplicação podem fazer o bind chamando o bindService(). O sistema Android então chama o onBind() do serviço que retorna um IBinder para a interação com o serviço.

O binding é assincrono. bindService() retorna imediatamente e não retorna o IBinder para o cliente. Para receber o IBinder, o cliente deve criar uma instância de ServiceConnection e passá-lo no bindService(). O ServiceConnection inclui um método callback que o sistema chama para entregar o IBinder.

Nota: Apenas atividades, serviços e content providers podem fazer o bind para o serviço - você não pode fazer o bind para um serviço de um broadcast receiver.

Então, para fazer o bind de um serviço para seu cliente você deve:
  1. Implemente ServiceConnection.
    Sua implementação deve fazer o override de dois métodos callbacks:

    onServiceConnected()
    O sistema o chama para entregar o IBinder retornado pelo método onBind() do serviço.

    onServiceDisconnected()
    O sistema Android o chama quando a conexão para o serviço é inexperadamente perdido, como quando um serviço caiu ou foi morto. Esse método não é chamado quando o cliente termina o bind.
  2. Chame o bindService(), passando a implementação de ServiceConnection.
  3. Quando o sistema chama o método callback onServiceConnected(), você pode começar a fazer chamadas para o serviço, usando os métodos definidos pela interface.
  4. Para disconectar-se do serviço, chame unbindService().
    Quando seu cliente é destruído, ele vai finalizar o bind do serviço mas você deve sempre chamar o método unbindService() quando terminar a interação com o serviço ou quando sua atividade pausa para que o serviço possa finalizar enquanto não está sendo usado.
Por exemplo, o código abaixo conecta o cliente ao serviço criado acima extendendo a classe Binder de maneira que tudo o que é necessário é fazer um cast no IBinder retornado para a classe LocalService e requisitar uma instância de LocalService.
LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }
    // Called when the connection with the service disconnects unexpectedly
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};
Com esse ServiceConnection, o cliente pode fazer o bind para um serviço passando-o para o bindService().

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  • O primeiro parâmetro de bindService() é um Intent que explicitamente nomeia o serviço para efetuar o bind.
  • O segundo parâmetro é o objeto de ServiceConnection
  • O terceiro parâmetro é uma flag indicando as opções do binding. Ele deve usualmente ser do tipo BIND_AUTO_CREATE para criar o serviço se ele não já tiver sido criado. Outros possíveis valores são BIND_DEBUG_UNBIND e BIND_NOT_FOREGROUND ou 0 para nada.

quinta-feira, 7 de abril de 2011

0 comentários

Services - Parte 02 - Started Service

Criando um Started Service

Um started service é um que outro componente inicia chamando startService(), resultando numa chamada para o método onStartCommand() do serviço.

Quando um serviço é iniciado, ele tem um ciclo de vida que é independente da do componente que o iniciou e o serviço pode rodar em background indefinidamente, mesmo que o componente que o iniciou seja destruído. Como tal, o serviço deverá ser parado apenas se o seu trabalho está concluído chamando stopSelf() ou outro componente pode parálo chamando stopService().

Um componente de aplicação como uma atividade pode iniciar o serviço chamando startService() e passando um Intent que especifica o serviço e inclui quaisquer dados para o serviço usar. O serviço recebe esse Intent no onStartCommand().

Suponha que uma atividade precise salvar dados em um banco de dados online. A atividade pode iniciar um serviço e entregar dados para salvar passando o intent para o startService(). O serviço recebe o intent no onStartCommand(), conecta-se à internet e faz a transação com o banco de dados. Quando a transação está realizada, o serviço pára e depois se destrói.

Tradicionalmente, existem duas classes que você pode extender para criar um started service.

Service
É a classe base para todos os serviços. Quando você extende essa classe é importante que você crie uma nova thread na qual fará todo o trabalho do service já que o serviço usa a thread principal de sua aplicação por default e que poderia diminuir sobremaneira a performance de quaisquer atividades que sua aplicação esteja rodando.

IntentService
É a subclasse de Service que usa um worker thread para manusear todos as requisições iniciais, um de cada vez. Essa é a melhor opção se você não quer que o seu serviço manuseie multiplas requisições simultâneamente. Todo que você precisa fazer é implementar onHandleIntent() que recebe o intent para cada requisição inicial para que você possa fazer o trabalho de background.

Extendendo a classe IntentService

Como muitos dos serviços iniciados não precisam manusear multiplas requisições simultaneamente (o que poderia ser muito perigoso para um cenário multi-threading), é provavelmente melhor que você implemente seu serviço usando a classe IntentService.

Um IntentService faz o seguinte:
  • Cria uma classe padrão worker thread que executa todos os intents recebidos no onStartCommand() separado da thread principal da aplicação.
  • Cria uma fila de trabalhos que passam um intent a cada vez para a sua implementação de onHandleIntent() para que você nunca precise se preocupar com multi-threading.
  • Pára o serviço após todos as requisições iniciais terem sido processadas para que você nunca precise chamada stopSelf().
  • Prove a implementação padrão de onBind() que retorna null
  • Prove uma implementação padrão de onStartCommand() que envia o intent para a fila de trabalhos e então para sua implementação de onHandleIntent().
Tudo isso adicionado ao fato de que tudo que você precisa implementar é o método onHandleIntent() para fazer o trabalho provido pelo cliente (apesar de você precisar prover um pequeno construtor para o serviço).

Aqui está um exemplo de implementação de IntentService:
public class HelloIntentService extends IntentService {


  /**
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }


  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}

Isso é tudo o que é necessário: um construtor e uma implementação de onHandleIntent().

Se você decidir também por fazer um override de outro método callback como onCreate(), onStartCommand() ou onDestroy(), esteja certo de chamar a super implementação para que o IntentService possa processar corretamente a vida do worker thread.

Por exemplo, o onStartCommand() deve retornar para a implementação padrão (que é como o intent é entregue para o onHandleIntent()):
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    return super.onStartCommand(intent,flags,startId);
}
Além de onHandleIntent(), o único método a partir do qual você não precisará chamar a super classe é o onBind() (mas você precisará apenas implementar isso se o seu serviço permitir binding).

Extendendo a classe Service

Como visto na seção anterior, usar o IntentService faz sua implementação de um started service muito simples. Mas e se você quiser que seu serviço faça uso de multi-threading(ao invés de processar requisições iniciais através de uma fila de trabalho), então você pode extender a classe Service para manusear cada intent.

Para efeito de comparação, o exemplo seguinte é uma implementação de uma classe Service que realiza o trabalho exato como o do exemplo acima que usou o IntentService. Ou seja, para cada requisição inicial, ele usa um worker thread para realizar o trabalho e processar apenas uma requisição a cada vez.
public class HelloService extends Service {
  private Looper mServiceLooper;
  private ServiceHandler mServiceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          long endTime = System.currentTimeMillis() + 5*1000;
          while (System.currentTimeMillis() < endTime) {
              synchronized (this) {
                  try {
                      wait(endTime - System.currentTimeMillis());
                  } catch (Exception e) {
                  }
              }
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service.  Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block.  We also make it
    // background priority so CPU-intensive work will not disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();
   
    // Get the HandlerThread's Looper and use it for our Handler
    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = mServiceHandler.obtainMessage();
      msg.arg1 = startId;
      mServiceHandler.sendMessage(msg);
     
      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }
 
  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}
Como pode ver, acima há muito mais código do que havia quando usamos o IntentService.

Mas como você tem de manusear cada chamada para onStartCommand() você mesmo, você pode realizar multiplas requisições simultâneamente. Não é o que o exemplo faz mas se é o que você quer, então você pode criar uma nova thread para cada requisição e rodá-los separadamente (ao invés de esperar a thread anterior terminar para a sua iniciar).

Note que o onStartCommand() deve retornar um inteiro. Um inteiro é um valor que descreve como o sistema deve continuar o serviço no evento deste matá-lo. O valor retornado de onStartCommand() deve ser uma das seguintes constantes:

START_NOT_STICKY
START_STICKY
START_REDELIVER_INTENT

Iniciando um serviço

Você pode iniciar um serviço a partir de uma atividade ou outro componente de aplicação passando um Intent para o startService(). O sistema Android chama o método onStartCommand() do serviço e passa-o para o Intent. Você nunca deverá chamar onStartCommand() diretamente. Deixe o sistema Android fazê-lo por você.

Por exemplo, uma atividade pode iniciar o serviço como abaixo:

Intent intent = new Intent(this, HelloService.class);
startService(intent);

O método startService retorna imediaamente e o sistema Android chama o método onStartCommand() do serviço. Se o serviço não está sendo rodado, o sistema primeiro chama onCreate() e então onStartCommand().

Se o serviço não prove binding, o intent entregue com o startService() é a única maneira de comunicação entre o componente de aplicação e o serviço. Contudo, se você quer que o serviço envie um resultado de volta, então o cliente que inicia o serviço pode criar um PendingIntent para um broadcast (com getBroadcast()) e entregá-lo ao serviço no Intent que inicia o serviço. O serviço pode então usar o broadcast para entregar o resultado.

Multiplas requisições para iniciar um serviço resultam em multiplas chamadas correspondentes para o onStartCommand() do serviço. Mas, apenas uma requisição para parar o serviço (com stopSelf() ou stopService()) é requerida.

Parando um serviço

Um started service deve gerenciar seu ciclo de vida. Ou seja, o sistema não pára ou destrói o serviço a menos que ele precise recuperar memória. Então, o serviço deve parar a si mesmo chamando stopSelf() ou outro componente pode pará-lo chamando stopService().

Uma vez que seja requisitado a parada com o stopSelf() ou stopService(), o sistema destrói o serviço assim que possível.

quarta-feira, 6 de abril de 2011

0 comentários

Services - Parte 01

Um serviço é um componente da aplicação que pode realizar operações de longa duração em modo background e não prove interface de usuário. Outro componente de aplicação pode iniciar o serviço e ele vai continuar rodando no background mesmo que o usuário mude para outra aplicação. Adicionalmente, um componente pode fazer o bind para um serviço para interagir com ele e inclusive realizar processos de comunicação entre processo (IPC na sigla em inglês que significa Inter-Process communication). Por exemplo, um serviço pode querer manusear transações de rede, tocar música, fazer uma troca de arquivo (processo de I/O) ou interagir com um content provider, tudo em modo background.

Um serviço essencialmente tem duas formas:

Started
Um serviço está iniciado quando um componente de aplicação (como uma atividade) inicia-o chamando startService(). Uma vez iniciado, o serviço pode rodar em modo background indefinidamente, mesmo que o componente que o iniciou seja destruído. Usualmente, um serviço iniciado realiza uma tarefa única e não retorna o resultado para quem o chamou. Por exemplo, você pode fazer o download ou upload de arquivos em uma rede. Quando a operação for terminada, o serviço termina sem a necessidade de interação do usuário.

Bound
Um serviço está bound quando um componente de aplicação faz um bind para ele chamando bindService(). Um serviço bound oferece uma interface cliente servidor que permite ao componente interagir com o serviço, enviar requisições, obter resulados e mesmo fazer alguns processos com comunicação entre processos (IPC). Um serviço bound roda apenas enquanto o componente da aplicação estiver ligado a ele. Multiplos componentes podem ser ligados a um serviço ao mesmo tempo mas quando todos eles se desligarem dele, o serviço será destruído.

Apesar dessa documentação geralmente discutir esses dois tipos de serviço separadamente, seu serviço pode trabalhar das duas formas - ele pode ser inicializado (para rodar indefinidamente) e também permitir que seja feito um bind para ele. É simplesmente uma questão de se você 
 implementará dois métodos callback(): onStartCommand() para permitir que os componentes o inicializem e onBind() para permitir que seja feito um bind para ele.

Independente de se a sua aplicação foi iniciada, ligada a uma atividade ou ambas maneiras, qualquer componente de aplicação pode usar o serviço (mesmo de uma aplicação separada) da mesma maneira que qualquer componente pode usar uma atividade - quando iniciada por um Intent. Contudo, você pode declarar o serviço como privado no arquivo de manifesto e bloquear o acesso a ele a partir de outras aplicações.

Cuidado: Um serviço roda na thread principal do processo hospedeiro - o serviço não cria seu próprio thread e não roda em um processo separado (a não ser que você especifique em contrário). Isso significa que se seu serviço está usando muita CPU ou bloqueando operações (como playback de MP3 ou serviços de rede), você deve criar uma nova thread dentro do serviço para fazer o trabalho. Usando uma thread separada, você reduzirá o risco de erros do tipo Application Not Responding (ANR) e a thread principal da aplicação pode remanescer dedicada exclusivamente com a interação do usuário em suas atividades.

Devo usar um serviço ou uma thread?

Um serviço é simplesmente um componente que roda em background mesmo quando o usuário não está interagindo com a aplicação. Ou seja, você só deverá criar um serviço se realmente necessitar.

Se você precisar realizar trabalhos fora da thread principal, mas apenas enquanto o usuário está interagindo com sua aplicação, então você deve provavelmente criar uma nova thread e não um serviço. Por exemplo, se você quer tocar música mas apenas enquanto a atividade estiver rodando, você deve criar uma thread em onCreate(), iniciá-la com onStart() e então parar usando onStop(). Considere também usar AsyncTask ou HandlerThread ao invés da classe tradicional Thread. Quando falarmos de processos e threads esse conceito ficará mais claro.

Lembre-se que se você usar um serviço ele vai rodar na thread principal da sua aplicação por default, então você deve criar uma nova thread dentro do serviço se ele realiza operações intensivas e que bloqueiam outras operações.

O básico

Para criar um serviço, você deve criar uma subclasse de Service (ou uma de suas subclasses existentes). Em sua implementação, você precisará fazer o override de algums métodos callback para manusear aspectos chaves de seu ciclo de vida e prover um mecanismo para componentes fazerem o bind a ele, se apropriado. Os mais importantes métodos callback que você deverá fazer o override são:

onStartCommand()
O sistema chama esse método quando outro componente, como uma atividade, faz a requisição para que o serviço seja iniciado chamando startService(). Uma vez que esse método é executado, o serviço é iniciado e pode roda em background indefinidamente. Se você implementar isso é sua responsabilidade parar o serviço uma vez que o trabalho está feito, chamando stopSelf() ou stopService(). Se você quiser prover apenas o binding, não é necessário implementar esse método.
 
onBind()
O sistema chama o método quando outro componente quer fazer o bind com o serviço (para realizar um Remote Procedure Call - RPC) chamando bindService(). Em sua implementação desse método você deve prover uma interface que os clientes usarão para comunicar com o serviço, retornando um IBinder. Você deve sempre implementar esse método mas se você não quer permitir o binding, então deve retornar null.

onCreate()
O sistema chama esse método quando o serviço é criado pela primeira vez, para realizar procedimentos de configuração iniciais (antes mesmo de chamar ou o onStartCommand() ou onBind()). Se o serviço já está rodando, esse método não é chamado.

onDestroy()
O sistema chama esse método quando o serviço não está mais sendo usado e está sendo destruído. Seu serviço deve implementar esse método para limpar os recursos como threads, listeners registrados, receivers, etc. Esse é a última chamada que o serviço recebe.

Se um componente inicia um serviço chamando startService() (que resulta em uma chamada para onStartCommand()), então o serviço permanece rodando até ser parado com stopSelf() ou outro componente chamando stopService().

Se um componente chama o bindService() para criar o serviço (e o onStartCommand() não foi chamado) então o serviço roda apenas enquanto o componente estiver ligado a ele. Uma vez que o serviço foi desligado de todos os seus clientes, o sistema o destrói.

O sistema Android vai forçar a parada de um serviço apenas quando a memória do dispositivo estiver muito baixa e ele precisar recuperar recursos para uma atividade que está em foco para o usuário. Se o serviço está ligado a uma atividade que tem o foco, então é menos provável que ele seja morto e se o serviço está declarado para rodar em tela (item discutido em posts futuros), então é bem provável que ele nunca seja morto. De outra maneira, se o serviço foi iniciado e está rodando um processo longo, então o sistema vai diminuir sua posição na lista de processos em background e pode se tornar sucetível a ser morto pelo sistema.

Declarando um serviço no manifesto

Como uma atividade (e outros componentes), você deve declarar todos os serviços no arquivo de manifesto da aplicação.

Para declarar seu serviço, adicione um elemento <service> como filho de um elemento <application>.

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

Existem outros atributos que você pode incluir no elemento <service> que definem propriedades como permissões requeridas para a inicialização de tal serviço e o processo no qual o serviço pode rodar.

Assim como uma atividade, um serviço pode definir intent filters que permitem outros componentes chamarem o serviço usando intents implicitos. Declarando intent filters, os componentes de qualquer aplicação instalada no dispositivo podem potencialmente iniciar seu serviço se seu serviço declara um intent filter que é igual ao intent que outra aplicação passou para o startService().

Se você planeja usar seu serviço apenas de modo local (outras aplicações não podem usá-lo), então não é necessário (e você não deveria) suprir quaisquer intent filters. Sem um intent filter você deve iniciar o serviço usando um intent que explicitamente nomeie a classe do serviço.

Adicionalmente, você pode assegurar que seu serviço seja privado para sua aplicação apenas incluindo o atributo android:exported e setá-lo como false. Isso é efetivo mesmo que o serviço forneça intent filters.

terça-feira, 5 de abril de 2011

0 comentários

Loaders

Introduzido no Android 3.0, os loaders fazem ser mais fáceis o carregamento de dados de maneira assincrona nas atividades e fragmentos. Loaders tem as seguintes características:
  • Eles estão disponíveis em cada atividade e fragmento.
  • Eles provêem carregamento de dados de maneira assincrona.
  • Eles monitoram a fonte de seus dados e entregam novos resultados quando o conteúdo é modificado no back end.
  • Eles automaticamente reconectam para o último cursor do loader quando estão sendo recriados após uma mudança de configuração. Ou seja, eles não precisam fazer a chamada para recuperar os dados novamente (i.e. não é necessário passar novamente a query para o provider).
Sumário da API do Loader

Essas são as múltiplas classes e interfaces que estão involvidas no uso dos loaders em uma aplicação. Eles são listados nessa tabela:

Load manager
Uma classe abstrata associada com uma atividade ou fragmento para gerenciar uma ou mais intâncias de Loader. Isso ajuda uma aplicação no gerenciamento de operações que consomem tempo em conjunção com o ciclo de vida da atividade ou fragmento; o uso mais comum disso é o CursorLoader, contudo aplicações são livres para escrever seus proprios loaders para carregamento de outros tipos de dados.

Existe apenas um LoaderManager por atividade ou fragmento. Mas um LoaderManager pode ter múltiplos loaders.

LoaderManager.LoaderCallbacks
Uma interface de callback para um cliente para interagir com o LoaderManager. Por exemplo, você usa o método onCreateLoader() para criar um novo loader.

Loader
Uma classe abstrata que realiza o carregamento assincrono de dados. Ele é a classe base para o loader. Você tipicamente usará o CursorLoader mas você pode implementar sua própria subclasse. Enquanto os loaders estão ativos eles devem monitorar a fonte de seus dados e entregar novos resultados quando o conteúdo é mudado no back end.

AsyncTaskLoader
Loader abstrato que prove um AsyncTask para fazer o trabalho.

CursorLoader
Uma subclasse de AsyncTaskLoader que envia o query para o ContentResolver e retorna um Cursor. Essa classe implementa o protocolo Loader em uma forma padrão para fazer o envio de queries em cursores, contruindo o AsyncTaskLoader para realizar o query do cursor num thread de background para que ele não bloqueie a interface. Usando esse loader é a melhor maneira de capturar dados de maneira assincrona de um ContentProvider, ao invés de realizar um query gerenciado através do fragmento ou API da atividade.

As classes e interfaces acima listadas são componentes essenciais que você usará para implementar um loader para sua aplicação. Você não precisará de todos eles para cada loader que você criar mas você sempre necessitará de uma referência para o LoaderManager para inicializar o load e implementar uma classe Loader como CursorLoader.

Usando Loaders em sua aplicação

Uma aplicação que usa loaders tipicamente incluirá o seguinte:
  • Uma atividade ou fragmento
  • Uma instância do LoaderManager
  • Um CursorLoader para carregar dados guardados por um ContentProvider. Alternativamente, você pode implementar sua própria subclasse de Loader ou AsyncTaskLoader para carregar dados de outra fonte.
  • Uma implementação de LoaderManager.LoaderCallbacks. Isso é necessário para que seja criado novos loaders e gerenciar suas referências para loaders existentes.
  • Uma forma de mostrar os dados do loader, como um SimpleCursorAdapter.
  • Um data source, como um ContentProvider quando usando o CursorLoader.
Inicializando o Loader

O LoaderManager gerencia um ou mais instâncias de Loader dentro da atividade ou fragmento. Existe apenas um LoaderManager por atividade ou fragmento.

Você tipicamente inicializará o Loader dentro do onCreate() da atividade ou dentro do onActivityCreated() dentro do fragmento. Você faz isso da seguinte maneira:

// Prepare the loader.  Either re-connect with an existing one,
// or start a new one.
getLoaderManager().initLoader(0, null, this);

O initLoader() recebe os seguintes parâmetros:
  • Um ID único que identifica o loader. No exemplo, o ID é 0.
  • Argumentos opcionais para suprir o loader na construção deste (null é um exemplo).
  • Uma implementação de LoaderManager.LoaderCallbacks à qual o LoaderManager chamará para reportar eventos de loader. No exemplo, a classe local implementa a interface LoaderManager.LoaderCallbacks e passa a referência de si mesmo, a palavra chave this.
O chamada ao initLoader assegura que o loader está inicializado e ativo. São duas as respostas a essa chamada:
  • Se o loader especificado já existe, o último loader criado será reutilizado.
  • Se o loader especificado não existe, o initLoader inicia o método onCreateLoader dentro de LoaderManager.LoaderCallbacks. Aqui é onde você implementa código para instanciar e retornar um novo loader.
Em ambos os casos, a implementação dada de LoaderManager.LoaderCallbacks é associada ao loader e será chamado quando o estado do loader for mudado. Se em algum ponto da chamada o método que faz a chamada está em estado inicializado e o loader requisitado já existe e tem dados, então o sistema chama onLoadFinished() imediatamente (duranto o initLoader()) portanto, esteja preparado para tal cenário.

Note que o método initLoader retornar o Loader que é criado mas não necessita capturar uma referência para ele. O LoaderManager gerencia a vida do loader automaticamente. O LoaderManager inicia e pára o carregamento quando necessário e mantém o estado do loader e de seus conteúdos associados. Como se pode supor, você raramente interagirá com os loaders diretamente. Você vai, mais comumente, usar os métodos dentro de LoaderManager.LoaderCallbacks para intervir em um processo de carregamento quando um evento particular ocorrer.

Reiniciando um Loader

Quando você usa o initLoader(), como mostrado acima, ele usa um loader existente com o ID especificado nele. Se não existe o ID, ele o cria. Mas algumas vezes você poderá querer descartar seus dados antigos e começar tudo de novo.

Para descartar seus dados antigos, use restartLoader(). Por exemplo, essa implementação de SearchView.OnQueryTextListener reinicia o loader quando a query do usuário é modificada. O loader precisa ser reiniciado para que possa ser usado para recriar os dados já que o filtro foi modificado.

public boolean onQueryTextChanged(String newText) {
    // Called when the action bar search text has changed.  Update
    // the search filter, and restart the loader to do a new query
    // with this filter.
    mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
    getLoaderManager().restartLoader(0, null, this);
    return true;
}

Usando os callbacks do LoaderManager

O LoaderManager.LoaderCallbacks é uma interface de callback que permite ao usuário interagir com o LoaderManager.

Loaders, em particular o CursorLoader, experam reter seus dados após serem parados. Isso permite à aplicação manter seus dados mesmo quando os métodos onStop() e onStart() são chamados na atividade ou fragmento, para que quando o usuário retorne à aplicação eles não tenham de esperar que os dados sejam novamente carregados. Você usa os métodos do LoaderManager.LoaderCallbacks quando precisa saber quando criar um novo loader e para dizer à aplicação quando é hora de parar de usar os dados do Loader.

O LoaderManager.LoaderCallbacks incluem os métodos:
  • OnCreateLoader() - Instancia e retorna um novo Loader para um dado ID.
  • OnLoadFinished() - Chamado quando um loader criado préviamente tem seu carregamento finalizado.
  • OnLoaderReset() - Chamado quando um loader criado préviamente está sendo resetado, fazendo com que seus dados fiquem indisponíveis.
 Existem exemplos diferentes em ApiDemos que ilustram como usar os loaders.

segunda-feira, 4 de abril de 2011

0 comentários

Interfaces de Usuários - Fragmentos Parte 03

Gerenciamento do ciclo de vida do Fragmento

Gerenciar o ciclo de vida do fragmento é muito parecido com o gerenciamento do ciclo de vida da atividade. Assim como a atividade, um fragmento pode existir em três estados distintos:

Resumed
O fragmento é visível na atividade sendo executada

Paused
Outra atividade está na tela e tem o foco mas uma atividade e seus fragmentos ainda estão vivos e visíveis (a atividade que está na tela está parcialmente transparente ou não cobre toda a extensão da tela).

Stopped
O fragmento não está visível. Ou a atividade hospedeira parou ou o fragmento foi removido da atividade mas adicionada ao back stack. O fragmento parado está vivo mas não está mais visível para o usuário e será morto caso a atividade seja morta.

Também como a atividade, você pode reter o estado de um fragmento usando um Bundle no caso do processo da atividade ser morto você precisar restaurar o estado do fragmento quando a atividade for restaurada. Você pode salvar o estado durando o callback onSaveInstanceState() do fragmento e restaurá-lo durante ou o onCreate(), onCreateView() ou onActivityCreated().

A mais significativa diferença nos ciclos de vida da atividade e do fragmento é como elas são guardadas em seus respectivos back stacks. Uma atividade é guardado dentro do back stack de atividades que são gerenciadas pelo sistema quando ela é parada por padrão. Contudo, um fragmento é guardado no back stack gerenciado pela atividade hospedeira apenas quando você explicitamente faz a requisição para que a instância seja salva chamando o addToBackStack() durante a transação que remove o fragmento.

Coordenando com o ciclo de vida da atividade

O ciclo de vida da atividade na qual o fragmento vive afeta diretamente o ciclo de vida do fragmento. Por exemplo, quando uma atividade recebe o onPause(), cada fragmento da atividade também receber o onPause().

Fragmentos tem alguns callbacks extras no ciclo de vida que gerenciam interações únicas com a atividade para realizar ações como construir e destruir interfaces. Esses métodos callback adicionais são:

onAttach()
Chamado quando o fragmento foi associado com uma atividade.

onCreateView()
Chamado para criar uma hierarquia de view associada com o fragmento.

onActivityCreated()
Chamado quando o método onCreate() da atividade retorna.

onDestroyView()
Chamado quando a hierarquia da view associada com o fragmento está sendo removido.

onDetach()
Chamado quando o fragmento está sendo dissociado da atividade.

domingo, 3 de abril de 2011

1 comentários

Interfaces de Usuários - Fragmentos Parte 02

Gerenciando Fragmentos


Para gerenciar os fragmentos em sua atividade, você precisa usar o FragmentManager. Para tal, chame getFragmentManager() a partir de sua atividade.

Algumas coisas que você pode fazer com o FragmentManager incluem:

  • Conseguir ver os fragmentos que existem na atividade com o findFragmentById ou findFragmentByTag.
  • Retirar fragmentos do back stack usando o popBackStack() (que simula o comando BACK)
  • Registrar um listener para mudanças no back stack com o addOnBackStackChangedListener().
Executando transações de fragmento

Uma grande característica de usar fragmentos em sua atividade é a habilidade de adicionar, remover, substituir e realizar outras ações com eles em resposta à interação do usuário. Cada conjunto de mudanças que você faz commit para a atividade é chamada de transação e você pode realizá-las usando as APIs em FragmentTransaction. Você também pode salvar cadas transação para um back stack gerenciado pela atividade, permitindo ao usuário navegar pelas mudanças que foram sendo feitas no fragmento.

Você pode conseguir uma instância de FragmentTransaction a partir do FragmentManager como mostrado no código abaixo:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Cada transação é um conjunto de mudanças que você pode realizar ao mesmo tempo. Você pode realizar todas as mudanças que quer para uma dada transação usando métodos como add(), remove() e replace(). Então, persistir as modificações usando commit().

Antes de cada commit, contudo, você pode querer chamar addToBackStack() para adicionar a transação para o back stack de transações de fragmento. Esse back stack é gerenciado pela atividade e permite ao usuário voltar para estados de fragmento anteriores pressionando o botão BACK.

Por exemplo, aqui está como substituir um fragmento por outro e preservar o estado anterior no back stack:
// Cria novo fragmento e transação
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();


// Substitui quaisquer views do tipo fragment_containter com esse fragmento
// e adiciona uma transação ao back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);


// Faz o commit da transação
transaction.commit();
Nesse exemplo, newFragment substitui os fragmentos (caso haja algum) que está no container do layout ientificado pelo ID de R.id.fragment_container . Chamando addToBackStack(), a transação que vai ser substituída é salva no back stack para que possa ser chamada de volta quando o usuário pressiona BACK.

Se você adicionar múltiplas mudanças para a transação (como outro add() ou remove()) e chamar addToBackStack(), então todas as mudanças aplicadas antes de chamar o commit() serão adicionadas para o back stack como uma transação única e o BACK poderá voltar a ele.

A maneira com a qual você adicionará mudanças para o FragmentTransaction não importa, exceto que:
  • Você deve chama commit() no final da transação
  • Se você está adicionando multiplos fragmentos para um mesmo container, então a maneira com a qual são adicionadas determinam a ordem que aparecerão na hierarquia da View.
Se você não chama addToBackStack() quando você faz uma transação que remove um fragmento, então o fragmento é destruido quando a transação faz o commit() e o usuário não pode mais navegar de volta a ele. Mas, se você chama o addToBackStack() quando está removendo um fragmento, então o fragmento é parado e poderá ser reinicializado se o usuário navegar de volta para ele.

Dica: Para cada transação de fragmento, você pode aplicar animações de transição, chamando setTransition() antes de fazer o commit()

Chamar o commit() não realiza a transação imediatamente. Ao invés disso, ele agenda a realização dela na thread da interface da atividade assim que a thread estiver disponível para tal. Se necessário você poderá chamar executePendingTransactions() para executar imediatamente as transações submetidas para o commit(). Fazendo isso usualmente não será necessário a não ser que exista uma dependência entre os trabalhos em outras threads.

Cuidado: Você pode fazer o commit() apenas antes da atividade salvar o seu estado. Se você tentar fazer um commit após esse ponto, uma exceção ocorrerá. Isso acontece pois o estado após o commit pode ser perdido se a atividade necessita ser restaurada. Para situações nas quais você pode perder o commit, use commitAllowingStateLoss().

Comunicando com a Atividade

Apesar do fragmento ser implementado como um objeto que é independente de uma atividade e que pode ser usada dentro de multiplas atividades, uma dada instância de um fragmento é diretamente relacionado à atividade que a contém.

Especificamente, o fragmento pode acessar a instância da Atividade com getActivity() e realizar tarefas facilmente como encontrar uma view no layout da atividade:
View listView = getActivity().findViewById(R.id.list);
Outrossim, sua atividade pode chamar métodos no fragmento adquirindo uma referência para o fragmento a partir do FragmentManager, usando findFragmentById() ou findFragmentByTag(). Por exemplo:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
Criando eventos de callback para a atividade


Em alguns casos, você pode precisar que um fragmento compartilhe eventos com a atividade. Uma boa forma de fazer isso é definir uma interface de callback dentro do fragmento e requerir que a atividade que a hospeda a implemente. Quando a atividade recebe um callback através da interface, ele pode compartilhar as informações com outros fragmentos no layout se necessário.


Por exemplo, se uma aplicação de notícias tem dois fragmentos em uma atividade - uma que mostra uma lista de artigos (fragmento A) e outra que mostra o artigo (fragmento B) - então o fragmento A deve dizer à atividade quando um item da lista é seleciona para que então possa dizer para o fragmento B para mostrá-lo na tela. Nesse caso, a interface OnArtigcleSelectedListener é declarada dentro do fragmento A.
public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}
Então a atividade que hospeda o fragmento implementa a interface acima e faz um override em onArticleSelected() para notificar o fragmento B de um evento no fragmento A. Para assegurar que a atividade hospedeira implemente a interface, o método onAttach() do fragmento A instancia o OnArticleSelectedListener fazendo o casting da atividade que é passada dentro do onAttach().
public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            mListener = (OnArticleSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}
Se a atividade não foi implementada na interface, então o fragmento dá um throw para ClassCastException. Se tiver sucesso, o membro mListener guarda uma referência para a implementação da atividade de OnArticleSelectedListener, para que o fragmento A possa compartilhar eventos com a atividade chamando métodos definidos pela interface OnArticleSelectedListener. Por exemplo, se o fragmento A é uma extensão de ListFragment, cada vez que o usuário clicar num item de lista, o sistema chama onListItemClick() no fragmento, que então chama onArticleSelected() para compartilhar o evento com a atividade.
public static class FragmentA extends ListFragment {
    OnArticleSelectedListener mListener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        mListener.onArticleSelected(noteUri);
    }
    ...
}
O parâmetro id qe é passado para onListItemClick é o ID da linha que é clicada e que é usada pela atividade para a captura do artigo no ContentProvider da aplicação.


Adicionando itens para a Action Bar


Seus fragmentos podem incluir itens de menu para a Options Menu da atividade (e, consequentemente, para a Action Bar) implementando o onCreateOptionsMenu(). Para fazer isso o método recebe a chamada mas deve chamar setHasOptionsMenu() durante o onCreate() para indicar que fragmento ele gostaria que fosse adicionado para o Options Menu.


Quaisquer itens que você queira adicionar para o Options Menu do fragmento são concatenados no menu de itens existente. O fragmento também recebe callbacks para onOptionsItemSelected() quando um item de menu é selecionado.

Você também pode registrar um view no seu layout de fragmento para prover um menu de contexto chamando registerForContextMenu(). Quando o usuário abre o menu de contexto, o fragmento recebe uma chamada para onCreateContextMenu(). Quando o usuário seleciona um item, o fragmento recebe uma chamada para onContextItemSelected().

Recursos técnicos e onde estamos no conteúdo do Android

Recursos técnicos

No site do Android existem inúmeros exemplos de código que ajudam muito a quem está começando na programação com essa plataforma. O site pode ser acessado por aqui. Se você tem dúvidas e quer ver alguns excelentes códigos criados pela equipe do Android, é o caminho certo.

Onde estamos no conteúdo


Até agora abordamos uma parte considerável do conteúdo de Android, ainda que não pareça. Só essa parte de interface é gigante!

Abaixo está o conteúdo que disse que ia (e vou) cobrir nesse blog:
  1. Visão geral do Android
  2. Criando interfaces de usuário
  3. Atividades e Intents
  4. Threads, Serviços, Receivers e Alerts
  5. Eventos de interface
  6. Técnicas de multimidia
  7. Interface de hardware
  8. Networking
  9. Métodos de data storage
  10. Serviços baseados em localização
  11. Desenvolvimento avançado em Android e;
  12. Debugging.
Nos próximos posts vou continuar falando de fragmentos (que são atividades mas marquei como interface já que respondem por interfaces para o usuário) e loaders.

Após isso continuarei com os intents (mas antes disso também falarei de Services, BoundServices e ContentProviders) e os três primeiros tópicos estarão cobertos.

Só que alguns de vocês podem estar se perguntando: ok, vou aprender tudo isso após o término dos posts programados para essa semana. Mas como vou conseguir unir tudo isso? Eu quero dizer, uma coisa é saber como o motor de um carro funciona, como é o conceito de motores a combustão. Outra coisa é saber como pegar peça por peça e fazer daquele monte de peças um motor de verdade.

Por isso que abri o post falando sobre os recursos técnicos.

Vou pegar alguns desses exemplos e explorá-los aqui para mostrar como juntar os conteúdos para fazer uma aplicação de verdade. De nada adianta ter o conhecimento geral se não conseguimos, na prática, criar um app, certo?

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
Related Posts Plugin for WordPress, Blogger...