Использование try / catch для предотвращения сбоев приложения


Я работаю на Android приложение, которое использует try/catch часто, чтобы предотвратить его от падения даже в местах, где нет необходимости. Например,

вид xml layout С id = toolbar ссылка типа:

// 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) {
}

этот подход используется во всем приложении. Трассировка стека не печатается, и очень трудно найти, что пошло не так. Приложение закрывается внезапно без печати какой-либо трассировки стека.

Я попросил моего старшего объяснить мне это и он сказал:

это для предотвращения сбоев в производстве.

я с этим совершенно не согласен. Для меня это не способ предотвратить сбои приложений. Это показывает, что разработчик не знать, что он/она делает и находится в сомнении.

используется ли этот подход в промышленности для предотвращения сбоев корпоративных приложений?

если try/catch это действительно, действительно наша потребность, то это возможно чтобы прикрепить обработчик исключений с потоком пользовательского интерфейса или другими потоками и поймать все там? Это будет лучший подход, если это возможно.

да, пустой try/catch плохо, и даже если мы печатаем трассировку стека или исключение журнала на сервер, обертывание блоков кода в try/catch случайно через все приложение не имеет смысла для меня, например, когда каждая функция заключена в try/catch.

обновление

как этот вопрос получил много внимания и некоторые люди неправильно истолковали вопрос (возможно, потому, что я не сформулировал его четко), я собираюсь перефразировать его.

вот что здесь делают разработчики

  • пишется функция и проверен, это может быть небольшая функция, которая просто инициализирует представления или сложный, после тестирования он обернут вокруг try/catch блок. Даже для функции, которая никогда не будет бросать никаких исключений.

  • эта практика используется во всем приложении. Когда - то трассировка стека печатается, а когда-то просто debug log С некоторым случайным сообщением об ошибке. Это сообщение об ошибке отличается от разработчика к разработчику.

  • при таком подходе приложение не падает, но поведение приложения становится неопределенным. Иногда даже трудно понять, что пошло не так.

  • настоящий вопрос, который я задавал, был;это практика, следующая в отрасли для предотвращение сбоев корпоративных приложений? и я не прошу о пустой try / catch. Это похоже на то, что пользователи любят приложения, которые не разбиваются, чем приложения, которые ведут себя неожиданно? Потому что это действительно сводится к тому, чтобы либо разбить его, либо представить пользователю пустой экран, либо пользователь поведения не знает.

  • я публикую несколько фрагментов из реального кода

      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) {
        }
    }
    

It это не дубликат Android обработка исключений лучше всего практика, там ОП пытается поймать исключение для разные цель, чем этот вопрос.

14 72

14 ответов:

конечно, всегда есть исключения из правил, но если вам нужно, правило - то вы правы, пустые блоки catch являются "абсолютно" плохой практикой.

давайте посмотрим поближе, сначала начав с вашего конкретного примера:

try {
  View view = findViewById(R.id.toolbar);
}
catch(Exception e) { }

Итак, ссылка на что-то создается; и когда это не удается ... это не имеет значения, потому что ссылка не используется в первую очередь! Приведенный выше код абсолютно бесполезный шум линии. Или все-таки человек, написавший этот код, изначально предполагал, что второй подобный вызов волшебным образом больше не будет вызывать исключение?!

может быть, это должно выглядеть так:

try {
  View view = findViewById(R.id.toolbar);
  ... and now do something with that view variable ...
}
catch(Exception e) { }

но опять же, что это помогает?! Исключения существуют для того, чтобы общаться соответственно распространение ошибки в коде. Игнорирование ошибок редко бывает хорошей идеей. На самом деле, исключение можно рассматривать такими способами, как:

  • вы даете обратную связь пользователь; (например: "введенное значение не является строкой, попробуйте еще раз"); или заниматься более сложной обработкой ошибок
  • возможно, проблема каким-то образом ожидается и может быть смягчена (например, давая ответ "по умолчанию", когда какой-то "удаленный поиск" не удался)
  • ...

короче говоря: the минимум то, что вы делаете с исключением, - это регистрировать / отслеживать его; так что, когда вы приходите позже, отлаживая какую-то проблему, вы понимаете "хорошо, на этом время, когда произошло исключение".

и как другие указали: вы также избегаете ловли для исключение в общем (ну, в зависимости от слоя: там могут быть веские причины для того, чтобы поймать какую для исключение, и даже некоторые виды ошибок на самом высоком уровне, чтобы убедиться, что ничего теряется; когда-нибудь).

наконец, давайте цитата Уорд Каннингем:

