Aplikacja może wykonywać zbyt wiele pracy w głównym wątku

379

Jestem nowy w środowisku Android SDK / API. To pierwszy próbuję narysować wykres / wykres. Próbowałem uruchomić różne rodzaje przykładowych kodów emulatora przy użyciu 3 różnych bezpłatnych bibliotek, nic nie wyświetla się na ekranie układu. Logcat powtarza następujący komunikat:

 W / Trace (1378): Nieoczekiwana wartość z nativeGetEnabledTags: 0
 I / Choreographer (1378): Pominięto 55 klatek! Aplikacja może wykonywać zbyt wiele pracy w głównym wątku.

Problem nie utrzymywał się, a wykres działał, gdy uruchomiłem przykładowy kod dotyczący kopii testowej licencjonowanej biblioteki.

użytkownik2038135
źródło
2
Czy rysujesz swoje wykresy w osobnym wątku?
Areks
Dzięki za komentarz, zredagowałem pytanie, aby było bardziej jasne. Aktywność podczas uruchamiania pokazuje, że prowadzę działanie, które nie ma zaprojektowanego układu => pokazuje biały ekran.
user2038135
1
@Areks Nie, nie używam osobnego wątku.
user2038135
1
Myślę, że powinieneś, nie jest wcale zalecane wykonywanie długich operacji na głównym wątku, ponieważ to zamraża całą aplikację, możesz przeczytać tutaj, jak korzystać z wątków: stackoverflow.com/questions/3391272/... Zignoruj ​​kod "do zrobienia żądanie HTTP ”i po prostu wykonaj tam potencjalnie długie operacje.
Areks
1
Dlaczego nie spróbujesz szukać, znajdziesz informacje o choreografie. Polecam przeczytać tę odpowiedź: stackoverflow.com/questions/11266535/…
Gabriel Esteban

Odpowiedzi:

479

zaczerpnięte z: Android UI: Naprawianie pomijanych ramek

Każdy, kto zaczyna tworzyć aplikację na Androida, widzi ten komunikat na logcat „Choreographer (abc): Pominięto ramki xx! Aplikacja może wykonywać zbyt wiele pracy w głównym wątku. ” Co to właściwie oznacza, dlaczego powinieneś się martwić i jak to rozwiązać.

Oznacza to, że twój kod długo się przetwarza, a ramki są z tego powodu pomijane. Być może z powodu jakiegoś ciężkiego przetwarzania, które wykonujesz w sercu aplikacji lub dostępu do bazy danych, lub jakiejkolwiek innej rzeczy, która powoduje, że wątek zatrzymaj się na chwilę.

Oto bardziej szczegółowe wyjaśnienie:

Choreographer pozwala aplikacjom łączyć się z vsync i odpowiednio mierzyć czas w celu poprawy wydajności.

Animacje widoku Androida wykorzystują Choreographer do tego samego celu: do prawidłowego synchronizowania animacji i ewentualnej poprawy wydajności.

Ponieważ Choreographer jest informowany o każdym zdarzeniu vsync, mogę stwierdzić, czy jedno z Runnables przekazanych przez Choreographer.post * apis nie kończy się w czasie jednej klatki, powodując pomijanie klatek.

W moim rozumieniu Choreograf może wykryć tylko pomijanie klatek. Nie sposób powiedzieć, dlaczego tak się dzieje.

Komunikat „Aplikacja może wykonywać zbyt wiele pracy w głównym wątku”. może wprowadzać w błąd.

źródło: Znaczenie komunikatów choreografa w Logcat

Dlaczego powinieneś się martwić?

