Nice programing

앱 충돌 방지를 위해 try / catch 사용

nicepro 2020. 10. 14. 20:59
반응형

앱 충돌 방지를 위해 try / catch 사용


try/catch필요없는 곳에서도 충돌을 방지하기 위해 자주 사용하는 Android 앱을 개발 하고 있습니다. 예를 들면

의보기는 다음 xml layoutid = 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정말 우리의 필요 다음, 정말이 UI 스레드 또는 다른 스레드 거기 캐치 모든 것에 예외 핸들러를 첨부 할 수 있습니까? 가능하다면 더 나은 접근 방식이 될 것입니다.

예, 비어있는 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) {
        }
    }
    

Android 예외 처리 모범 사례의 중복 아니며 OP 가이 질문 과 다른 목적으로 예외를 포착하려고합니다 .


물론 규칙에는 항상 예외가 있지만 경험 규칙이 필요하면 맞습니다. 빈 캐치 블록은 "절대적으로"나쁜 습관입니다.

먼저 구체적인 예부터 자세히 살펴 보겠습니다.

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

그러나 이것은 무엇을 도와 주는가?! 예외가 존재 의사 소통 각각 전파 코드에서 오류 상황. 오류를 무시하는 것은 좋은 생각이 아닙니다. 실제로 예외는 다음과 같은 방식으로 처리 될 수 있습니다.

  • 사용자에게 피드백을 제공합니다. (예 : "입력 한 값이 문자열이 아닙니다. 다시 시도하십시오."); 또는 더 복잡한 오류 처리에 참여
  • 아마도 문제가 예상 된 것이며 완화 될 수 있습니다 (예 : 일부 "원격 검색"이 실패한 경우 "기본"응답을 제공함).
  • ...

간단히 말해서 , 예외로 할 수 있는 최소한의 일은 로그 / 추적입니다. 나중에 일부 문제를 디버깅 할 때 "OK,이 시점에서 예외가 발생했습니다"를 이해할 수 있습니다.

그리고 다른 사람들이 지적했듯이 일반적으로 Exception잡는 것을 피할 수 있습니다. (글쎄요, 레이어에 따라 : Exception을 잡을 수있는 좋은 이유가있을 수 있으며 , 심지어 최고 수준에서 어떤 종류의 오류가 있는지 확인하기 위해 아무것도 분실하지, 지금까지 ).

마지막으로 Ward Cunningham을 인용 해 보겠습니다 .

당신이 읽는 각 루틴이 당신이 예상했던 것과 거의 같을 때 당신은 깨끗한 코드로 작업하고 있다는 것을 알고 있습니다. 코드가 문제의 언어로 만들어진 것처럼 보이게 만들 때 아름다운 코드라고 부를 수 있습니다.

그것에 대해 묵상 해보세요. 깨끗한 코드는 당신을 놀라게 하지 않습니다 . 당신이 우리에게 보여주고있는 예는 보고있는 모두놀라게 합니다.

OP가 묻는 업데이트 에 관한 Update

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

같은 대답 : "모든 곳에서"그렇게하는 것도 나쁜 습관입니다. 이 코드이기 때문에 또한 독자를 놀라게.

위 :

  • 어딘가에 오류 정보를 인쇄합니다. 그것은이다 전혀없는 이 "어딘가"는 유사 보장 적절한 대상을. 반대로. 예 : 내가 작업중인 애플리케이션 내에서 이러한 호출은 추적 버퍼에 마술처럼 나타납니다. 상황에 따라 애플리케이션은 때때로 이러한 버퍼로 수많은 데이터를 펌핑 할 수 있습니다. 버퍼를 몇 초마다 정리합니다. 따라서 "단순한 인쇄 오류"는 종종 "단순히 모든 오류 정보를 잃어버린 것"으로 해석됩니다.
  • 그런 다음 : 당신이 때문에 당신은 시도 / 캐치하지 않을 수 있습니다 . 코드가 무엇을하는지 이해하기 때문에 그렇게합니다. 그리고 알다시피 : 나는 올바른 일을하기 위해 여기에 try / catch가있는 것이 좋습니다 (내 대답의 첫 번째 부분을 다시 참조하십시오).