вы знайте, что вы работаете с чистым кодом, когда каждая процедура, которую Вы читаете, оказывается в значительной степени тем, что вы ожидали. Вы можете назвать это красивым кодом, когда код также делает его похожим на язык, созданный для этой проблемы.

пусть это утонет и медитировать об этом. Чистый код не удивит вас. Пример, который вы показываете нам, удивляет все глядя на.

обновление, в связи с обновлением, что ОП просит о

try {
  do something
}
catch(Exception e) { 
  print stacktrace
}

тот же ответ: делать это "повсюду" также плохо практика. Потому что этот код и удивляет читателя.

выше:

  • печатает информацию об ошибке где-то. Это не все гарантировано, что это "где-то" напоминает разумный назначения. Наоборот. Пример: в приложении, с которым я работаю, такие вызовы волшебным образом появляются в наши буферы трассировки. В зависимости от контекста, наше приложение может иногда накачивать тонны и тонны данных в эти буферы; делая эти буферные обрезки каждые несколько секунд. Поэтому "просто ошибки печати "часто переводится как:"просто потерять ошибку всю эту информацию".
  • затем: вы не пытаетесь / поймать, потому что вы можете. Вы делаете это, потому что понимаете, что делает ваш код; и вы знаете: мне лучше попробовать / поймать здесь, чтобы сделать правильную вещь (см. первые части моего ответа снова.)

Итак, используя try / catch в качестве "шаблона", как вы показываете; как сказано: все еще не очень хорошая идея. И да, это предупреждает сбой; но приводит ко всем видам "неопределенного" поведения. Вы знаете, когда вы просто ловите исключение вместо правильно общаясь с ним, вы открываете банку с червями, потому что вы можете столкнуться с мириадами на ошибки, которые вы позже не понимаю. Потому что вы использовали событие "первопричина" ранее; напечатал его где-то; и что где-то теперь уже нет.

С документация для Android:

давайте назовем его как -

не поймать общее исключение

также может быть заманчиво быть ленивым при ловле исключений и делать что-то вроде этого:

try {
    someComplicatedIOFunction();        // may throw IOException
    someComplicatedParsingFunction();   // may throw ParsingException
    someComplicatedSecurityFunction();  // may throw SecurityException
    // phew, made it all the way
} catch (Exception e) {                 // I'll just catch all exceptions
    handleError();                      // with one generic handler!
}

почти во всех случаях неуместно ловить generic Exception или Throwable (предпочтительно не Throwable, потому что он включает исключения ошибок). Это очень опасно, потому что это означает что исключения вы никогда не ожидали (в том числе RuntimeExceptions как ClassCastException) попасть в обработку ошибок на уровне приложения.

он скрывает свойства обработки сбоев вашего кода, что означает, если кто-то добавляет новый тип Exception в коде, который вы вызываете, компилятор не поможет вам понять, что вам нужно обрабатывать ошибку по-другому.

альтернативы ловле общего исключения:

  • поймать каждое исключение отдельно как отдельные блоки catch после одной попытки. Это может быть неудобно, но все же предпочтительнее, чем ловить все исключения.
    редактировать автор: это мой выбор.остерегайтесь повторять слишком много кода в блоках catch. Если вы используете Java 7 или выше, используйте multi-catch, чтобы избежать повторения одного и того же блока catch.
  • рефакторинг кода, чтобы иметь более мелкие ошибки обработка, с несколькими блоками try. Разделите IO от разбора, обрабатывайте ошибки отдельно в каждом случае.
  • повторно бросить исключение. Много раз вам не нужно ловить исключение на этом уровне в любом случае, просто позвольте методу бросить его.

в большинстве случаев вы не должны обрабатывать различные типы исключений одинаково.

форматирование / абзацы слегка измененный из источника для этого ответа.

P. S. Не бойтесь исключений!! Они же друзья!!!

Я бы поставил это как комментарий к какому-то другому ответу, но у меня пока нет репутации для этого.

вы правы, говоря, что это плохая практика, на самом деле то, что вы опубликовали, показывает различные типы плохой практики в отношении исключений.

  1. отсутствие обработки ошибок
  2. Общий Улов
  3. никаких преднамеренных исключений
  4. одеяло попробовать/поймать

Я постараюсь объяснить все это через это образец.

try {
   User user = loadUserFromWeb();     
   if(user.getCountry().equals("us")) {  
       enableExtraFields();
   }
   fillFields(user);   
} catch (Exception e) { 
}