Gdy ten komunikat wyskakuje na emulatorze Androida, a liczba pomijanych ramek jest dość mała (<100), możesz bezpiecznie założyć, że emulator jest wolny - co zdarza się prawie cały czas. Ale jeśli liczba ramek jest pomijana i duża, a rzędu 300+, mogą wystąpić poważne problemy z kodem. Urządzenia z Androidem są dostępne w szerokiej gamie sprzętu, w przeciwieństwie do urządzeń iOS i Windows. Pamięć RAM i procesor są różne i jeśli chcesz mieć rozsądną wydajność i wrażenia użytkownika na wszystkich urządzeniach, musisz to naprawić. Gdy klatki są pomijane, interfejs użytkownika jest powolny i opóźniony, co nie jest pożądanym doświadczeniem użytkownika.

Jak to naprawić

Naprawienie tego wymaga identyfikacji węzłów, w których istnieje lub może się zdarzyć długi czas przetwarzania. Najlepszym sposobem jest wykonanie całego przetwarzania bez względu na to, jak mały lub duży w wątku oddzielnym od głównego wątku interfejsu użytkownika. Czy to będzie uzyskiwanie dostępu do danych z bazy danych SQLite, wykonywanie trudnych matematyki lub po prostu sortowanie tablicy - zrób to w innym wątku

Teraz jest tu haczyk. Utworzysz nowy wątek do wykonywania tych operacji, a po uruchomieniu aplikacji zawiesi się komunikat „Tylko oryginalny wątek, który utworzył hierarchię widoków, może dotknąć jego widoków”. Musisz wiedzieć, że interfejs użytkownika w Androidzie można zmienić tylko przez główny wątek lub tylko wątek interfejsu użytkownika. Każdy inny wątek, który próbuje to zrobić, zawiesza się i ulega awarii z tym błędem. Co musisz zrobić, to utworzyć nowy Runnable wewnątrz runOnUiThread i wewnątrz tego runnable powinieneś wykonać wszystkie operacje z udziałem interfejsu użytkownika. Znajdź przykład tutaj .

Mamy więc Thread i Runnable do przetwarzania danych z głównego wątku, co jeszcze? W Androidzie jest AsyncTask, który umożliwia wykonywanie długich procesów w wątku interfejsu użytkownika. Jest to najbardziej przydatne, gdy aplikacje są sterowane danymi lub interfejsami WWW lub używają złożonych interfejsów użytkownika, takich jak te budowane przy użyciu Canvas. Moc AsyncTask polega na tym, że pozwala robić rzeczy w tle, a po zakończeniu przetwarzania możesz po prostu wykonać wymagane czynności w interfejsie użytkownika, nie powodując żadnych opóźnień. Jest to możliwe, ponieważ AsyncTask wywodzi się z wątku interfejsu użytkownika działania - wszystkie operacje wykonywane na interfejsie użytkownika za pomocą AsyncTask są inne niż główny wątek interfejsu użytkownika. Brak przeszkód dla interakcji użytkownika.

To jest to, co musisz wiedzieć, aby tworzyć płynne aplikacje na Androida i o ile wiem, każdy początkujący otrzymuje tę wiadomość na swojej konsoli.

