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,

Ahmed
fonte
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);
David Wasser
fonte
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.

Alexey Vassiliev
fonte
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.

tema_man
fonte
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 {

}
Alexander Volkov
fonte
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
    })
david m lee
fonte
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
}
Sazzad Hissain Khan
fonte
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()
}
Inn0vative1
fonte
5

Código Kotlin mais preciso usando manipulador:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }
Hamed Jaliliani
fonte
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);
Dheeraj Vepakomma
fonte
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

Ravindra babu
fonte
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
}
mevdev
fonte
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);
    }
Margem
fonte
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
}
Francis
fonte
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)}
}
HotJard
fonte
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.

Revelation AF
fonte
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..
}
Abdulmajeed Alyafey
fonte
2
Isso é apenas quando você está em um Activity.
Farsheel