это может привести к сбою несколькими способами, которые должны обрабатываться по-разному.

  1. поля не будут заполнены, поэтому пользователю выдается пустой экран, а затем... что? Ничего-отсутствие обработки ошибок.
  2. нет никакого различия между различными типами ошибок, например, проблемы с Интернетом или проблемы с самим сервером (отключение, сломанный запрос, поврежденная передача,...) - Общий улов.
  3. вы не можете использовать исключения для ваших собственных целей, потому что текущая система мешает этому. - Никаких преднамеренных исключений
  4. несущественные и неожиданные ошибки (например, null.равняется.(..)) может привести к тому, что существенный код не будет выполняться. - Одеяло попробовать / поймать

решений

(1) Во-первых, отказ молча не является хорошей вещью. Если есть сбой, приложение не будет работать. Вместо этого должна быть попытка решить проблему или вывести предупреждение, например "не удалось загрузить пользовательские данные, может быть, вы не подключены к интернету?". Если приложение не делает то, что должно, это еще больше расстраивает пользователя, чем если бы оно просто закрывалось.

(4) если пользователь является неполным, например, страна не известна и возвращает null. Метод equals создаст исключение NullPointerException. Если этот NPE просто брошен и пойман, как указано выше, метод fillFields(user) не будет вызван, даже если он все еще может быть выполнен без проблемы. Вы можете предотвратить это, включив нулевые проверки, изменив порядок выполнения или настроив область try/catch. (Или вы могли бы сохранить кодирование следующим образом:"us".равно (пользователь.getCountry ()), но я должен был привести пример). Конечно, любое другое исключение также предотвратит выполнение fillFields (), но если нет пользователя, вы, вероятно, не хотите, чтобы он выполнялся в любом случае.

(1, 2, 3)Загрузка из интернета часто вызывает различные исключения, от IOException до HttpMessageNotReadable исключение-даже просто возвращение. Может быть, пользователь не подключен к интернету, может быть, произошло изменение на бэкэнд - сервер или он не работает, но вы не знаете, потому что вы ловите(исключение) - вместо этого вы должны ловить конкретные исключения. Вы даже можете поймать несколько из них, как это

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();
}

надеюсь, это все объясняет.