Jorgesys
źródło
41
Mam tylko aplikację, w której po kliknięciu przycisku obraz tła przycisku zmienia się, a przycisk jest nie do odczytania. Jak robię za dużo pracy :(
Remian8985,
1
@ Remian8985 - Zmiana obrazu tła dla przycisku (przy założeniu, że pobierasz ten obraz) powinna zostać wykonana w AsyncTask - to znaczy, aby wykonać operację pobierania tła i opublikować wynik w wątku interfejsu użytkownika (podać obraz z powrotem). Zobacz Android referencyjny Link
BenJaminSila
11
@BenJaminSila zmienia tło w AsyncTask? Naprawdę?
użytkownik25
11
@ user25 „zakładając, że
forresthopkinsa
„Gdy ten komunikat wyskakuje na emulatorze Androida, a liczba pomijanych ramek jest dość mała (<100), możesz bezpiecznie założyć, że emulator jest powolny” Czy to nadal obowiązuje? Emulatory stają się dość szybkie, prawda?
Robin Dijkhof,
243

Jak inni odpowiedzieli powyżej, „Pominęło 55 klatek!” oznacza, że ​​w twojej aplikacji jest trochę intensywnego przetwarzania.

W moim przypadku moja aplikacja nie wymaga intensywnego procesu. Podwójnie i potrójnie sprawdziłem wszystko i usunąłem proces, który moim zdaniem był trochę ciężki.

Usunąłem Fragmenty, Aktywności, Biblioteki, dopóki nie został tylko szkielet. Ale problem nie zniknął. Postanowiłem sprawdzić zasoby i znalazłem kilka ikon i tła, z których korzystam, są dość duże, ponieważ zapomniałem sprawdzić rozmiar tych zasobów.

Sugeruję więc, że jeśli żadna z powyższych odpowiedzi nie pomoże, możesz również sprawdzić rozmiar plików zasobów.

Sithu
źródło
1
Pracował również dla mnie. Miałem aplikację, która wykonywała bardzo mało pracy, ale była powolna i opóźniona. Ciągle dostawałem pomijane logi ramek. Po usunięciu tła z mojej aktywności wszystko było w porządku. Dzięki!
akrabi
Świetna odpowiedź, uważam, że to był dokładnie mój problem. Wypróbowałem kilka innych (dość zaangażowanych) rozwiązań, a aplikacja działała równie wolno. Wyciągnąłem wszystkie usługi sieciowe i próbowałem zoptymalizować mój kod aż do kości. Nie działało, to zobaczyłem. Gdy tylko usunę mój obraz tła (największy obraz, jaki mam), aplikacja działa tak szybko, jak można klikać różne elementy, nawet przy starym „wolnym” kodzie.
M Barbosa,
uczyniłeś mój dzień!
Nicolas Mastromarino,
:) Jesteś absurdalnym geniuszem.
Metin Ilhan,
@ Batszewa nie musi być 1 KB. To zależy od twoich potrzeb, powiedzmy, że potrzebujesz wyraźniejszego obrazu, możesz użyć wyższej rozdzielczości, ale upewnij się, że podzieliłeś się na różne rozmiary w różnych folderach zasobów.
Sithu,
61

Ja też miałem ten sam problem.
Mój był przypadek, w którym używałem obrazu tła, który był w drawable. Ten konkretny obraz miał około 130kB i ​​był używany podczas ekranu powitalnego i strony głównej w mojej aplikacji na Androida.

Rozwiązanie - właśnie przeniosłem ten konkretny obraz do folderu drawables-xxx z drawable i mogłem zwolnić dużo pamięci zajmowanej w tle, a ramki pomijania nie były już pomijane.

Aktualizacja Użyj folderu zasobów do rysowania „nodp” do przechowywania plików rysowanych w tle.
Czy pierwszeństwo ma folder z możliwością rysowania lub nodpi z możliwością rysowania?

Prakhar1001
źródło
7
Przeniosłem duży obraz tła z rysowalnej na mimap-xxxhdpi i udało się!
bgplaya
Bardzo ci pomogłeś. Dzięki
N.Droid
2
To rozwiązanie rozwiązuje problem. drawable-xxxhdpiZamiast drawabletego używam folderu, co radykalnie zmniejsza zużycie pamięci (~ 70 procent mniej). Warto również wiedzieć, że ekrany o tym samym rozmiarze różnią się rozmiarem DPI. Stosunek w pikselach między nimi jest ldpi = 1:0.75, mdpi = 1:1, hdpi = 1:1.5, xhdpi = 1:2, xxhdpi = 1:3, xxxhdpi = 1:4. Korzystając z drawable-xxxhdpifolderu, możesz zmniejszyć skalę obrazów do rozmiaru ekranu urządzenia, co zmniejsza zużycie pamięci i procesora.
Timo Bähr
2
Przenoszenie obrazów z drawabledo drawable-nodpiuniemożliwia uzyskanie aplikacji Out of Memory Error.
Shruti
O mój boże ... dzięki! Miałem obraz w folderze do rysowania, co spowodowało, że moja aplikacja działała jak diabli (choć obraz miał tylko 100 KB !!!). Po wygenerowaniu plików drawable-xxx (korzystałem z Android Drawable Importer) moja aplikacja jest cholernie szybka. Wielkie dzięki!
błąd1337,
20

