Co dokładnie robi metoda postu?


106

Napotkałem bardzo dziwną funkcję.

Kiedy próbuję uruchomić animację w głównym wątku, to się nie uruchamia. Kiedy uruchomiłem wspomnianą animację za pomocą

getView().post(new Runnable() {
            @Override
            public void run() {
                getView().startAnimation(a);
            }
        });

Zaczyna się.

Wydrukowałem CurrentThreadprzed rozpoczęciem animacji i oba wydruki main.

Oczywiście czegoś mi tu brakuje, bo obaj powinni zacząć animację w głównym wątku ... Domyślam się, że gdy post dodaje zadanie do kolejki, zaczyna się w bardziej "odpowiednim czasie", ale chciałbym wiedzieć co dzieje się tutaj na większej głębokości.

EDYCJA: Pozwólcie, że wyjaśnię - moje pytanie brzmi, dlaczego uruchomienie animacji na poście powoduje jej uruchomienie, a uruchomienie animacji na głównym wątku nie.


Czy to zachowanie jest specyficzne dla wersji Androida? Nie mogłem go odtworzyć na Androidzie 4.1.2!
— Akdeniz

Odtworzyłem to zachowanie na Androidzie 2.3.3. Ale dla AnimationDrawable! Zwykła Animationinstancja zaczęła pomyślnie animować się przy każdej konfiguracji. W AnimationDrawableprzypadku; kiedy próbujesz rozpocząć onCreate, nie zaczyna się, ponieważ w tym momencie nie jesteś przywiązany do widoku. Nie jest to więc problem z wątkami AnimationDrawable. Może to samo dotyczy Animation? developer.android.com/guide/topics/graphics/…
— Akdeniz,

Odpowiedzi:


161

post : post powoduje dodanie Runnable do kolejki wiadomości,

Runnable: Reprezentuje polecenie, które można wykonać. Często używany do uruchamiania kodu w innym wątku.

run () : Rozpoczyna wykonywanie aktywnej części kodu klasy. Ta metoda jest wywoływana, gdy uruchamiany jest wątek, który został utworzony za pomocą klasy implementującej Runnable.

getView().post(new Runnable() {

         @Override
         public void run() {
             getView().startAnimation(a);
         }
     });

kod :getView().startAnimation(a);

w swoim kodzie,

post powoduje, że Runnable ( kod zostanie uruchomiony w innym wątku), aby dodać kolejkę komunikatów.

Więc startAnimation zostanie uruchomiona w nowym wątku, gdy zostanie pobrana z messageQueue

[EDYCJA 1]

Dlaczego używamy nowego wątku zamiast wątku interfejsu użytkownika (wątek główny)?

Wątek interfejsu użytkownika:

  • Po uruchomieniu aplikacji, wątek Ui jest tworzony automatycznie

  • odpowiada za wysyłanie wydarzeń do odpowiednich widżetów, w tym za rysowanie.

  • Jest to również wątek, z którym korzystasz z widżetów Androida

Na przykład, jeśli dotkniesz przycisku a na ekranie, wątek interfejsu użytkownika wysyła zdarzenie dotyku do widżetu, który z kolei ustawia swój stan naciśnięcia i wysyła żądanie unieważnienia do kolejki zdarzeń. Wątek interfejsu użytkownika usuwa z kolejki żądanie i powiadamia widżet o konieczności przerysowania.

Co się stanie, jeśli użytkownik naciśnie przycisk, który będzie działał długo?

((Button)findViewById(R.id.Button1)).setOnClickListener(           
             new OnClickListener() {        
        @Override
    public void onClick(View v) {
            final Bitmap b = loadImageFromNetwork();
            mImageView.setImageBitmap(b);
}
});

Interfejs użytkownika zawiesza się. Program może się nawet zawiesić.

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        final Bitmap b = loadImageFromNetwork();
        mImageView.setImageBitmap(b);
    }
  }).start();
}

Łamie regułę Androida, która nigdy nie aktualizuje interfejsu użytkownika bezpośrednio z wątku roboczego

Android oferuje kilka sposobów uzyskiwania dostępu do wątku interfejsu użytkownika z innych wątków.

  • Activity.runOnUiThread (do uruchomienia)
  • View.post (Runnable)
  • View.postDelayed (Runnable, long)
  • Treser

Jak poniżej,

View.post (Runnable)

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}

Treser

final Handler myHandler = new Handler(Looper.getMainLooper());

(new Thread(new Runnable() {

    @Override
    public void run() {
       final Bitmap b = loadImageFromNetwork();
      myHandler.post(new Runnable() {                           

        @Override
        public void run() {
           mImageView.setImageBitmap(b);
          }
        });
      }
    })).start();                
}

wprowadź opis obrazu tutaj

Po więcej informacji

http://android-developers.blogspot.com/2009/05/peling-threading.html

http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/


15
Dlaczego więc uruchamianie animacji w poście różni się od uruchamiania jej w głównym wątku, skoro oba ostatecznie działają w tym samym wątku?
— Gal