그래서, 당신이 보여주는 것처럼 try / catch를 "패턴"으로 사용합니다; 아직 좋은 생각이 아닙니다. 그리고 예, 충돌 방지합니다 . 그러나 모든 종류의 "정의되지 않은"동작으로 이어집니다. 알다시피, 예외를 제대로 처리하는 대신 그냥 잡을 때 ; 당신은 벌레 캔을 엽니 다. 나중에 이해할 수없는 수많은 후속 오류가 발생할 수 있기 때문 입니다. 이전에 "근본 원인"이벤트를 사용했기 때문입니다. 어딘가에 인쇄했습니다. 그리고 그 어딘가 는 이제 사라졌습니다.


로부터 안드로이드 문서 :

다음과 같이 자격을 부여합시다.

일반 예외를 포착하지 마십시오

예외를 잡을 때 게으르고 다음과 같이하는 것도 유혹이 될 수 있습니다.

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!
}

거의 모든 경우에 일반 Exception또는 Throwable 을 포착하는 것은 부적절합니다 (오류 예외를 포함하므로 Throwable이 바람직하지 않음). 당신이 (를 포함하여 예상 결코 예외 의미하기 때문에 매우 위험하다 RuntimeExceptions처럼이 ClassCastException) 응용 프로그램 수준 오류 처리에 잡힐.

이는 코드의 오류 처리 속성을 모호 Exception하게 만듭니다. 즉, 호출하는 코드에 누군가 새로운 유형을 추가 하면 컴파일러가 오류를 다르게 처리해야한다는 사실을 깨닫는 데 도움이되지 않습니다 .

일반 예외 포착에 대한 대안 :

  • 한 번의 시도 후 각 예외를 개별 catch 블록으로 개별적으로 catch하십시오. 이것은 어색 할 수 있지만 모든 예외를 포착하는 것보다 여전히 바람직합니다.
    저자에 의한 편집 : 이것은 내 선택입니다. catch 블록에서 너무 많은 코드를 반복하지 않도록주의하십시오. Java 7 이상을 사용하는 경우 동일한 catch 블록을 반복하지 않으려면 multi-catch를 사용하십시오.
  • 여러 개의 try 블록을 사용하여 더 세분화 된 오류 처리를 갖도록 코드를 리팩터링합니다 . 파싱에서 IO를 분리하고 각 경우에 개별적으로 오류를 처리합니다.
  • 예외를 다시 발생 시킵니다. 어쨌든이 수준에서 예외를 잡을 필요가없는 경우가 많습니다. 메소드가 예외를 던지도록하십시오.

대부분의 경우 다른 유형의 예외를 동일한 방식으로 처리해서는 안됩니다.

이 답변의 출처에서 서식 / 단락을 약간 수정했습니다.

추신 : 예외를 두려워하지 마십시오 !! 그들은 친구들이야!!!


나는 이것을 다른 답변에 대한 주석으로 넣을 것이지만 아직 그 명성이 없습니다.

당신은 그것이 나쁜 관행이라고 말하는 것이 맞습니다. 사실 당신이 게시 한 것은 예외와 관련하여 다른 유형의 나쁜 관행을 보여줍니다.

  1. 오류 처리 부족
  2. 일반 캐치
  3. 의도적 인 예외 없음
  4. 블랭킷 트라이 / 캐치

이 예를 통해 모든 것을 설명하려고합니다.

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