Inną częstą przyczyną opóźnień w wątku interfejsu użytkownika jest dostęp do SharedPreferences. Po pierwszym wywołaniu PreferenceManager.getSharedPreferencesi podobnych metod skojarzony plik .xml jest natychmiast ładowany i analizowany w tym samym wątku .

Jednym z dobrych sposobów rozwiązania tego problemu jest uruchomienie pierwszego obciążenia SharedPreference z wątku w tle, uruchomionego jak najwcześniej (np. Z onCreateklasy Application). W ten sposób obiekt preferencji może być już skonstruowany do czasu, kiedy chcesz go użyć.

Niestety, czasami czytanie plików preferencji jest konieczne na wczesnych etapach uruchamiania (np. W początkowej czynności lub nawet samej aplikacji). W takich przypadkach nadal można uniknąć zablokowania interfejsu użytkownika przy użyciu MessageQueue.IdleHandler. Zrób wszystko, co musisz wykonać w głównym wątku, a następnie zainstaluj IdleHandler, aby wykonać kod po pełnym narysowaniu działania. W tym Runnable powinieneś mieć dostęp do SharedPreferences bez opóźniania zbyt wielu operacji rysowania i powodowania niezadowolenia choreografa.

użytkownik1643723
źródło
1
W takim przypadku powinieneś preferować metodę apply () zamiast commit (). Metoda Apply () nie może blokować interfejsu użytkownika. Możesz tutaj szukać developer.android.com/training/data-storage/shared-preferences
Emre Gürses
16

Spróbuj zastosować następujące strategie, aby poprawić wydajność aplikacji:

  • Jeśli to możliwe, użyj programowania wielowątkowego. Korzyści z wydajności są ogromne, nawet jeśli smartfon ma jeden rdzeń (wątki mogą działać w różnych rdzeniach, jeśli procesor ma dwa lub więcej). Przydatne jest oddzielenie logiki aplikacji od interfejsu użytkownika. Użyj wątków Java, AsyncTask lub IntentService. Sprawdź to .
  • Przeczytaj i postępuj zgodnie ze wskazówkami dotyczącymi wydajności na stronie poświęconej programowaniu Androida. Sprawdź tutaj .
MigDus
źródło
3
Twój pierwszy link wymaga, abyś „... miał zweryfikowane konto ...”, aby uzyskać do niego dostęp.
chornge
9

Miałem ten sam problem. Emulator Androida działał doskonale na Androidzie <6.0. Kiedy korzystałem z emulatora Nexusa 5 (Android 6.0), aplikacja działała bardzo wolnoI/Choreographer: Skipped frames w logach.

Rozwiązałem ten problem, zmieniając hardwareAcceleratedopcję pliku manifestu truetak, aby :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapplication">

    <application android:hardwareAccelerated="true">
        ...
    </application>
</manifest>
phen0menon
źródło
8

Nie jestem ekspertem, ale dostałem ten komunikat debugowania, gdy chciałem wysłać dane z mojej aplikacji na Androida na serwer WWW. Chociaż użyłem klasy AsyncTask i przeszedłem dane w tle, do odzyskania danych wynikowych z serwera użyłem metody get () klasy AsyncTask, która sprawia, że ​​interfejs użytkownika jest synchroniczny, co oznacza, że ​​interfejs użytkownika będzie czekał zbyt długo. Dlatego radzę, aby Twoja aplikacja wykonywała wszystkie zadania sieciowe w osobnym wątku.

saba
źródło
6

Zoptymalizuj swoje zdjęcia ... Nie używaj obrazów większych niż 100 KB ... Ładowanie zdjęć zajmuje zbyt dużo procesora i powoduje zawieszanie się aplikacji.

