Come utilizziamo runOnUiThread in Android?


157

Sono nuovo su Android e sto provando a utilizzare il thread dell'interfaccia utente, quindi ho scritto una semplice attività di test. Ma penso di aver frainteso qualcosa, perché facendo clic sul pulsante - l'app non risponde più

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();
                    }
                }
             }
        }));
    }
}

Risposte:


204

Di seguito è stato corretto lo snippet di runThreadfunzione.

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();
}

Non è stata questa la raccolta dei rifiuti quasi immediatamente? Probabilmente devi fare qualche riferimento alla discussione ()
Nick,

14
@Nick: il garbage collector osserva anche lo stack, ovvero quando il thread è in esecuzione, non verrà GC.
Miro Kropacek,

@Vipul, ho avuto una domanda sulla rotazione del telefono: voglio che una volta che ruoti il ​​mio telefono questo thread funzioni e non venga creato alcun nuovo thread. Potete fornire alcuni suggerimenti su come impedire la creazione di nuovi thread dopo aver ruotato il telefono?
user1836957,

89

Basta avvolgerlo come una funzione, quindi chiamare questa funzione dal thread in background.

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

3
votato per mostrare come accedere ai dati in ambito esterno ( final)
mariotomo,

27

Ce l'hai di fronte. Il clic del pulsante comporta una chiamata a runOnUiThread(), ma ciò non è necessario, poiché il gestore di clic è già in esecuzione sul thread dell'interfaccia utente. Quindi, il tuo codice runOnUiThread()sta lanciando un nuovo thread in background, in cui si tenta di eseguire operazioni dell'interfaccia utente, che poi falliscono.

Invece, basta avviare il thread in background direttamente dal gestore di clic. Quindi, avvolgi le chiamate btn.setText()all'interno di una chiamata a runOnUiThread().


Mentre è vero che il gestore di clic è già nel thread dell'interfaccia utente, una chiamata a runOnUiThread()non è necessaria ma dovrebbe essere innocua. Javadoc per quel metodo dice "Esegue l'azione specificata sul thread dell'interfaccia utente. Se il thread corrente è il thread dell'interfaccia utente, l'azione viene eseguita immediatamente. Se il thread corrente non è il thread dell'interfaccia utente, l'azione viene registrata nella coda degli eventi del thread dell'interfaccia utente. "
k2col,

15
runOnUiThread(new Runnable() {
                public void run() {
                //Do something on UiThread
            }
        });

10

Esistono diverse tecniche usando runOnUiThread (), vediamo tutto

Questo è il mio thread principale (thread dell'interfaccia utente) chiamato AndroidBasicThreadActivity e lo aggiornerò da un thread di lavoro in vari modi:

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.) Passando l'istanza di Activity come argomento sul thread di lavoro

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.) Utilizzando il metodo Post (Runnable runnable) di View nel thread di lavoro

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 la classe Handler dal pacchetto android.os Se non abbiamo il contesto (this / getApplicationContext ()) o l'istanza di Activity (AndroidBasicThreadActivity.this), allora dobbiamo usare la classe Handler come di seguito -

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.");
        }
    });
  }
}

Grazie .. Invece di limitarsi a ripetere su runOnUIThread di attività, si è accennato tutti i possibili modi di invocandolo ..
arun

Grazie .. Invece di limitarsi a ripetere su runOnUIThread di attività, si è accennato tutti i possibili modi di invocandolo ..
arun

6

Se si utilizza in frammento, semplicemente scrivere

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

1

tuo questo:

@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");


            }
        });
    }

1

Usiamo Worker Thread per rendere le app più fluide ed evitare ANR. Potremmo aver bisogno di aggiornare l'interfaccia utente dopo il pesante processo di lavoro del battistrada. L'interfaccia utente può essere aggiornata solo dal thread dell'interfaccia utente. In tali casi, utilizziamo Handler o runOnUiThread entrambi hanno un metodo di esecuzione Runnable che viene eseguito nel thread dell'interfaccia utente. Il metodo onClick viene eseguito nel thread dell'interfaccia utente, quindi non è necessario utilizzare runOnUiThread qui.

Usando Kotlin

Durante l'attività,

this.runOnUiThread {
      // Do stuff
}

Dal frammento,

activity?.runOnUiThread {
      // Do stuff
}

Utilizzando Java ,

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});

0

È possibile utilizzare da questo esempio:

Nel seguente esempio, utilizzeremo questa funzione per pubblicare il risultato da una ricerca sinonimo elaborata da un thread in background.

Per raggiungere l'obiettivo durante il callback dell'attività di OnCreate, configureremo onClickListener per eseguire searchTask su un thread creato.

Quando l'utente fa clic sul pulsante Cerca, creeremo una classe anonima Runnable che cerca la parola digitata in R.id.wordEt EditText e avvia il thread per eseguire Runnable.

Al termine della ricerca, creeremo un'istanza di Runnable SetSynonymResult per pubblicare il risultato sul sinonimo TextView sul thread dell'interfaccia utente.

Questa tecnica a volte non è la più conveniente, specialmente quando non abbiamo accesso a un'istanza Activity; pertanto, nei capitoli seguenti, discuteremo di tecniche più semplici e più pulite per aggiornare l'interfaccia utente da un'attività di elaborazione in background.

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 :

programmazione Android asincrona Helder Vasconcelos


0

Ecco come lo uso:

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                //Do something on UiThread
            }
        });

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();

    }

0

Prova questo: getActivity().runOnUiThread(new Runnable...

È perchè:

1) ciò implicito nella chiamata a runOnUiThread si riferisce ad AsyncTask , non al tuo frammento.

2) Il frammento non ha runOnUiThread.

Tuttavia, l'attività fa.

Nota che Activity esegue il Runnable se sei già sul thread principale, altrimenti usa un Handler. Puoi implementare un gestore nel tuo frammento se non vuoi preoccuparti del contesto, in realtà è molto semplice:

// Un'istanza di classe

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

// altrove nel tuo codice

mHandler.post(<your runnable>);

// ^ questo verrà sempre eseguito sul ciclo di esecuzione successivo sul thread principale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.