이는 다르게 처리해야하는 여러 가지 방법으로 실패 할 수 있습니다.

  1. 필드가 채워지지 않으므로 사용자에게 빈 화면이 표시되고 ... 없음-오류 처리 부족.
  2. 인터넷 문제 또는 서버 자체 문제 (정지, 요청 중단, 전송 손상 등)와 같은 다양한 유형의 오류를 구분할 수 없습니다.-일반적인 캐치.
  3. 현재 시스템이이를 방해하기 때문에 자신의 목적으로 예외를 사용할 수 없습니다. -고의적 예외 없음
  4. 불필요한 오류 (예 : null.equals (...))로 인해 필수 코드가 실행되지 않을 수 있습니다. -블랭킷 트라이 / 캐치

솔루션

(1) 우선, 조용히 실패하는 것은 좋은 것이 아닙니다. 실패하면 앱이 작동하지 않습니다. 대신 문제 해결을 시도하거나 "사용자 데이터를로드 할 수 없습니다. 인터넷에 연결되어 있지 않습니까?"와 같은 경고를 표시해야합니다. 앱이 의도 한대로 작동하지 않는 경우 앱이 자동으로 닫히는 것보다 사용자에게 더 실망 스럽습니다.

(4) 사용자가 불완전한 경우, 예를 들어 국가를 알 수없고 null을 반환합니다. equals 메소드는 NullPointerException을 생성합니다. NPE가 위와 같이 그냥 던져지고 잡히면, 문제없이 실행될 수 있어도 fillFields (user) 메서드가 호출되지 않습니다. 널 검사를 포함하거나 실행 순서를 변경하거나 try / catch 범위를 조정하여이를 방지 할 수 있습니다. (또는 "us".equals (user.getCountry ())와 같이 코딩을 저장할 수 있지만 예제를 제공해야했습니다). 물론 다른 예외도 fillFields ()가 실행되는 것을 막지 만, 사용자가 없다면 어쨌든 실행하고 싶지 않을 것입니다.

(1, 2, 3) 웹에서로드하면 종종 IOException에서 HttpMessageNotReadable 예외, 심지어 그냥 반환까지 다양한 예외가 발생합니다. 사용자가 인터넷에 연결되지 않았거나 백엔드 서버가 변경되었거나 다운되었을 수 있지만 catch (Exception)를 수행하기 때문에 알 수 없습니다. 대신 특정 예외를 잡아야합니다. 이런 식으로 여러 개를 잡을 수도 있습니다

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

나는 그것이 그것을 설명하기를 바랍니다.