HarshitG
źródło
4
Zmniejsz rozmiar obrazu za pomocą kodu Java lub użyj programu Photoshop do przycinania obrazów .. kompresuj również obrazy przy użyciu compressor.io
HarshitG
5

Miałem ten sam problem. W moim przypadku miałem 2 zagnieżdżone układy względne. RelativeLayout zawsze musi wykonać dwa pomiary. Po zagnieżdżeniu RelativeLayouts otrzymujemy wykładniczy algorytm pomiaru.

Radosław
źródło
4

zwykle dzieje się tak, gdy wykonujesz ogromne procesy w głównym wątku. możesz pominąć ramki mniejsze niż 200. ale jeśli masz więcej niż 200 pominiętych ramek, może to spowolnić Twój wątek interfejsu aplikacji. co możesz zrobić, to wykonać te procesy w nowym wątku zwanym wątkiem roboczym, a następnie, gdy chcesz uzyskać dostęp i zrobić coś z wątkiem interfejsu użytkownika (np. zrobić coś z widokami, findView itp.), możesz użyć modułu obsługi lub runOnUiThread (Podoba mi się to bardziej), aby wyświetlić wyniki przetwarzania. to absolutnie rozwiązuje problem. używanie wątków roboczych jest bardzo przydatne, a nawet należy je stosować w takich przypadkach.

Hossein Karami
źródło
1

Miałem ten sam problem. Kiedy uruchomiłem kod na innym komputerze, zadziałało dobrze. Jednak mój wyświetlał komunikat „Aplikacja może wykonywać zbyt wiele pracy w głównym wątku”.

Rozwiązałem problem, uruchamiając ponownie studio Android [Plik -> Unieważnione pamięci podręczne / Uruchom ponownie -> kliknij „Unieważnij i uruchom ponownie”].

sonida
źródło
Nie wiem, dlaczego twoje rozwiązanie zadziałało. W każdym razie dzięki.
Bhuvanesh BS,
1

W moim przypadku było tak, ponieważ przypadkowo ustawiłem punkt przerwania dla metody. Gdy go usunąłem, komunikat zniknął, a wydajność znacznie się poprawiła.

FractalBob
źródło
0

Moja aplikacja miała ten sam problem. Ale nie robił nic poza wyświetlaniem na nim listy kart i tekstu. Nic nie działa w tle. Ale potem, po pewnym dochodzeniu, ustalono, że przyczyną tego był zestaw obrazów tła karty, mimo że był mały (350 kb). Następnie przekonwertowałem obraz na 9-częściowe obrazy za pomocą http://romannurik.github.io/AndroidAssetStudio/index.html .
To zadziałało dla mnie.

naamadheya
źródło
0

Po przeprowadzeniu wielu prac badawczo-rozwojowych w tej kwestii otrzymałem Rozwiązanie,

W moim przypadku korzystam z usługi, która będzie uruchamiana co 2 sekundy, a wraz z runonUIThread zastanawiałem się, czy problem istnieje, ale wcale. Kolejnym problemem, który znalazłem, jest to, że używam dużego obrazu w aplikacji May i to jest problem.

Usunąłem Obrazy i ustawiłem nowe Obrazy.

Wniosek: - Sprawdź, czy w kodzie jest dowolny plik raw, którego używasz, ma duży rozmiar.

Hector Morris
źródło
0

Najpierw przeczytaj ostrzeżenie. Mówi więcej obciążenia głównego wątku. Musisz więc po prostu uruchamiać funkcje z większą ilością pracy w wątku.

de_billa_
źródło
-1

Mam ten sam problem podczas tworzenia aplikacji, która używa wielu rysowalnych plików png w układzie siatki. Próbowałem również zoptymalizować mój kod tak dalece, jak to możliwe .. ale to nie zadziałało dla mnie .. Potem próbowałem zmniejszyć rozmiar tych png .. i zgaduję, że działa absolutnie dobrze .. Więc moja sugestia to zmniejszyć wielkość zasobów do wyciągnięcia, jeśli występują ..

Shubham Ranakoti
źródło