Acessando o manipulador de thread de IU de um serviço

89

Estou tentando algo novo no Android para o qual preciso acessar o manipulador do thread de interface do usuário.

Eu sei o seguinte:

  1. O thread de IU tem seu próprio manipulador e looper
  2. Qualquer mensagem será colocada na fila de mensagens do thread da IU
  3. O looper pega o evento e o passa para o manipulador
  4. O manipulador lida com a mensagem e envia o evento específico para a IU

Eu quero ter meu serviço que precisa obter o manipulador de thread de IU e colocar uma mensagem neste manipulador. Para que esta mensagem seja processada e emitida para a IU. Aqui, o serviço será um serviço normal que será iniciado por algum aplicativo.

Eu gostaria de saber se isso é possível. Se sim, sugira alguns trechos de código, para que eu possa tentar.

Atenciosamente, Girish

iLikeAndroid
fonte

Respostas:

179

Este snippet de código constrói um Handler associado ao thread principal (UI):

Handler handler = new Handler(Looper.getMainLooper());

Você pode então postar coisas para execução no thread principal (UI) como:

handler.post(runnable_to_call_from_main_thread);

Se o próprio manipulador for criado a partir do thread principal (UI), o argumento pode ser omitido por questões de brevidade:

Handler handler = new Handler();

O Android Dev Guide sobre processos e threads tem mais informações.

voleio
fonte
2
Testei e funciona muito bem! Um exemplo de caso de uso: eu tenho uma interface da web que está sendo servida por um servidor executado diretamente no dispositivo. Como a interface pode ser usada para interagir diretamente com a IU e como o servidor precisa ser executado em seu próprio encadeamento, eu precisava encontrar uma maneira de tocar no encadeamento da IU de fora de uma Activity. O método que você descreveu funcionou muito bem.
Sr.Pjer
1
Brilhante. Funciona perfeitamente e é muito útil. OBRIGADO.
JRun
perfeito ^^ acabei de usá-lo para atualizar minha interface do usuário de um StreamingService. exatamente o que eu precisava, obrigado!
An-droid
Você sabe se posso criar uma instância singleton de um manipulador e usá-la sempre que precisar executar algo no thread da interface do usuário?
HelloWorld
Acho que nunca saberemos
Denny
28

Crie um Messengerobjeto anexado ao seu Handlere passe Messenger-o para o Service(por exemplo, em um Intentextra para startService()). O Servicepode então enviar um Messagepara o Handlervia Messenger. Aqui está um aplicativo de amostra que demonstra isso.

CommonsWare
fonte
Obrigado por esta dica. Isso foi útil. Consulte a seguinte pilha para um fluxo de evento de toque para minha atividade MyDemo.dispatchTouchEvent (MotionEvent) linha: 20 PhoneWindow $ DecorView.dispatchTouchEvent (MotionEvent) linha: 1696 ViewRoot.handleMessage (Message) linha: 1658 ViewRoot (Handler) .dispatchMessage (Message ) line: 99 Looper.loop () line: 123 // O tratamento de eventos começa aqui ActivityThread.main (String []) line: 4203 Aqui o ViewRoot é um Handler. Quero obter a referência deste manipulador ... é possível obter isso do meu aplicativo?
iLikeAndroid
@iLikeAndroid: Se você não criou o Handler, você não pode acessá-lo, AFAIK.
CommonsWare
Obrigado. Tentei criar uma instância do ViewRoot. Isso nada mais é do que um manipulador. Agora posso emitir as mensagens neste manipulador. O manipulador está recebendo a mensagem. Mas o ViewRoot não consegue processar a mensagem porque não foi inicializado corretamente. Eu preciso chamar ViewRoot.setView () para inicializar os dados apropriados para ViewRoot. Quero saber se existe uma visualização padrão ou uma visualização base, etc., que posso usar para inicializar?
iLikeAndroid
@iLikeAndroid: Não existe ViewRootno Android SDK, AFAICT.
CommonsWare
1
@ hadez30: Pessoalmente, não uso muito os serviços vinculados. Você ainda pode usar um Handler/ Messenger, embora eu substitua tudo isso por um barramento de evento (por exemplo, EventBus do greenrobot).
CommonsWare
4

No momento, prefiro usar uma biblioteca de ônibus de eventos como a Otto para esse tipo de problema. Basta inscrever os componentes desejados (atividade):

protected void onResume() {
    super.onResume();
    bus.register(this);
}

Em seguida, forneça um método de retorno de chamada:

public void onTimeLeftEvent(TimeLeftEvent ev) {
    // process event..
}

e então quando seu serviço executa uma declaração como esta:

bus.post(new TimeLeftEvent(340));

Esse POJO será passado para sua atividade acima e todos os outros componentes de assinatura. Simples e elegante.

carrinho de bebê
fonte
3

Sugiro tentar o seguinte código:

    new Handler(Looper.getMainLooper()).post(() -> {

        //UI THREAD CODE HERE



    });
user2983041
fonte
Algumas explicações adicionais são necessárias para auxiliar o OP.
Moog
2

Você pode obter valores por meio do receptor de transmissão ...... da seguinte forma, primeiro crie seu próprio IntentFilter como,

Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");

Em seguida, crie a classe interna BroadcastReceiver como,

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    /** Receives the broadcast that has been fired */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction()=="YOUR_INTENT_FILTER"){
           //HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
           String receivedValue=intent.getStringExtra("KEY");
        }
    }
};

Agora registre seu receptor de transmissão em onResume () como,

registerReceiver(broadcastReceiver, intentFilter);

E, finalmente, cancele o registro de BroadcastReceiver em onDestroy () como,

unregisterReceiver(broadcastReceiver);

Agora, a parte mais importante ... Você precisa disparar a transmissão de onde você precisar enviar valores ... então faça como,

Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

....Felicidades :)

Melbourne Lopes
fonte
1

É kotlinassim que você pode fazer isso

Digamos se você deseja mostrar mensagem de brinde do serviço

val handler = Handler(Looper.getMainLooper())
handler.post {
   Toast.makeText(context, "This is my message",Toast.LENGTH_LONG).show()
}
Zohab Ali
fonte
0

Solução:

  1. Crie um manipulador com Looper do thread principal: requestHandler
  2. Crie um Handlercom a Looperpartir do thread principal: responseHandler e handleMessagemétodo override
  3. postar um Runnable tarefa em RequestHandler
  4. RunnableTarefa interna , chame sendMessage em responseHandler
  5. Esta sendMessagechamada de resultado de handleMessage em responseHandler.
  6. Obtenha atributos do Messagee processe-os, atualize a IU

Código de amostra:

    /* Handler from UI Thread to send request */

    Handler requestHandler = new Handler(Looper.getMainLooper());

     /* Handler from UI Thread to process messages */

    final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {

            /* Processing handleMessage */

            Toast.makeText(MainActivity.this,
                    "Runnable completed with result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

    for ( int i=0; i<10; i++) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                   /* Send an Event to UI Thread through message. 
                      Add business logic and prepare message by 
                      replacing example code */

                    String text = "" + (++rId);
                    Message msg = new Message();

                    msg.obj = text.toString();
                    responseHandler.sendMessage(msg);
                    System.out.println(text.toString());

                } catch (Exception err) {
                    err.printStackTrace();
                }
            }
        };
        requestHandler.post(myRunnable);
    }
Ravindra babu
fonte