Zakończ starą czynność i rozpocznij nową lub odwrotnie

80

Wiem, że otrzymuję ten sam wynik w przypadku obu fragmentów kodu

finish();
startActivity(newActivity);

i

startActivity(newActivity);
finish();

Chciałbym poznać Twoją opinię, jeśli jest między nimi duża różnica. Czy jeden jest lepszy od drugiego? Jeśli tak, dlaczego?

Tima
źródło

Odpowiedzi:

61

Kiedy wykonujesz startActivity (), wszystko, co robi, to umieszczanie intencji w kolejce zdarzeń. Faktyczne rozpoczęcie działania następuje asynchronicznie w najbliższej przyszłości. Więc nie widzę dużej różnicy między tymi dwoma.

Emmanuel
źródło
1
dla mnie też brzmi to sensownie
Tima
6
Animacja jest dla mnie inna. Zobacz moją odpowiedź poniżej.
Monstieur
ale podejście jest po prostu całkowicie NIEPOPRAWNE .. Istnieją flagi (dla intencji również w manifeście), które faktycznie istnieją dla tych przypadków użycia.
Ewoks
@Ewoki, proszę, idźcie, co oznacza, które flagi?
Anthony
3
W rzeczywistości istnieje duża różnica w zachowaniu zadań aplikacji. Omówiłem sprawę w osobnej odpowiedzi.
Vit Khudenko
27

Animacja jest wyraźnie inna (przynajmniej od 4.1). finish()Pierwsze wywołanie zaczyna zanikać pierwsze działanie wcześniej i możesz przez chwilę zobaczyć czarne tło przed zanikiem nowej czynności. Wywołanie startActivity()pierwsze zanika w nowej czynności na poprzedniej, a czarne tło nie jest widoczne.

Monstieur
źródło
16

Istnieje ważna różnica w zachowaniu zadań aplikacji w zależności od kolejności startActivity()i finish()wywołań.

Przypadek, który opisuję, dotyczy tylko sytuacji, gdy bieżąca czynność (zatrzymywana) jest jedyną w zadaniu.

Zwykle można by się spodziewać, że zamiar rozpoczęcia (zamiar, który tworzysz, aby rozpocząć inną czynność) nie zostanie zmieniony przez system. I tak nie jest, jeśli finish()wywoływana jest ostatnia czynność w zadaniu przed wywołaniem startActivity().

W tym przypadku ActivityManager, komponent systemowy, podczas wykonywania flagi startActivity() add Intent.FLAG_ACTIVITY_NEW_TASK do celu.

Kiedy tak się dzieje, można zauważyć wpis dziennika w LogCat podobny do tego:

W / ActivityManager: startActivity wywołane z zakończenia ActivityRecord {4a19b47 u0 com.foo.bar/com.foo.bar.SplashActivity t4928 f}; wymuszanie zamiaru.FLAG_ACTIVITY_NEW_TASK dla: zamiaru {cmp = com.foo.bar / com.foo.bar.MainActivity}

I to jest punkt zwrotny, od którego (w pewnych warunkach) sprawy mogą się nie udać.

Podsumowując, jeśli chcesz być po bezpiecznej stronie (zamiast doświadczać nieoczekiwanych skutków ubocznych FLAG_ACTIVITY_NEW_TASKdodania do intencji), to kolejność musi być:

  • startActivity()
  • finish()

Projekt demonstracyjny .

Nagrania z ekranu:

Vit Khudenko
źródło
Patrząc na kod źródłowy, Activity.startActivity () ostatecznie wywołuje ActivityThread.sendActivityResult (), która z kolei wywołuje scheduleSendResult (), co po prostu dodaje działanie do kolejki. Wątek przetworzy to później. Z drugiej strony finish () wywołuje ActivityManagerNative.finishActivity (), co natychmiast kończy działanie.
Emmanuel
@Emmanuel, nie rozumiem, czy twoje oświadczenie dotyczy opisanego przeze mnie problemu. :)
Vit Khudenko
9

Oprócz odpowiedzi Emmanuels:

Obie metody startActivityi finishzostaną zaplanowane po zakończeniu metody wywołującej, ponieważ obie są przetwarzane przez wątek interfejsu użytkownika.

scheffield
źródło
7

Zrobiłbym drugi wybór, nie popieram tego na niczym, co wyszukałem w oficjalnych źródłach, ale bardziej sensowne jest rozpoczęcie nowej czynności przed wywołaniem zakończenia, w ten sposób nowa aktywność pojawia się z powodu zamiaru , a działanie w tle teraz może wywołać wszystkie swoje metody czyszczenia.

Gdybyś zrobił to na odwrót, być może zamiar nie będzie miał czasu na odpalenie przed zakończeniem czyszczenia. To znaczy, czy aktywność wywoła startActivity () po wywołaniu finish ()?

Mam nadzieję, że rozumiesz, co próbuję powiedzieć, zrobiłbym drugą opcję, aby być bezpiecznym.

ślepy
źródło
Tak, zrozumiałem twój pomysł. Brzmi logicznie. Ale nie wyobrażam sobie, kiedy taka sytuacja byłaby możliwa
Tima
2

Miałem podobny problem:

Activity A: singleInstance
Activity B: singleInstance
Activity C: singleInstance

A starts B 
B starts C
C wants to start A:

tutaj, jeśli używam:

finish();
startActivity(A);

dzieje się coś przewodowego: działanie B pojawia się na pierwszym planie zamiast A! ale jeśli zmienię kod w ten sposób:

startActivity(A);
finish();

wszystko wydaje się w porządku i widać Aktywność A.

Nie wiem, w czym jest problem, ale wydaje się, że w pierwszym przypadku C kończy się przed wykonaniem polecenia startActivity, tak że tylny stos radzi sobie z sytuacją i pokazuje jego najwyższą aktywność, czyli B! ale w drugim przypadku wszystko dzieje się normalnie.

narges
źródło
Kiedy kończysz () aplikację, Android pobiera aktywność LRU ze stosu, czyli B. Ponadto, co sprawia, że ​​myślę, że finish () jest natychmiastowa i nie jest asynchroniczna.
Mehmet AVŞAR
0

Zwykle robię to startActivity()wcześniej, finish()ponieważ myślę, że to upewniłoby się, że nowy ekran pojawi się, zanim poprzedni zgaśnie.

Mam stronę logowania w mojej aplikacji. Po pomyślnym zalogowaniu użytkownika aktywność logowania zniknęła, a główna aktywność została zakończona. Działa dobrze w systemie Android 4.

Dzisiaj chciałem przepisać to w Material Design. Jednak mam duży problem. Nowe studio Android tworzy puste zadania z Material Design, które moim zdaniem wymagają wielu zasobów. Ten sam proces, ale wystąpił błąd

11-26 18:20:44.450 18397-18397/? I/Choreographer: Skipped 42 frames!  The application may be doing too much work on its main thread.
11-26 18:20:44.485 18397-18408/? I/art: Background partial concurrent mark sweep GC freed 2864(191KB) AllocSpace objects, 4(43MB) LOS objects, 13% free, 100MB/116MB, paused 8.056ms total 39.767ms

Powiedział, że moje aplikacje zajmują wiele zasobów podczas mainActivityuruchamiania w moim dzienniku telefonu. Nie mam nicmainActivity tym, że jest to domyślny układ Material Design.

Odwróciłem kolejność i teraz działa bezbłędnie na moim telefonie.

w kapeluszu
źródło