Pracowałem nad aplikacją na Androida, która try/catch
często używa, aby zapobiec awariom nawet w miejscach, w których nie ma takiej potrzeby. Na przykład,
Do widoku w xml layout
z id = toolbar
odwołuje się:
// see new example below, this one is just confusing
// it seems like I am asking about empty try/catch
try {
View view = findViewById(R.id.toolbar);
}
catch(Exception e) {
}
To podejście jest używane w całej aplikacji. Ślad stosu nie jest drukowany i naprawdę trudno jest stwierdzić, co poszło nie tak. Aplikacja zamyka się nagle bez drukowania śladu stosu.
Poprosiłem seniora, żeby mi to wyjaśnił, a on powiedział:
Ma to na celu zapobieganie awariom w produkcji.
Całkowicie się z tym nie zgadzam . Dla mnie nie jest to sposób na zapobieganie awariom aplikacji. Pokazuje, że programista nie wie, co robi i ma wątpliwości.
Czy jest to podejście stosowane w przemyśle w celu zapobiegania awariom aplikacji korporacyjnych?
Jeśli try/catch
tak, naprawdę potrzebujemy, to czy możliwe jest dołączenie procedury obsługi wyjątków z wątkiem interfejsu użytkownika lub innymi wątkami i przechwycenie tam wszystkiego? Jeśli to możliwe, będzie to lepsze podejście.
Tak, puste try/catch
jest złe i nawet jeśli drukujemy ślad stosu lub rejestrujemy wyjątek na serwerze, try/catch
losowe zawijanie bloków kodu w całej aplikacji nie ma dla mnie sensu, np. Gdy każda funkcja jest ujęta w try/catch
.
AKTUALIZACJA
Ponieważ pytanie to wzbudziło wiele uwagi, a niektórzy błędnie je zinterpretowali (być może dlatego, że nie sformułowałem go jasno), zamierzam je przeformułować.
Oto, co robią tutaj programiści
Funkcja jest pisana i testowana , może to być mała funkcja, która po prostu inicjuje widoki lub złożona, po przetestowaniu jest zawijana wokół
try/catch
bloku. Nawet dla funkcji, która nigdy nie zgłosi żadnego wyjątku.Ta praktyka jest stosowana w całej aplikacji. Czasami drukowany jest ślad stosu, a czasami tylko
debug log
z przypadkowym komunikatem o błędzie. Ten komunikat o błędzie różni się w zależności od programisty.Dzięki takiemu podejściu aplikacja nie ulega awarii, ale zachowanie aplikacji staje się nieokreślone. Nawet czasami trudno jest śledzić, co poszło nie tak.
Prawdziwe pytanie, które zadawałem, brzmiało; Czy jest to praktyka stosowana w branży w celu zapobiegania awariom aplikacji korporacyjnych? i nie pytam o puste try / catch . Czy to tak, że użytkownicy uwielbiają aplikacje, które nie ulegają awarii niż aplikacje, które zachowują się nieoczekiwanie? Ponieważ tak naprawdę sprowadza się to do awarii lub pokazania użytkownikowi pustego ekranu lub zachowania, którego użytkownik nie jest świadomy.
Wstawiam tutaj kilka fragmentów prawdziwego kodu
private void makeRequestForForgetPassword() { try { HashMap<String, Object> params = new HashMap<>(); String email= CurrentUserData.msisdn; params.put("email", "blabla"); params.put("new_password", password); NetworkProcess networkProcessForgetStep = new NetworkProcess( serviceCallListenerForgotPasswordStep, ForgotPassword.this); networkProcessForgetStep.serviceProcessing(params, Constants.API_FORGOT_PASSWORD); } catch (Exception e) { e.printStackTrace(); } } private void languagePopUpDialog(View view) { try { PopupWindow popupwindow_obj = popupDisplay(); popupwindow_obj.showAsDropDown(view, -50, 0); } catch (Exception e) { e.printStackTrace(); } } void reloadActivity() { try { onCreateProcess(); } catch (Exception e) { } }
To nie jest duplikat najlepszych praktyk w zakresie obsługi wyjątków Androida , OP próbuje złapać wyjątek w innym celu niż to pytanie.
źródło
catch(Exception e){}
- ten komentarz miał sens przed edycją pytaniaON ERROR RESUME NEXT
Odpowiedzi:
Oczywiście zawsze są wyjątki od reguł, ale jeśli potrzebujesz praktycznej reguły - masz rację; puste bloki catch są „absolutnie” złą praktyką.
Przyjrzyjmy się bliżej, zaczynając od konkretnego przykładu:
try { View view = findViewById(R.id.toolbar); } catch(Exception e) { }
Powstaje więc odniesienie do czegoś; a kiedy to zawiedzie ... to nie ma znaczenia; ponieważ to odniesienie nie jest używane w pierwszej kolejności! Powyższy kod jest absolutnie bezużytecznym szumem liniowym . A może osoba, która napisała ten kod, początkowo zakłada, że drugie, podobne wywołanie w magiczny sposób nie rzuci już wyjątku ?!
Może to miało wyglądać tak:
try { View view = findViewById(R.id.toolbar); ... and now do something with that view variable ... } catch(Exception e) { }
Ale znowu, co to pomaga ?! Istnieją wyjątki do komunikowania się odpowiednio propagować sytuacje błędów w kodzie. Ignorowanie błędów rzadko jest dobrym pomysłem. Właściwie wyjątek można potraktować w następujący sposób:
Krótko mówiąc: minimalną rzeczą, którą robisz z wyjątkiem jest rejestrowanie / śledzenie go; tak, że kiedy przyjdziesz później do debugowania jakiegoś problemu, zrozumiesz „OK, w tym momencie zdarzył się wyjątek”.
I jak inni zauważyli: generalnie unikasz również wyłapywania wyjątków (cóż, w zależności od warstwy: mogą być dobre powody, aby złapać jakiś wyjątek , a nawet niektóre rodzaje błędów na najwyższym poziomie, aby upewnić się, że nic się nie gubi; nigdy ).
Na koniec zacytujmy Warda Cunninghama:
Wiesz, że pracujesz z czystym kodem, gdy każda przeczytana procedura okazuje się być prawie taka, jakiej się spodziewałeś. Możesz nazwać to pięknym kodem, gdy kod również sprawia, że wygląda na to, że język został stworzony dla problemu.
Pozwólcie temu zagłębić się i rozmyślajcie o tym. Czysty kod Cię nie zaskakuje. Przykład, który nam pokazujesz, zaskakuje wszystkich .
Aktualizacja dotycząca aktualizacji, o którą pyta PO
try { do something } catch(Exception e) { print stacktrace }
Ta sama odpowiedź: robienie tego „wszędzie” również jest złą praktyką. Ponieważ ten kod również zaskakuje czytelnika.
Powyższe:
Więc używając try / catch jako „wzorca”, tak jak pokazujesz; jest jak powiedział: nadal nie jest to dobry pomysł. I tak, zapobiega awariom; ale prowadzi do wszelkiego rodzaju „nieokreślonych” zachowań. Wiesz, kiedy po prostu łapiesz wyjątek, zamiast odpowiednio sobie z nim radzić; otwierasz puszkę robaków; ponieważ możesz napotkać mnóstwo następczych błędów, których później nie rozumiesz. Ponieważ wcześniej wykorzystałeś zdarzenie „główna przyczyna”; wydrukował to gdzieś; i to gdzieś już nie ma.
źródło
Z dokumentacji Androida :
Nazwijmy to jako -
Formatowanie / akapity nieznacznie zmienione w stosunku do źródła tej odpowiedzi.
PS Nie bój się wyjątków !! Są przyjaciółmi!!!
źródło
Object a; try { a = thing(); } catch (Exception e) {...} try { a.action(); } catch (Exception e) {...}
))Exception
może być przydatne jako część systemu "log-before-crash", chociaż w takim przypadku powinno być ponownie.Dodałbym to jako komentarz do innej odpowiedzi, ale nie mam jeszcze takiej reputacji.
Masz rację, mówiąc, że jest to zła praktyka, w rzeczywistości to, co opublikowałeś, pokazuje różne rodzaje złych praktyk w odniesieniu do wyjątków.
Spróbuję to wszystko wyjaśnić na tym przykładzie.
try { User user = loadUserFromWeb(); if(user.getCountry().equals("us")) { enableExtraFields(); } fillFields(user); } catch (Exception e) { }
Może to się nie powieść na kilka sposobów, które należy traktować inaczej.
Rozwiązania
(1) Po pierwsze, cicha porażka nie jest dobra. Jeśli wystąpi awaria, aplikacja nie będzie działać. Zamiast tego należy podjąć próbę rozwiązania problemu lub wyświetlić ostrzeżenie, na przykład „Nie można załadować danych użytkownika, może nie masz połączenia z Internetem?”. Jeśli aplikacja nie robi tego, co powinna, jest to o wiele bardziej frustrujące dla użytkownika, niż gdyby po prostu się zamknęła.
(4) Jeśli Użytkownik jest niekompletny, np. Kraj nie jest znany i zwraca wartość null. Metoda equals utworzy wyjątek NullPointerException. Jeśli ten NPE zostanie po prostu wyrzucony i przechwycony jak powyżej, metoda fillFields (user) nie zostanie wywołana, mimo że nadal można ją bezproblemowo wykonać. Można temu zapobiec, włączając sprawdzanie wartości null, zmieniając kolejność wykonywania lub dostosowując zakres try / catch. (Lub możesz zapisać kodowanie w ten sposób: "nas" .equals (user.getCountry ()), ale musiałem podać przykład). Oczywiście każdy inny wyjątek również uniemożliwi wykonanie funkcji fillFields (), ale jeśli nie ma użytkownika, prawdopodobnie i tak nie chcesz, aby było ono wykonywane.
(1, 2, 3) Ładowanie z sieci WWW często generuje różne wyjątki, od wyjątku IOException do HttpMessageNotReadable, a nawet po prostu zwracanie. Możliwe, że użytkownik nie jest połączony z Internetem, może nastąpiła zmiana na serwerze zaplecza lub jest on wyłączony, ale nie wiesz, ponieważ przechwytujesz (wyjątek) - zamiast tego powinieneś przechwytywać określone wyjątki. Możesz nawet złapać kilka z nich
try{ User user = loadUserFromWeb(); //throws NoInternetException, ServerNotAvailableException or returns null if user does not exist if(user == null) { throw new UserDoesNotExistException(); //there might be better options to solve this, but it highlights how exceptions can be used. } fillFields(user); if("us".equals(user.getCountry()) { enableExtraFields(); } } catch(NoInternetException e){ displayWarning("Your internet conneciton is down :("); } catch(ServerNotAvailableException e){ displayWarning("Seems like our server is having trouble, try again later."); } catch(UserDoesNotExistException e){ startCreateUserActivity(); }
Mam nadzieję, że to wyjaśnia.
Przynajmniej jako szybką naprawę możesz wysłać zdarzenie do zaplecza z wyjątkiem. Na przykład przez firebase lub crashlytics. W ten sposób możesz przynajmniej zobaczyć takie rzeczy jak (hej, główna aktywność nie ładuje się dla 80% naszych użytkowników z powodu problemu takiego jak (4).
źródło
To zdecydowanie zła praktyka programistyczna.
Z obecnego scenariusza, jeśli jest ich setki
try
catch
, nie będziesz nawet wiedział, gdzie występuje wyjątek bez debugowania aplikacji, co jest koszmarem, jeśli Twoja aplikacja znajduje się w środowisku produkcyjnym.Ale możesz dołączyć rejestrator, aby wiedzieć, kiedy zgłaszany jest wyjątek (i dlaczego). Nie zmieni to Twojego normalnego przepływu pracy.
... try { View view = findViewById(R.id.toolbar); }catch(Exception e){ logger.log(Level.SEVERE, "an exception was thrown", e); } ...
źródło
To zła praktyka. Inne odpowiedzi mówią tak, ale myślę, że ważne jest, aby cofnąć się i zrozumieć, dlaczego mamy wyjątki.
Każda funkcja ma warunek końcowy - zestaw rzeczy, które muszą być spełnione po wykonaniu tej funkcji. Na przykład funkcja odczytująca z pliku ma warunek post, że dane w pliku zostaną odczytane z dysku i zwrócone. Wyjątek jest zatem generowany, gdy funkcja nie była w stanie spełnić jednego z warunków końcowych.
Ignorując wyjątek od funkcji (lub nawet skutecznie go ignorując, po prostu rejestrując wyjątek), mówisz, że nie przeszkadza ci ta funkcja, która tak naprawdę nie wykonuje całej pracy, na którą się zgodził. Wydaje się to mało prawdopodobne - jeśli funkcja nie działa poprawnie, nie ma gwarancji, że to, co następuje, w ogóle zadziała. A jeśli reszta twojego kodu działa dobrze, niezależnie od tego, czy dana funkcja działa do końca, to można się zastanawiać, dlaczego masz tę funkcję w pierwszej kolejności.
[Teraz są pewne przypadki, w których puste
catch
es są w porządku. Na przykład rejestrowanie jest czymś, co można uzasadnić zawijaniem pustego połowu. Twoja aplikacja prawdopodobnie będzie działać dobrze, nawet jeśli nie będzie można zapisać niektórych danych logowania. Ale są to szczególne przypadki, w których trzeba naprawdę ciężko pracować, aby znaleźć je w normalnej aplikacji.]Chodzi o to, że jest to zła praktyka, ponieważ tak naprawdę nie powoduje to, że aplikacja działa (rzekome uzasadnienie tego stylu). Być może technicznie system operacyjny go nie zabił. Ale jest mało prawdopodobne, aby aplikacja nadal działała poprawnie po zignorowaniu wyjątku. W najgorszym przypadku może to faktycznie wyrządzić szkodę (np. Uszkodzić pliki użytkownika itp.).
źródło
Jest to złe z wielu powodów:
findViewById
powoduje wyjątek? Napraw to (i powiedz mi, bo nigdy tego nie widziałem) zamiast łapać.Exception
kiedy możesz złapać określony typ wyjątku.Jeśli aplikacja przejdzie w zły stan, znacznie lepiej jest zawiesić ją, niż ciągnąć w stanie bezużytecznym. Kiedy widzi się NPE, nie należy po prostu trzymać się czeku zerowego i odchodzić. Lepszym podejściem jest ustalenie, dlaczego coś jest zerowe i albo powstrzymanie tego przed byciem null, albo (jeśli null stanie się prawidłowym i oczekiwanym stanem) sprawdź, czy nie ma wartości null. Ale musisz zrozumieć, dlaczego problem występuje w pierwszej kolejności.
źródło
Tworzę aplikacje na Androida przez ostatnie 4-5 lat i nigdy nie użyłem try catch do inicjalizacji widoku.
Jeśli jest to pasek narzędzi, zrób to w ten sposób
np: - Aby uzyskać TextView z widoku (fragment / dialog / dowolny niestandardowy widok)
TextView textview = (TextView) view.findViewById (R.id.viewId);
zamiast tego
Obiekt widoku ma minimalny zasięg w porównaniu do jego rzeczywistego typu widoku.
Uwaga: - Możliwe, że się zawiesił, ponieważ widok został załadowany. Ale dodanie próby złapania jest złą praktyką.
źródło
View
które mogą być już przydatne (np.setVisibility()
), Bez określania dokładnie, która klasa jest używana. (na przykład w przypadku układu, który od czasu do czasu może ulec zmianie)Tak, try / catch służy do zapobiegania awariom aplikacji, ale z pewnością nie potrzebujesz try / catch do pobrania widoku z XML, jak przedstawiono w pytaniu.
try / catch jest zwykle używane podczas wykonywania dowolnego żądania http, podczas przetwarzania dowolnego ciągu znaków do adresu URL, tworzenia połączeń URL itp., a także upewnij się, że drukujesz ślad stosu. Brak drukowania nie ma większego sensu w otaczaniu go try / catch.
źródło
Jak wspomniano wcześniej, nie należy łapać ogólnych wyjątków, a przynajmniej tylko w kilku centralnych miejscach (zwykle zlokalizowanych w kodzie frameworka / infrastruktury, a nie w kodzie aplikacji). W przypadku wychwytywania wyjątków ogólnych i rejestrowania go, aplikacja powinna zostać później zamknięta lub przynajmniej użytkownik powinien zostać poinformowany, że aplikacja jest potencjalnie w stanie niestabilnym i może dojść do uszkodzenia danych (jeśli użytkownik zdecyduje się kontynuować wykonywanie). Ponieważ tak może się zdarzyć, jeśli złapiesz wszelkiego rodzaju wyjątki (zabraknie pamięci, aby je nazwać) i pozostawisz aplikację w niezdefiniowanym stanie.
IMHO gorzej jest połknąć wyjątki i ryzykować integralność danych, utratę danych lub po prostu pozostawić aplikację w nieokreślonym stanie, niż pozwolić, aby aplikacja uległa awarii, a użytkownik wie, że coś poszło nie tak i może spróbować ponownie. Spowoduje to również lepsze zgłaszanie problemów (bardziej u źródła problemu), prawdopodobnie mniej różnych objawów, niż gdyby użytkownicy zaczęli zgłaszać wszelkiego rodzaju problemy wynikające z niezdefiniowanego stanu aplikacji.
Po wdrożeniu centralnej obsługi / rejestrowania / raportowania wyjątków i kontrolowanego zamykania należy rozpocząć przepisywanie obsługi wyjątków, aby wychwycić lokalne wyjątki tak szczegółowe, jak to tylko możliwe. Postaraj się, aby blok try {} był jak najkrótszy.
źródło
Pozwólcie, że dodam mój punkt widzenia, jako faceta pracującego w branży rozwoju urządzeń mobilnych od ponad dziesięciu lat. Najpierw kilka ogólnych wskazówek dotyczących wyjątków, większość z nich znajduje się w powyższych odpowiedziach:
Teraz, gdy nie tworzysz aplikacji dla siebie, ale dla firmy lub korporacji, często spotykasz dodatkowe wymagania w tym temacie:
Krótka odpowiedź jest taka, że znaleziony kod nie jest przykładem dobrej praktyki, ponieważ jest trudny i kosztowny w utrzymaniu bez poprawy jakości aplikacji.
źródło
Z innej perspektywy, ponieważ ktoś, kto codziennie pisze oprogramowanie dla przedsiębiorstw, jeśli aplikacja zawiera nieodwracalny błąd, chcę , aby się zawiesił. Rozbijanie się jest pożądane. Jeśli ulegnie awarii, zostanie zarejestrowany. Jeśli zawiesza się więcej niż kilka razy w krótkim czasie, otrzymuję wiadomość e-mail z informacją, że aplikacja ulega awarii i mogę sprawdzić, czy nasza aplikacja i wszystkie używane przez nas usługi internetowe nadal działają.
Więc pytanie:
Jest
try{ someMethod(); }catch(Exception e){}
dobra praktyka? Nie! Oto kilka punktów:
NAJWAŻNIEJSZA rzecz: to zła obsługa klienta. Skąd mam wiedzieć, że dzieje się coś złego? Moi klienci próbują używać mojej aplikacji i nic nie działa. Nie mogą sprawdzić swojego konta bankowego, opłacić rachunków, cokolwiek robi moja aplikacja. Moja aplikacja jest całkowicie bezużyteczna, ale hej, przynajmniej się nie zawiesiła! (Część mnie wierzy, że ten „starszy” programista dostaje dodatkowe punkty za niską liczbę awarii, więc grają w system).
Kiedy robię programowanie i piszę zły kod, jeśli po prostu łapię i połykam wszystkie wyjątki w górnej warstwie, nie mam logowania. Nie mam nic w konsoli, a moja aplikacja cicho nie działa. Z tego, co wiem, wszystko wydaje się działać dobrze. Więc zatwierdzam kod ... Okazuje się, że mój obiekt DAO był przez cały czas zerowy, a płatności klienta w rzeczywistości nigdy nie były aktualizowane w DB. Ups! Ale moja aplikacja nie uległa awarii, więc to plus.
Udając adwokata diabła, powiedzmy, że nie przeszkadza mi łapanie i połykanie każdego wyjątku. To bardzo proste , aby napisać klasę obsługi wyjątku w Androidzie. Jeśli naprawdę musisz złapać każdy wyjątek, możesz to zrobić w jednym miejscu i nie zasypywać
try/catch
całej bazy kodu.Niektórzy deweloperzy, z którymi pracowałem w przeszłości, uważali, że awarie były złe.
Muszę ich zapewnić, że chcemy , aby nasza aplikacja uległa awarii. Nie, niestabilna aplikacja nie jest w porządku, ale awaria oznacza, że zrobiliśmy coś źle i musimy to naprawić. Im szybciej się zawiesza, tym wcześniej go znajdziemy, tym łatwiej jest to naprawić. Jedyną inną opcją, jaką przychodzi mi do głowy, jest umożliwienie użytkownikowi kontynuowania zepsutej sesji, co utożsamiam z wkurzaniem mojej bazy użytkowników.
źródło
Jest to zła praktyka,
catch(Exception e){}
ponieważ zasadniczo ignorujesz błąd. Prawdopodobnie chcesz zrobić coś bardziej na przykład:try { //run code that could crash here } catch (Exception e) { System.out.println(e.getMessage()); }
źródło
W zasadzie używamy tej samej logiki. Służy
try-catch
do zapobiegania awariom aplikacji produkcyjnych.NIGDY nie należy ignorować wyjątków . To zła praktyka kodowania. Facetom zajmującym się kodem będzie naprawdę trudno zlokalizować część kodu, która wywołała wyjątek, jeśli nie zostaną zalogowani.
Używamy
Crashlytics
do rejestrowania wyjątków. Kod nie ulegnie awarii (ale niektóre funkcje zostaną zakłócone). Ale dziennik wyjątków pojawia się na pulpicie nawigacyjnym programuFabric/Crashlytics
. Możesz przejrzeć te dzienniki i naprawić wyjątki.try { codeThatCouldRaiseError(); } catch (Exception e) { e.printStackTrace(); Crashlytics.logException(e); }
źródło
Chociaż zgadzam się z innymi odpowiedziami, jest jedna okoliczność, z którą wielokrotnie się spotykałem, w której ten motyw jest marginalnie tolerowany. Załóżmy, że ktoś napisał fragment kodu dla klasy w następujący sposób:
private int foo=0; . . . public int getFoo() throws SomeException { return foo; }
W takiej sytuacji metoda „getFoo ()” nie może zawieść - zawsze będzie zwrócona prawidłowa wartość pola prywatnego „foo”. Jednak ktoś - prawdopodobnie z powodów pedantycznych - zdecydował, że ta metoda powinna zostać uznana za potencjalnie rzucającą Wyjątek. Jeśli następnie spróbujesz wywołać tę metodę w kontekście - np. Program obsługi zdarzeń - który nie pozwala na wyrzucenie wyjątku, jesteś w zasadzie zmuszony do użycia tej konstrukcji (nawet wtedy zgadzam się, że należy przynajmniej zarejestrować wyjątek po prostu w przypadku) . Ilekroć muszę to zrobić, zawsze przynajmniej dodaję duży, gruby komentarz „TO NIE MOŻE WYSTĄPIĆ” obok klauzuli „catch”.
źródło