Executando código no thread principal de outro thread


326

Em um serviço Android, criei thread (s) para executar alguma tarefa em segundo plano.

Eu tenho uma situação em que um thread precisa postar determinada tarefa na fila de mensagens do thread principal, por exemplo: a Runnable.

Existe uma maneira de obter Handlerdo segmento principal e pós Message/ Runnablea ele do meu outro segmento?

Obrigado,


2
Você também pode usar o receptor de transmissão personalizado .... tente minha resposta aqui, [Inner Broadcast Receiver] [1] [1]: stackoverflow.com/a/22541324/1881527
Melbourne Lopes

Existem muitos caminhos. Além da resposta de David e do comentário de dzeikei em sua resposta, (3) você pode usar um Receptor de Transmissão ou (4) passar o manipulador em extras do Intent usados ​​para iniciar o serviço e, em seguida, recuperar o manipulador do encadeamento principal dentro do serviço usando getIntent ( ) .getExtras ().
Ashok Bijoy Debnath

2
@ sazzad-hissain-khan, Por que marcar esta pergunta de 2012 com respostas principalmente em Java com a tag kotlin?
precisa saber é o seguinte

Respostas:


617

NOTA: Esta resposta recebeu tanta atenção que preciso atualizá-la. Desde que a resposta original foi postada, o comentário de @dzeikei recebeu quase tanta atenção quanto a resposta original. Então, aqui estão duas soluções possíveis:

1. Se o encadeamento em segundo plano tiver uma referência a um Contextobjeto:

Verifique se os threads de trabalho em segundo plano têm acesso a um objeto de Contexto (pode ser o contexto de Aplicativo ou o contexto de Serviço). Em seguida, basta fazer isso no thread de trabalho em segundo plano:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

2. Se o encadeamento em segundo plano não tiver (ou precisar) de um Contextobjeto

(sugerido por @dzeikei):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);

1
Obrigado David, deu certo para mim, mais uma coisa se você pudesse me ajudar, se eu estender Handler e impl handleMessage (), impediria que o thread principal manipulasse suas mensagens? isso é apenas uma questão de curosity ..
Ahmed

2
Não. Se você subclassificar Handler(ou usar a Handler.Callbackinterface), seu handleMessage()método SOMENTE será chamado para mensagens postadas usando seu manipulador. O thread principal está usando um manipulador diferente para postar / processar mensagens, para que não haja conflito.
precisa

205
Eu acredito que você não precisa sequer de contexto, se você usarHandler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
Ponto menor; seu código não vai para onde está ... atualmente. Deveria sernew Runnable(){@Override public void run() {....}};
Richard Tingle

1
@SagarDevanga Este não é o lugar certo para fazer uma pergunta diferente. Poste uma nova pergunta, não um comentário em uma resposta não relacionada. Você obterá uma resposta melhor e mais rápida dessa maneira.
David Wasser

138

Como um comentarista abaixo apontou corretamente, essa não é uma solução geral para serviços, apenas para threads iniciados a partir de sua atividade (um serviço pode ser esse segmento, mas nem todos). Sobre o tópico complicado da comunicação de atividade de serviço, leia toda a seção Serviços do documento oficial - é complexo, portanto vale a pena entender o básico: http://developer.android.com/guide/components/services.html #Notifications

O método abaixo pode funcionar nos casos mais simples.

Se eu entendi corretamente, você precisa de algum código para ser executado no thread da GUI do aplicativo (não consigo pensar em nada mais chamado thread "principal"). Para isso, existe um método em Activity:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

Doc: http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29

Espero que seja isso que você está procurando.


20
OP diz que está executando threads em um Service. Você não pode usar runOnUiThread()em a Service. Esta resposta é enganosa e não aborda a pergunta feita.
precisa

31

Existe outra maneira simples, se você não tiver acesso ao contexto.

1) Crie um manipulador a partir do looper principal:

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

2) Implemente uma interface Runnable:

Runnable runnable = new Runnable() { // your code here }

3) Publique o seu Runnable no uiHandler:

uiHandler.post(runnable);

Isso é tudo ;-) Divirta-se com os tópicos, mas não se esqueça de sincronizá-los.


29

Se você executar código em um encadeamento, por exemplo, adiando alguma ação, precisará invocar a runOnUiThreadpartir do contexto. Por exemplo, se seu código estiver dentro da MainActivityclasse, use o seguinte:

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});

Se o seu método puder ser chamado a partir do main (thread da UI) ou de outros threads, você precisará de uma verificação como:

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}

7
OP diz que está executando threads em um Service. Você não pode usar runOnUiThread()em a Service.
precisa