Ponieważ ten model pojedynczego wątku może dawać słabą wydajność w aplikacjach dla systemu Android.
— Talha

1
Co ma wspólnego słaba wydajność z brakiem animacji?
— Gal

17
Nie sądzę, aby to odpowiadało na pytanie, jest to bardziej ogólna odpowiedź dla początkujących, którzy nie wiedzą nic o interfejsie użytkownika i wielowątkowości. To nie wyjaśnia, dlaczego wrzucenie animacji do przodu w kolejce powoduje, że animacja działa; animacja powinna być czymś do wykonania bezpośrednio w wątku interfejsu użytkownika bez użycia jakichkolwiek sztuczek post () lub runOnUiThread ().
— carrizo

3
Cała praca interfejsu użytkownika powinna znajdować się w wątku głównym (wątku interfejsu użytkownika). Sztuczka, która sprawia, że ​​animacja działa przez użycie post () zamiast wywołania w głównym wątku od razu, to Time: Jeśli zadzwonisz od razu w głównym wątku, co oznacza, że ​​powiedziałeś „rozpocznij animację teraz”, ale w tej chwili widok nie jest gotowy do animacji (pomiar, rysowanie ...). Ale jeśli umieścisz to w post (), to będzie czekało na startAnimation w kolejce, aby przygotować widok gotowy do anim.
— NguyenDat

35

Czy dzieje się to w onCreate czy onCreateView? Jeśli tak, aplikacja może nie znajdować się w stanie, w którym widok jest dołączony do okna. Wiele algorytmów opartych na metrykach widoku może nie działać, ponieważ takie elementy, jak pomiary i położenie widoku, mogły nie zostać obliczone. Animacje systemu Android zazwyczaj wymagają, aby były obsługiwane przez obliczenia w interfejsie użytkownika

View.post faktycznie kolejkuje animację w pętli komunikatów widoku, więc gdy widok zostanie dołączony do okna, wykonuje animację, zamiast wykonywać ją ręcznie.

W rzeczywistości uruchamiasz rzeczy w wątku interfejsu użytkownika, ale w innym czasie


Zaakceptowana odpowiedź jest myląca, gdy plakat stwierdza: „post powoduje Runnable (kod zostanie uruchomiony w innym wątku)”. To jest poprawna odpowiedź „Rzeczywiście uruchamiasz rzeczy w wątku interfejsu użytkownika, ale w innym czasie” - plus 1
— smitty1

18

Zajrzyj tutaj, aby uzyskać dobrą odpowiedź. view.post () działa prawie tak samo jak handler.post (). Przechodzi do głównej kolejki wątków i jest wykonywany po zakończeniu innych oczekujących zadań. Jeśli wywołasz activity.runOnUiThread () zostanie wywołany natychmiast w wątku interfejsu użytkownika.


31
Jedną z ogromnych (i niezwykle pomocnych) różnic, które znalazłem, jest to, że funkcja runnable in view.post () zostanie wywołana po pierwszym wyświetleniu widoku. IE, możesz ustawić, aby uruchamiał animację po rozwinięciu widoku, a następnie w pewnym momencie w przyszłości, ostatecznie dodając go do hierarchii widoków. W tym momencie rozpocznie się animacja i nie będziesz musiał się tym martwić.
— DeeV

W rzeczywistości funkcja handler.post () nie zawsze wysyła wiadomość / runnable w głównym wątku. Zależy to od tego, jak został utworzony program obsługi (może być powiązany z pętlą w innym wątku). Z drugiej strony funkcja view.post () zawsze będzie działać w głównym wątku
— Yair Kukielka

4

Myślę, że problemem może być metoda cyklu życia, w której wywołujesz metodę post (). Czy robisz to w onCreate ()? jeśli tak, spójrz na to, co znalazłem w dokumentacji onResume () działania:

onResume ()

Dodano na poziomie API 1 void onResume () Wywoływany po onRestoreInstanceState (Bundle), onRestart () lub onPause (), aby Twoja aktywność zaczęła wchodzić w interakcję z użytkownikiem. To dobre miejsce do rozpoczęcia animacji , otwierania urządzeń z wyłącznym dostępem (takich jak kamera) itp.

https://developer.android.com/reference/android/app/Activity.html#onResume ()

Tak więc, jak powiedział Joe Plante, być może widok nie jest gotowy do rozpoczęcia animacji w momencie wywołania post (), więc spróbuj przenieść go do onResume ().

PD: Właściwie, jeśli przeniesiesz kod do onResume (), myślę, że możesz usunąć wywołanie post (), ponieważ jesteś już w wątku interfejsu użytkownika, a widok powinien być gotowy do rozpoczęcia animacji.


3
onResumemoże być wywoływane wiele razy (ekrany przechodzą w stan uśpienia, aktywność przenoszona do tyłu itp.) po tym, jak początkowo „widok jest gotowy”. Jeśli zostanie wywołana z onResume, może być potrzebna flaga do śledzenia pogody, której animacja została już uruchomiona, aby uniknąć (ponownego) uruchomienia się wiele razy.
— samis
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.