Como usamos o runOnUiThread no Android?

157

Eu sou novo no Android e estou tentando usar o UI-Thread, então escrevi uma atividade de teste simples. Mas acho que entendi errado algo, porque ao clicar no botão - o aplicativo não responde mais

public class TestActivity extends Activity {

    Button btn;
    int i = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btn = (Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runThread();
            }
        });
    }

    private void runThread(){
        runOnUiThread (new Thread(new Runnable() {  
            public void run() {
                while(i++ < 1000){
                    btn.setText("#"+i);
                    try {
                        Thread.sleep(300);
                    } 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
             }
        }));
    }
}
user1049280
fonte

Respostas:

204

Abaixo está o trecho de runThreadfunção corrigido .

private void runThread() {

    new Thread() {
        public void run() {
            while (i++ < 1000) {
                try {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            btn.setText("#" + i);
                        }
                    });
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}
Vipul Shah
fonte
Não foi coletado lixo quase imediatamente? Provavelmente você precisa manter uma referência para o segmento ()
Nick
14
@ Nick: o coletor de lixo também observa a pilha, ou seja, quando o encadeamento estiver em execução, ele não será gravado em GC.
Miro Kropacek 13/03/19
@Vipul, eu tinha uma pergunta sobre a rotação do telefone: quero que, uma vez que eu gire o meu telefone, este segmento seja executado e nenhum novo segmento seja criado. Você pode fornecer algumas dicas sobre como impedir a criação de um novo encadeamento quando o telefone for girado?
user1836957
89

Apenas envolva-o como uma função e chame-a de seu thread em segundo plano.

public void debugMsg(String msg) {
    final String str = msg;
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mInfo.setText(str);
        }
    });
}

fonte
3
upvoted para mostrar como os dados de acesso no escopo externo ( final)
mariotomo
27

Você tem de trás para frente. O clique no botão resulta em uma chamada para runOnUiThread(), mas isso não é necessário, pois o manipulador de cliques já está em execução no thread da interface do usuário. Em seguida, seu código runOnUiThread()está iniciando um novo encadeamento em segundo plano, no qual você tenta executar operações da interface do usuário, que falham.

Em vez disso, basta iniciar o thread em segundo plano diretamente do seu manipulador de cliques. Em seguida, envolva as chamadas para btn.setText()dentro de uma chamada para runOnUiThread().

Graham Borland
fonte
Embora seja verdade que o manipulador de cliques já está no segmento da interface do usuário, uma chamada para runOnUiThread()é desnecessária, mas deve ser inofensiva. O Javadoc para esse método diz "Executa a ação especificada no encadeamento da interface do usuário. Se o encadeamento atual for o encadeamento da interface do usuário, a ação será executada imediatamente. Se o encadeamento atual não for o encadeamento da interface do usuário, a ação será postada na fila de eventos do thread da interface do usuário ".
K2col #
15
runOnUiThread(new Runnable() {
                public void run() {
                //Do something on UiThread
            }
        });
Terranologia
fonte
10

Existem várias técnicas usando runOnUiThread (), vamos ver todas

Este é o meu thread principal (thread da interface do usuário) chamado AndroidBasicThreadActivity e vou atualizá-lo de um thread de trabalho de várias maneiras -

public class AndroidBasicThreadActivity extends AppCompatActivity
{
    public static TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_basic_thread);

        textView = (TextView) findViewById(R.id.textview);

        MyAndroidThread myTask = new MyAndroidThread(AndroidBasicThreadActivity.this);
        Thread t1 = new Thread(myTask, "Bajrang");
        t1.start();
    }
}

1.) Ao passar a instância de Activity como um argumento no thread de trabalho

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {

        //perform heavy task here and finally update the UI with result this way - 
        activity.runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
            }
        });
    }
}

2.) Usando o método de publicação da View (Runnable runnable) no thread de trabalho

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {
     //perform heavy task here and finally update the UI with result this way - 
       AndroidBasicThreadActivity.textView.post(new Runnable()
      { 
        @Override
        public void run()
        {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });

    }
}

3.) Usando a classe Handler do pacote android.os Se não temos o contexto (this / getApplicationContext ()) ou a instância da Activity (AndroidBasicThreadActivity.this), precisamos usar a classe Handler da seguinte forma:

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
   public void run()
  {
  //perform heavy task here and finally update the UI with result this way - 
  new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });
  }
}
Bajrang Hudda
fonte
Obrigado .. Em vez de apenas repetir sobre runOnUIThread de atividade, você mencionou todas as formas possíveis de invocá-lo ..
arun
Obrigado .. Em vez de apenas repetir sobre runOnUIThread de atividade, você mencionou todas as formas possíveis de invocá-lo ..
arun
6