@DavidWasser Isso está documentado em algum lugar? Os documentos do método não mencionam nada sobre isso. developer.android.com/reference/android/app/…
Greg Brown

1
@GregBrown Como você indicou pelo seu link para a Activitydocumentação, runOnUiThread()é um método de Activity. Não é um método de Service, portanto, você não pode usá-lo. O que poderia ser mais claro do que isso?
David Wasser

@DavidWasser Fair suficiente. Nem me lembro por que fiz essa pergunta agora (foi postada quase um ano atrás).
Greg Brown

24

Um bloco de código condensado é o seguinte:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });

Isso não envolve passar a referência de atividade ou a referência de aplicativo.

Equivalente de Kotlin:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })

20

Versões do Kotlin

Quando você estiver em uma atividade , use

runOnUiThread {
    //code that runs in main
}

Quando você tem um contexto de atividade , o mContext então usa

mContext.runOnUiThread {
    //code that runs in main
}

Quando você estiver em algum lugar onde não há contexto disponível , use

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}

Não sei qual é a versão do Kotlin, mas tive que adicionar chaves:runOnUiThread(){...}
Wex

5

A maneira mais simples, especialmente se você não tiver um contexto, se estiver usando o RxAndroid, poderá:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}

5

Código Kotlin mais preciso usando manipulador:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }

4

Um método que posso pensar é o seguinte:

1) Deixe a interface do usuário vincular ao serviço.
2) Exponha um método como o abaixo, Binderque registra seu Handler:

public void registerHandler(Handler handler) {
    mHandler = handler;
}

3) No thread da interface do usuário, chame o método acima após a ligação ao serviço:

mBinder.registerHandler(new Handler());

4) Use o manipulador no encadeamento do serviço para postar sua tarefa:

mHandler.post(runnable);

3

HandlerThread é a melhor opção para threads Java normais no Android.

  1. Crie um HandlerThread e inicie-o
  2. Criar um manipulador com Looper de HandlerThread:requestHandler
  3. postuma Runnabletarefa emrequestHandler

Comunicação com o Thread da UI de HandlerThread

  1. Crie um método Handlerwith Looperpara main: responseHandlere substituahandleMessage
  2. Dentro Runnabletarefa de outra Thread ( HandlerThreadneste caso), chamada sendMessageemresponseHandler
  3. A sendMessagechamada deste resultado de handleMessagein responseHandler.
  4. Obter atributos Messagee processá-lo, atualizar a interface do usuário

Exemplo : atualização TextViewcom dados recebidos de um serviço da web. Como o serviço da web deve ser chamado em um segmento que não seja da interface do usuário, criado HandlerThreadpara a Operação de Rede. Depois de obter o conteúdo do serviço da Web, envie uma mensagem para o manipulador de thread principal (UI Thread) e Handlerele manipulará a mensagem e atualizará a UI.

Código de amostra:

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


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

Artigos úteis:

manipuladores-e-por-que-você-deveria-estar-usando-seus-aplicativos-android

android-looper-handler-handlerthread-i


2

Sei que essa é uma pergunta antiga, mas me deparei com uma linha principal de thread principal usada no Kotlin e no Java. Essa pode não ser a melhor solução para um serviço, mas chamar alguma coisa que altere a interface do usuário dentro de um fragmento é extremamente simples e óbvio.

Java (8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });

Kotlin:

this.runOnUiThread {
     //your main thread code
}

1

Siga este método. Usando essa maneira, você pode simplesmente atualizar a interface do usuário a partir de um thread em segundo plano. O runOnUiThread funciona no thread principal (UI). Eu acho que esse trecho de código é menos complexo e fácil, principalmente para iniciantes.

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });

no caso de um serviço

crie um manipulador no oncreate

 handler = new Handler();

então use-o assim

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }

Obrigado por este trecho de código, que pode fornecer ajuda imediata e limitada. Uma explicação adequada melhoraria bastante seu valor a longo prazo , mostrando por que essa é uma boa solução para o problema e a tornaria mais útil para futuros leitores com outras perguntas semelhantes. Por favor edite sua resposta para adicionar alguma explicação, incluindo as suposições que você fez.
iBug 19/02/19

1

para Kotlin, você pode usar as corountines Anko :

atualizar

doAsync {
   ...
}

descontinuada

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}

1

O mais útil é fazer o seguinte:

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}

E depois:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}

0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

Isso também pode funcionar em uma classe de serviço sem problemas.


Embora esse código possa responder à pergunta, você ainda pode adicionar algumas frases explicativas, pois isso aumenta o valor da sua resposta para outros usuários!
MBT

0

Com o Kotlin, é assim dentro de qualquer função:

runOnUiThread {
   // Do work..
}

2
Isso é apenas quando você está em um Activity.
Farsheel
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.