최소한 빠른 수정으로 할 수있는 일은 예외를 제외하고 백엔드에 이벤트를 보내는 것입니다. 예를 들어 firebase 또는 crashlytics를 통해. 이렇게하면 최소한 다음과 같은 것을 볼 수 있습니다 (예 : (4)와 같은 문제로 인해 사용자의 80 %가 주요 활동을로드하지 않습니다.


확실히 나쁜 프로그래밍 관행입니다.

현재 시나리오에서 try catch이와 같은 수백 개가 있다면 애플리케이션을 디버깅하지 않고 예외가 어디에서 발생하는지조차 알 수 없습니다. 이는 애플리케이션이 프로덕션 환경에있는 경우 악몽입니다.

그러나 로거를 포함하여 예외가 발생하는시기 (및 이유)를 알 수 있습니다. 정상적인 작업 흐름은 변경되지 않습니다.

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

이것은 나쁜 습관입니다. 다른 답변은 그렇게 말했지만 뒤로 물러서서 처음에 예외가있는 이유이해하는 것이 중요하다고 생각합니다 .

모든 함수에는 사후 조건이 있습니다. 함수가 실행 된 후에는 모두 참이어야합니다. 예를 들어 파일에서 읽는 함수에는 파일의 데이터를 디스크에서 읽어서 반환하는 게시 조건이 있습니다. 그러면 함수가 사후 조건 중 하나를 충족 할 수 없을 때 예외가 발생합니다.

함수에서 예외를 무시하면 (또는 단순히 예외를 로깅하여 효과적으로 무시하는 경우), 실제로 동의 한 모든 작업을 수행하지 않고 해당 함수에 대해 괜찮다고 말하는 것입니다. 이것은 가능성이없는 것 같습니다. 함수가 올바르게 실행되지 않으면 다음이 실행된다는 보장이 전혀 없습니다. 그리고 특정 함수가 완료되었는지 여부에 관계없이 나머지 코드가 제대로 실행되면 처음에 해당 함수가있는 이유가 궁금합니다.

[이제 빈 항목이 catch괜찮은 경우가 있습니다. 예를 들어, 로깅은 빈 캐치로 래핑하는 것을 정당화 할 수있는 것입니다. 일부 로깅을 작성할 수없는 경우에도 애플리케이션이 정상적으로 실행될 것입니다. 하지만 일반 앱에서 찾기 위해 정말 열심히 노력해야하는 특별한 경우입니다.]

So the point is, this is bad practice because it doesn't actually keep your app running (the supposed justification for this style). Maybe technically the OS hasn't killed it. But it's unlikely that the app is still running properly after simply ignoring an exception. And in the worst case, it could actually be doing harm (e.g. corrupting user files, etc.).


This is bad for multiple reasons:

  1. What are you doing that findViewById throws an Exception? Fix that (and tell me, because I've never seen this) instead of catching.
  2. Don't catch Exception when you could catch a specific type of exception.
  3. There is this belief that good apps don't crash. That's not true. A good app crashes if it must.

If an app goes into a bad state, it is much better for it to crash than for it to chug along in its unusable state. When one sees an NPE, one shouldn't just stick in a null check and walk away. The better approach is to find out why something is null and either stop it from being null, or (if null ends up being a valid and expected state) check for null. But you have to understand why the issue occurs in the first place.


I have been developing android apps for the past 4-5 years and never used a try catch for view initialisation.

If its a toolbar do like this

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

eg:- To get a TextView from a view(fragment/dialog/any custom view)

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

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

instead of this

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

A view object has minimum reach compared to its real view type.

Note:- May be it crashed because the view was loaded. But adding try catch is a bad practice.


Yes, try/catch is used to prevent app from crashing but You certainly don't need try/catch for fetching a view from XML as depicted in your question.

try/catch is generally used while making any http request, while parsing any String to URL, creating URL connections, etc. and also make sure to print stack trace. Not printing it doesn't make much sense in surrounding it with try/catch.


As said before, general exceptions shouldn't be catched, or at least only in a few central places (usually located in framework/infrastructure code, not application code). If catching general exceptions and logging it, the application should be shut down afterwards, or at the very least user should be informed that application is potentially in a unstable state and data corruption could occur (if user chooses to continue execution). Because this is what might happen if you catch all sort of exceptions (out of memory to name one) and leaving the app in an undefined state.

IMHO it is worse to swallow exceptions and risk data integrity, data loss, or simply leaving the app in an undefined state than letting the app crash and the user knows that something went wrong and can try again. This will also lead to better issues reported (more at the root of the problem), probably fewer different symptoms than if your users start to report all kind of troubles originating from undefined application state.

After a central exception handling/logging/reporting and controlled shutdown is in place, start rewriting exception handling to catch local exceptions as specific as possible. Try to make the try{} block as short as possible.


Let me add my point of view, as a guy working in the corporate mobile development industry for more than a decade. First, some general tips on exceptions, most of them included in answers above:

  • Exceptions should be used for exceptional, unexpected or uncontrolled situations, not on a regular basis throughout the code.
  • A programmer must know the portions of code susceptible to throw exceptions and try-catch them, leaving the rest of the code as clean as possible.
  • Exceptions should not be left silent, as a general rule.

Now, when you are not developing an app for yourself, but for a company or a corporation, it is common to face additional requirements on this topic:

  • "App crashes show a poor image of the company, so they are not acceptable". Then, careful development should be performed, and catching even improbable exceptions may be an option. If so, this must be done selectively and kept in reasonable limits. And notice that development is not all about lines of code, for instance, an intensive testing process is critical in these cases. However, unexpected behaviour in a corporate app is worse than crashing. So, whenever you catch an exception in your app, you must know what to do, what to show and how the app will behave next. If you cannot control that, better let the app crash.
  • "Logs and stack traces may dump sensitive information to the console. That might be used for an attacker, so for security reasons they cannot be used in a production environment". This requirement conflicts with the general rule for a developer not to write silent exceptions, so you have to find a way for it. For instance, your app could control the environment, so it uses logs and stack traces in non-production environments, while using cloud-based tools like bugsense, crashlitics or similar for production environments.

So, the short answer is that the code you found it is not a good practice example, since it is hard and costly to maintain without improving the quality of the app.


Another perspective, as someone who writes enterprise software on a daily basis, if an app has an unrecoverable error, I want it to crash. Crashing is desirable. If it crashes, it gets logged. If it crashes more than a few times in a short period of time, I get an e-mail saying that the app is crashing and I can verify that our app and all the web services we consume are still working.

So the question:

Is

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

good practice? No! Here are a few points:

  1. The MOST important thing: This is a bad customer experience. How am I supposed to know when something bad is happening? My customers are trying to use my app and nothing works. They can't check their bank account, pay their bills, whatever my app does. My app is completely useless, but hey, at least it didn't crash! (Part of me believes this "Senior" dev gets brownie points for low crash numbers, so they're gaming the system.)

  2. When I'm doing development and I write bad code, if I'm just catching and swallowing all exceptions at the top layer I have no logging. I have nothing in my console, and my app fails silently. From what I can tell, everything appears to work okay. So I commit the code... Turns out my DAO object was null the whole time, and the customer's payments were never actually updating in the DB. Whoops! But my app didn't crash, so that's a plus.

  3. Playing devil's advocate, let's say I was okay with catching and swallowing every exception. It's extremely easy to write a custom exception handler in Android. If you really just have to catch every exception, you can do it in one place and not pepper try/catch all over your codebase.

Some of the developers I've worked with in the past have thought crashing was bad.

I have to assure them we want our app to crash. No, an unstable app is not okay, but a crash means we did something wrong and we need to fix it. The faster it crashes, the earlier we find it, the easier it is to fix. The only other option I can think of is allowing the user to continue in a broken session, which I equate with pissing off my userbase.


It's bad practice to use catch(Exception e){} because you're essentially ignoring the error. What you probably want to do is something more like:

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

We pretty use much your same logic. Use try-catch to prevent production apps from crashing.

Exceptions should be NEVER ignored. It is a bad coding practice. The guys maintaining the code will have a really hard time localizing the part of code that raised the exception if they are not logged.

We use Crashlytics to log the exceptions. The code will not crash (but some functionality will be disrupted). But you get the exception log in the dashboard of Fabric/Crashlytics. You can look at these logs and fix the exceptions.

try {
    codeThatCouldRaiseError();
} catch (Exception e) {
    e.printStackTrace();
    Crashlytics.logException(e);
}

While I agree with the other responses, there is one circumstance I have repeatedly encountered where this motif is marginally tolerable. Suppose someone wrote a bit of code for a class as follows:

private int foo=0;

    . . .

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

In this circumstance, the 'getFoo()' method cannot fail - there will always be a legitimate value of the private field 'foo' to be returned. Yet someone - probably for pedantic reasons - decided that this method should be declared as potentially throwing an Exception. If you then try to call this method in a context - e.g an event handler - which does not allow an exception to be thrown, you are basically forced to use this construct (even then, I agree that one should at least log the exception just in case). Whenever I have to do this, I always at least add a big fat comment 'THIS CANNOT OCCUR' next to the 'catch' clause.

참고URL : https://stackoverflow.com/questions/42942952/using-try-catch-for-preventing-app-from-crashes

반응형