по крайней мере, как быстрое решение, что вы могли бы сделать, это отправить событие на ваш сервер с исключением. Например, через военнослужащих или приложений. Таким образом, вы можете хотя бы увидеть такие вещи, как (Эй, основная деятельность не загружается для 80% наших пользователей из-за проблемы, такой как (4).

Это определенно плохая практика программирования.

из текущего сценария, если есть сотни try

но вы можете включить регистратор, так что вы узнаете, когда исключение кидается (и почему). Это не изменит ваш обычный рабочий процесс.

...
try {
    View view = findViewById(R.id.toolbar);
}catch(Exception e){
    logger.log(Level.SEVERE, "an exception was thrown", e);
}
...

Это плохая практика. Другие ответы сказали это, но я думаю, что важно отступить и понять почему у нас есть исключения в первую очередь.

каждая функция имеет пост-условие – набор вещей, которые должны быть истинными после выполнения этой функции. Например, функция, которая читает из файла, имеет условие post, что данные в файле будут считаны с диска и возвращены. Исключение, таким образом, возникает, когда функция имеет не удалось выполнить одно из его постусловий.

игнорируя исключение из функции (или даже эффективно игнорируя его, просто регистрируя исключение), вы говорите, что вы в порядке с этой функцией, фактически не выполняя всю работу, которую она согласилась сделать. Это кажется маловероятным – если функция не работает правильно, нет никакой гарантии, что следующее будет работать вообще. И если остальная часть вашего кода работает нормально, независимо от того, выполняется ли определенная функция до завершения, то один интересно, почему у вас есть эта функция в первую очередь.

[теперь есть определенные случаи, когда пустой catch es в порядке. Например, ведение журнала-это то, что вы можете оправдать переносом в пустой улов. Ваше приложение, вероятно, будет работать нормально, даже если некоторые из журналов не могут быть записаны. Но это особые случаи, которые вам нужно очень много работать, чтобы найти в обычном приложении.]

Итак, дело в том, что это плохая практика, потому что она фактически не поддерживает работу вашего приложения (предполагаемое оправдание этого стиля). Может быть, технически ОС не убила его. Но маловероятно, что приложение все еще работает правильно после простого игнорирования исключения. И в худшем случае, это действительно может нанести вред (например, повреждение пользовательских файлов и т. д.).

это плохо по нескольким причинам:

  1. что ты делаешь, что findViewById выдает исключение? Исправьте это (и скажите мне, потому что я никогда этого не видел) вместо того, чтобы ловить.
  2. не поймать Exception когда вы можете поймать определенный тип исключения.
  3. есть такое мнение, что хорошие приложения не рушатся. Это неправда. Хорошее приложение аварийно завершает работу, если это необходимо.

Если приложение переходит в плохом состоянии, это гораздо лучше, чем авария чтобы он пыхтел в своем непригодном состоянии. Когда вы видите NPE, вы не должны просто вставлять нулевую проверку и уходить. Лучший подход-выяснить, почему что-то равно null, и либо остановить его от null, либо (если null становится допустимым и ожидаемым состоянием) проверить null. Но вы должны понять, почему проблема возникает в первую очередь.

Я разрабатывал приложения для android в течение последних 4-5 лет и никогда не использовал try catch для инициализации просмотра.

если его панель инструментов сделать так

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

например: - чтобы получить TextView из представления (фрагмент / диалог / любой пользовательский вид)

TextView textview = (TextView) view.findViewById(R.id.viewId);

виджет TextView виджет TextView = (виджет TextView) смотреть.команду findViewById(Р. ИД.viewid, которые);

вместо

View view = findViewById(R.id.toolbar);

вид объекта имеет минимальный охват по сравнению с его реальным видом тип.

обратите внимание:- может быть, он разбился, потому что вид был загружен. Но добавление try catch-это плохая практика.

да, try / catch используется для предотвращения сбоя приложения, но вам, конечно, не нужно try/catch для получения представления из XML, как показано в вашем вопросе.

try / catch обычно используется при выполнении любого http-запроса, при разборе любой строки на URL, создании URL-соединений и т. д. а также не забудьте распечатать трассировку стека. Не печать это не имеет большого смысла в окружении его с try / catch.

Как было сказано ранее, общие исключения не должны поймать, или по крайней мере только в несколько центральные места (обычно расположенные в коде фреймворка/инфраструктуры, а не в коде приложения). Если вы ловите общие исключения и регистрируете их, приложение должно быть закрыто после этого или, по крайней мере, пользователь должен быть проинформирован о том, что приложение потенциально находится в нестабильном состоянии и может произойти повреждение данных (если пользователь решит продолжить выполнение). Потому что это то, что может произойти если вы поймаете все виды исключений (из памяти, чтобы назвать один) и оставить приложение в неопределенном состоянии.

IMHO хуже проглотить исключения и рисковать целостностью данных, потерей данных или просто оставить приложение в неопределенном состоянии, чем позволить приложению сбой, и пользователь знает, что что-то пошло не так, и может попробовать еще раз. Это также приведет к улучшению сообщений о проблемах (больше в корне проблемы), вероятно, меньше различных симптомов, чем если бы ваши пользователи начали сообщать обо всех видах проблем, возникающих из неопределенного состояния приложения.

после централизованной обработки исключений / ведения журнала / отчетности и контролируемого завершения работы, начните перезаписывать обработку исключений, чтобы поймать локальные исключения как можно более конкретно. Постарайтесь сделать блок try{} как можно короче.

позвольте мне высказать свою точку зрения, как парень, работающий в корпоративной мобильной индустрии уже более десяти лет. Во-первых, некоторые общие советы по исключениям, большинство из которых включены в ответы выше:

  • исключения должны использоваться для исключительных, неожиданных или неконтролируемых ситуаций, а не на регулярной основе во всем коде.
  • программист должен знать части кода, восприимчивые к исключениям броска и попытаться поймать их, оставляя остальную часть кода как можно чище.
  • исключения не следует оставлять без внимания, как правило.

теперь, когда вы разрабатываете приложение не для себя, а для компании или корпорации, обычно возникают дополнительные требования по этой теме:

  • "приложение вылетает шоу плохой имидж компании, поэтому они не приемлемы". Затем следует провести тщательную разработку, и ловить даже маловероятные исключения может быть вариантом. Если да, то это должно быть выборочно и держится в разумных пределах. И обратите внимание, что разработка не все о строках кода, например, интенсивный процесс тестирования имеет решающее значение в этих случаях. Однако неожиданное поведение в корпоративном приложении хуже, чем сбой. Таким образом, всякий раз, когда вы ловите исключение в своем приложении, вы должны знать, что делать, что показывать и как приложение будет вести себя дальше. Если вы не можете контролировать это, лучше пусть приложение аварии.
  • "журналы и трассировки стека могут сбрасывать конфиденциальную информацию к консоли. Это может быть использовано для злоумышленника, поэтому по соображениям безопасности они не могут использоваться в производственной среде". Это требование противоречит общему правилу для разработчика не писать молчаливые исключения, поэтому вы должны найти способ для этого. Например, ваше приложение может управлять средой, поэтому оно использует журналы и трассировки стека в непроизводственных средах, используя облачные инструменты, такие как bugsense, crashlitics или аналогичные для производства окружающая среда.

Итак, короткий ответ заключается в том, что код, который вы нашли, не является примером хорошей практики, поскольку его трудно и дорого поддерживать без улучшения качества приложения.

другая перспектива, как кто-то, кто пишет корпоративное программное обеспечение на ежедневной основе, если приложение имеет неустранимую ошибку, я хочу это к краху. Грохот желателен. Если он падает,он регистрируется. Если он падает более чем несколько раз за короткий промежуток времени, я получаю электронное письмо, в котором говорится, что приложение падает, и я могу проверить, что наше приложение и все веб-службы, которые мы потребляем, все еще работают.

Итак, вопрос:

Is

try{
  someMethod();
}catch(Exception e){}

хорошая практика? Нет! Вот несколько моментов:

  1. самое главное: это плохой опыт работы с клиентами. Откуда мне знать, когда происходит что-то плохое? Мои клиенты пытаются использовать мое приложение и ничего не работает. Они не могут проверить свой банковский счет, оплатить свои счета, независимо от того, что делает мое приложение. Мое приложение совершенно бесполезно, но эй, по крайней мере, он не разбился! (Часть меня считает, что этот "старший" Дев получает очки брауни за низкий крах цифры, так что они играют в систему.)

  2. когда я занимаюсь разработкой и пишу плохой код, если я просто ловлю и проглатываю все исключения на верхнем уровне, у меня нет журнала. У меня ничего нет в моей консоли, и мое приложение не работает молча. Насколько я могу судить, все работает нормально. Поэтому я совершаю код... Оказывается, мой объект DAO был null все время, и платежи клиента никогда не обновлялись в БД. Упс! Но мое приложение не разбилось, так что это плюс.

  3. играя адвоката дьявола, скажем, я был в порядке с ловлей и глотанием каждого исключения. Это очень просто написать пользовательский обработчик исключений в Android. Если вы действительно просто есть, чтобы поймать любое исключение, вы можете сделать это в одном месте и не Перец try/catch по всей вашей кодовой базе.

некоторые разработчики, с которыми я работал в прошлом, думала, что сбой был плохой.

Я должен заверить их, что мы хочу наше приложение к краху. Нет, нестабильное приложение не в порядке, но сбой означает, что мы сделали что-то не так, и нам нужно это исправить. Чем быстрее он падает, тем раньше мы его находим, тем легче его исправить. Единственный другой вариант, который я могу придумать, - это позволить пользователю продолжить прерванную сессию, которую я приравниваю к тому, чтобы разозлить мою пользовательскую базу.

Это плохая практика, чтобы использовать catch(Exception e){} потому что вы по существу игнорируете ошибку. То, что вы, вероятно, хотите сделать, это что-то вроде:

try {
    //run code that could crash here
} catch (Exception e) {
    System.out.println(e.getMessage());
}

мы довольно много используем вашу же логику. Используйте try-catch чтобы предотвратить приложения от сбоев.

исключения должны быть никогда игнорируется. Это плохая практика кодирования. Ребятам, поддерживающим код, будет очень сложно локализовать часть кода, которая вызвала исключение, если они не вошли в систему.

мы используем:Crashlytics для регистрации исключений. Код не будет аварийно завершаться (но некоторые функции будут нарушены). Но вы получаете журнал исключений приборная панель Fabric/Crashlytics. Вы можете посмотреть на эти журналы и исправить исключения.

try {
    codeThatCouldRaiseError();
} catch (Exception e) {
    e.printStackTrace();
    Crashlytics.logException(e);
}
private int foo=0;

    . . .

public int getFoo() throws SomeException { return foo; }

в этом случае метод 'getFoo ()'не могу не - всегда будет законное значение частного поля 'foo', которое будет возвращено. но кто - то-вероятно, по педантичным причинам-решил, что этот метод должен быть объявлен как потенциально бросая исключение. если вы затем попытаетесь вызвать этот метод в контексте-например, обработчик событий - который не позволяет создавать исключение, вы в основном вынуждены использовать эту конструкцию (даже тогда, я согласен, что нужно хотя бы регистрировать исключение на всякий случай). Всякий раз, когда мне приходится это делать, я всегда, по крайней мере, добавляю большой жирный комментарий "это не может произойти" рядом с предложением "catch".