Se estiver usando um fragmento, basta escrever

getActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // Do something on UiThread
    }
});
Shivam Yadav
fonte
1

teu isto:

@UiThread
    public void logMsg(final String msg) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                Log.d("UI thread", "I am the UI thread");


            }
        });
    }
MrGuy
fonte
1

Usamos Worker Thread para tornar os aplicativos mais suaves e evitar ANRs. Talvez seja necessário atualizar a interface do usuário após o processo pesado no trabalho do Tread. A interface do usuário só pode ser atualizada no Thread da interface do usuário. Nesses casos, usamos Handler ou runOnUiThread, ambos possuem um método Runnable run que é executado no UI Thread. O método onClick é executado no thread da interface do usuário, portanto, não é necessário usar o runOnUiThread aqui.

Usando o Kotlin

Enquanto em atividade,

this.runOnUiThread {
      // Do stuff
}

De Fragment,

activity?.runOnUiThread {
      // Do stuff
}

Usando Java ,

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});
Varun Chandran
fonte
0

Você pode usar a partir desta amostra:

No exemplo a seguir, usaremos esse recurso para publicar o resultado de uma pesquisa de sinônimos que foi processada por um encadeamento em segundo plano.

Para atingir a meta durante o retorno de chamada da atividade OnCreate, configuraremos onClickListener para executar searchTask em um thread criado.

Quando o usuário clica no botão Pesquisar, criaremos uma classe anônima Runnable que pesquisa a palavra digitada em R.id.wordEt EditText e inicia o thread para executar Runnable.

Quando a pesquisa for concluída, criaremos uma instância do Runnable SetSynonymResult para publicar o resultado novamente no sinônimo TextView sobre o thread da interface do usuário.

Essa técnica às vezes não é a mais conveniente, principalmente quando não temos acesso a uma instância de Activity; portanto, nos capítulos seguintes, discutiremos técnicas mais simples e limpas para atualizar a interface do usuário a partir de uma tarefa de computação em segundo plano.

public class MainActivity extends AppCompatActivity {

    class SetSynonymResult implements Runnable {
        String synonym;

        SetSynonymResult(String synonym) {
            this.synonym = synonym;
        }

        public void run() {
            Log.d("AsyncAndroid", String.format("Sending synonym result %s on %d",
                    synonym, Thread.currentThread().getId()) + " !");
            TextView tv = (TextView) findViewById(R.id.synonymTv);
            tv.setText(this.synonym);
        }
    }

    ;

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

        Button search = (Button) findViewById(R.id.searchBut);
        final EditText word = (EditText) findViewById(R.id.wordEt);
        search.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Runnable searchTask = new Runnable() {
                    @Override
                    public void run() {
                        String result = searchSynomim(word.getText().toString());
                        Log.d("AsyncAndroid", String.format("Searching for synonym for %s on %s",
                                word.getText(), Thread.currentThread().getName()));
                        runOnUiThread(new SetSynonymResult(result));
                    }
                };
                Thread thread = new Thread(searchTask);
                thread.start();
            }
        });

    }

    static int i = 0;

    String searchSynomim(String word) {
        return ++i % 2 == 0 ? "fake" : "mock";
    }
}

Fonte :

programação android assíncrona Helder Vasconcelos


fonte
0

É assim que eu uso:

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                //Do something on UiThread
            }
        });
Josh Aquino
fonte
0
  @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.success1);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //dummy delay for 2 second
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //update ui on UI thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        gifImageView.setGifImageResource(R.drawable.success);
                    }
                });

            }
        }).start();

    }
Keshav Gera
fonte
0

Tente o seguinte: getActivity().runOnUiThread(new Runnable...

É porque:

1) o implícito na sua chamada para runOnUiThread está se referindo ao AsyncTask , não ao seu fragmento.

2) O fragmento não possui o runOnUiThread.

No entanto, a atividade faz.

Observe que Activity apenas executa o Runnable se você já estiver no thread principal, caso contrário, ele usa um Handler. Você pode implementar um manipulador em seu fragmento, se não quiser se preocupar com o contexto, é realmente muito fácil:

// Uma instância de classe

private Handler mHandler = new Handler(Looper.getMainLooper());

// em qualquer outro lugar do seu código

mHandler.post(<your runnable>);

// ^ isso sempre será executado no próximo loop de execução no thread principal.

Pradeep Kumar
fonte