Jak używamy runOnUiThread w Androidzie?

157

Jestem nowy w Androidzie i próbuję użyć wątku interfejsu użytkownika, więc napisałem proste ćwiczenie testowe. Ale wydaje mi się, że coś źle zrozumiałem, bo po kliknięciu przycisku - aplikacja już nie reaguje

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
źródło

Odpowiedzi:

204

Poniżej poprawiono fragment runThreadfunkcji.

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
źródło
Czy to nie było śmieci zebrane prawie natychmiast? Prawdopodobnie musisz zachować jakieś odniesienie do Thread ()
Nick
14
@Nick: garbage collector obserwuje również stos, tzn. Kiedy wątek jest uruchomiony, nie zostanie poddany GC.
Miro Kropacek
@Vipul, miałem pytanie dotyczące obracania telefonu: chcę, aby po obróceniu telefonu ten wątek działał i nie został utworzony żaden nowy wątek. Czy możesz podać kilka wskazówek, jak zapobiec tworzeniu nowego wątku po obróceniu telefonu?
user1836957
89

Po prostu opakuj go jako funkcję, a następnie wywołaj tę funkcję z wątku w tle.

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

źródło
3
głosowano za pokazanie, jak uzyskać dostęp do danych w zakresie zewnętrznym ( final)
mariotomo
27

Masz to od tyłu do przodu. Kliknięcie przycisku powoduje wywołanie funkcji runOnUiThread(), ale nie jest to potrzebne, ponieważ moduł obsługi kliknięcia jest już uruchomiony w wątku interfejsu użytkownika. Następnie twój kod runOnUiThread()uruchamia nowy wątek w tle, w którym próbujesz wykonać operacje interfejsu użytkownika, które następnie kończą się niepowodzeniem.

Zamiast tego po prostu uruchom wątek w tle bezpośrednio z modułu obsługi kliknięć. Następnie zawiń wywołania do btn.setText()wewnątrz wywołania do runOnUiThread().

Graham Borland
źródło
Chociaż prawdą jest, że moduł obsługi kliknięć znajduje się już w wątku interfejsu użytkownika, wywołanie nie runOnUiThread()jest konieczne, ale powinno być nieszkodliwe. Dokument Javadoc dla tej metody mówi „Uruchamia określoną akcję w wątku interfejsu użytkownika. Jeśli bieżący wątek jest wątkiem interfejsu użytkownika, akcja jest wykonywana natychmiast. Jeśli bieżący wątek nie jest wątkiem interfejsu użytkownika, akcja jest wysyłana do kolejki zdarzeń wątku interfejsu użytkownika ”.
k2col
15
runOnUiThread(new Runnable() {
                public void run() {
                //Do something on UiThread
            }
        });
Terranology
źródło
10

Istnieje kilka technik wykorzystujących runOnUiThread (), zobaczmy wszystko

To jest mój główny wątek (wątek UI) o nazwie AndroidBasicThreadActivity i mam zamiar zaktualizować go z wątku roboczego na różne sposoby -

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.) Przekazując instancję Activity jako argument w wątku roboczym

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.) Używając metody posta View (Runnable runnable) w wątku roboczym

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.) Używając klasy Handler z pakietu android.os Jeśli nie mamy kontekstu (this / getApplicationContext ()) lub instancji Activity (AndroidBasicThreadActivity.this) to musimy użyć klasy Handler jak poniżej -

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
źródło
Dziękuję .. Zamiast tylko powtarzać o działaniu runOnUIThread, wspomniałeś o wszystkich możliwych sposobach wywoływania go ..
arun
Dziękuję .. Zamiast tylko powtarzać o działaniu runOnUIThread, wspomniałeś o wszystkich możliwych sposobach wywoływania go ..
arun
6

Jeśli używasz we fragmencie, po prostu napisz

getActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // Do something on UiThread
    }
});
Shivam Yadav
źródło
1

twój to:

@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
źródło
1

Używamy wątku roboczego, aby aplikacje działały płynniej i unikały błędów ANR. Może być konieczne zaktualizowanie interfejsu użytkownika po ciężkim procesie roboczym Tread. Interfejs użytkownika można aktualizować tylko z poziomu wątku interfejsu użytkownika. W takich przypadkach używamy Handler lub runOnUiThread, oba mają Runnable run metodę, która jest wykonywana w UI Thread. Metoda onClick działa w wątku interfejsu użytkownika, więc nie trzeba tutaj używać runOnUiThread.

Korzystanie z Kotlin

Podczas aktywności

this.runOnUiThread {
      // Do stuff
}

Od fragmentu,

activity?.runOnUiThread {
      // Do stuff
}

Korzystanie z języka Java ,

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});
Varun Chandran
źródło
0

Możesz użyć z tego przykładu:

W poniższym przykładzie zamierzamy użyć tej funkcji do opublikowania wyniku wyszukiwania synonimów, które zostało przetworzone przez wątek w tle.

Aby osiągnąć cel podczas wywołania zwrotnego działania OnCreate, skonfigurujemy onClickListener, aby uruchamiał searchTask na utworzonym wątku.

Kiedy użytkownik kliknie przycisk Wyszukaj, utworzymy anonimową klasę Runnable, która wyszukuje słowo wpisane w R.id.wordEt EditText i uruchamia wątek w celu wykonania Runnable.

Po zakończeniu wyszukiwania utworzymy wystąpienie Runnable SetSynonymResult, aby opublikować wynik z powrotem w synonimie TextView w wątku interfejsu użytkownika.

Ta technika czasami nie jest najwygodniejsza, zwłaszcza gdy nie mamy dostępu do instancji Activity; Dlatego w następnych rozdziałach omówimy prostsze i czystsze techniki aktualizacji interfejsu użytkownika z zadania obliczeniowego w tle.

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

Źródło :

asynchroniczne programowanie na Androida Helder Vasconcelos


źródło
0

Oto jak go używam:

runOnUiThread(new Runnable() {
                @Override
                public void run() {
                //Do something on UiThread
            }
        });
Josh Aquino
źródło
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
źródło
0

Spróbuj tego: getActivity().runOnUiThread(new Runnable...

To dlatego, że:

1) niejawne this w wywołaniu runOnUiThread odnosi się do AsyncTask , a nie do twojego fragmentu.

2) Fragment nie ma runOnUiThread.

Jednak Activity tak.

Zauważ, że Activity po prostu wykonuje Runnable, jeśli jesteś już w głównym wątku, w przeciwnym razie używa Handler. Możesz zaimplementować Handler w swoim fragmencie, jeśli nie chcesz się martwić o kontekst tego, jest to w rzeczywistości bardzo proste:

// Instancja klasy

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

// gdziekolwiek w kodzie

mHandler.post(<your runnable>);

// ^ to będzie zawsze uruchamiane w następnej pętli uruchamiania w głównym wątku.

Pradeep Kumar
źródło