빈 ꡬ멍 μ±„μš°κΈ°

[Android][Toast] Can't toast on a thread that has not called Looper.prepare()

⭐⭐기둝 2023. 2. 1. 11:41

Crashlyticsμ—μ„œ 이슈 ν•΄λ‹Ή 발견

 

문제 λ°œμƒ

λ§ˆμ΄λ„ˆ 이슈둜 λ“±μž₯ν•˜κ³  μžˆλ‹€. (κ³  νŒλ‹¨ν•˜κ³  μžˆλ‹€. 제발 λ§ˆμ΄λ„ˆ μ΄μŠˆμ—¬λΌ...)

 

μ΄μŠˆμ— λŒ€ν•œ μ„€λͺ…λ“€

https://stackoverflow.com/a/3875204

 

Can't create handler inside thread that has not called Looper.prepare()

What does the following exception mean; how can I fix it? This is the code: Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT); This is the exception: java.lang.RuntimeExce...

stackoverflow.com

https://gdlseed.tistory.com/70

 

μ•ˆλ“œλ‘œμ΄λ“œ μ½”ν‹€λ¦° / can't toast on a thread that has not called looper.prepare()

Toast λ₯Ό μ‚¬μš©ν•˜λ‹€κ°€ can't toast on a thread that has not called looper.prepare() μ—λŸ¬κ°€ λ‚˜μ˜€λ©΄μ„œ App 비정상 μ’…λ£Œλ‹€ λ˜μ—ˆλ‹€. ꡬ글링 ν–ˆλ”λ‹ˆ UI μŠ€λ ˆλ“œκ°€ μ•„λ‹Œ μŠ€λ ˆλ“œμ—μ„œ ν† μŠ€νŠΈ 창을 λ„μš°λ €κ³  ν•΄μ„œ λ°œμƒν•œ λ¬Έ

gdlseed.tistory.com

즉, Toast ν˜ΈμΆœμ€ UI μž‘μ—…μ΄λΌ Main Threadμ—μ„œ μž‘μ—…ν•΄μ•Ό ν•˜λŠ”λ°, μ‹€μ œλ‘œ Main Threadκ°€ μ•„λ‹Œ μ“°λ ˆλ“œμ—μ„œ Toast 호좜이 μΌμ–΄λ‚œ 것.

 

그럼 μ½”λ“œμ˜ μ–΄λ””μ„œ, μ™œ λ°œμƒν–ˆλŠ”κ°€

RetrofitService.INSTANCE.getMyApi()
	.observeOn(Scheduler.io())
    .subscribeOn(Scheduler.io())
    .subscribe(data -> handleData(data),
    	error -> if (error != null) {
        	ExceptionExtensionsKt.handleErrorIfMyCustomException(error);
        }
    );

μžλ°”λ‘œ μž‘μ„±λœ μ½”λ“œμ΄λ‹€.

 

μ—λŸ¬ 곡톡 처리 λ©”μ†Œλ“œ(μ € λΆ€λΆ„μ—μ„œ ν† μŠ€ν‹€ ν˜ΈμΆœν•œλ‹€.) 호좜이 Main Threadμ—μ„œ μ΄λ€„μ§€λŠ” 것이 μ•„λ‹Œ 것을 이제 ν™•μΈν–ˆλ‹€.

 

μ € μ—λŸ¬ 곡톡 처리 뢀뢄은 Main Threadμ—μ„œ ν˜ΈμΆœλ˜λŠ” κ²½μš°κ°€ 더 λ§ŽκΈ°λŠ” ν•˜λ‹€. 

 

이슈λ₯Ό μ–΄λ–»κ²Œ μˆ˜μ •ν•  것인가

λ‹€μŒκ³Ό 같은 방법듀이 μžˆλ‹€.

 

1. μ € μ—λŸ¬ 곡톡 처리 λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜λŠ” λͺ¨λ“  곳을 λ’€μ Έμ„œ Main Threadμ—μ„œ μ‹€ν–‰ν•˜λ„λ‘ μˆ˜μ •ν•œλ‹€.

μ΄λ ‡κ²Œ μ§„ν–‰ν•  경우 μ˜ˆμƒλ˜λŠ” 것듀

  • μˆ˜κ³ λ‘­λ‹€.
  • μˆ˜μ •ν•΄μ•Όν•  뢀뢄을 λ‚΄κ°€ 놓칠 수 μžˆλ‹€. κ²°κ΅­μ—” 또 같은 λ¬Έμ œκ°€ λ°œμƒν•  것이닀.
  • μ € μ—λŸ¬ 곡톡 처리 λ©”μ†Œλ“œμ— ν† μŠ€νŠΈ 호좜이 μžˆλ‹€λŠ” κ±Έ μΈμ‹ν•˜μ§€ λͺ»ν•œ (νžˆμŠ€ν† λ¦¬λ₯Ό μžŠμ–΄λ²„λ¦° 미래의 λ‚˜λ₯Ό ν¬ν•¨ν•΄μ„œ) λ‹€λ₯Έ κ°œλ°œμžκ°€ μ € λ©”μ†Œλ“œλ₯Ό μž¬μ‚¬μš©μ‹œ Main Threadκ°€ μ•„λ‹Œ κ³³μ—μ„œ ν˜ΈμΆœν•˜λ„λ‘ μ½”λ“œλ₯Ό μž‘μ„±ν•  수 μžˆλ‹€. 그럼 또 μ΄μŠˆκ°€ λ°œμƒν•œλ‹€.

 

2. μ € μ—λŸ¬ 곡톡 처리 λ©”μ†Œλ“œ μ•ˆμ—μ„œ ν† μŠ€νŠΈλ₯Ό ν˜ΈμΆœν•˜λŠ” 뢀뢄을 Main Threadμ—μ„œ μ‹€ν–‰ν•˜λ„λ‘ μˆ˜μ •ν•œλ‹€.

μ΄λ ‡κ²Œ μ§„ν–‰ν•  경우 μ˜ˆμƒλ˜λŠ” 것듀

  • 1λ²ˆλ³΄λ‹€λŠ” 훨씬 덜 μˆ˜κ³ λ‘­λ‹€.
  • μ € λ©”μ†Œλ“œλ₯Ό μž¬μ‚¬μš©ν•˜λŠ” 경우 μ½”λ“œλ₯Ό μž‘μ„±ν•˜λŠ” κ°œλ°œμžλŠ” μž‘μ—…ν•˜λŠ” Thread에 λŒ€ν•΄μ„œ μ‹ κ²½μ“Έ ν•„μš”κ°€ μ—†λ‹€.

2번으둜 μ„ νƒν•œλ‹€.

 

 

이슈 μˆ˜μ •

μ—λŸ¬ 곡톡 처리 μ½”λ“œ μ•ˆμ„ 까보고 λ‚˜μ„œ 과거의 λ‚΄κ°€ λ†“μΉœ 뢀뢄을 λ°œκ²¬ν–ˆλ‹€.🀦‍♀️ 

μ—λŸ¬ 곡톡 처리 μ½”λ“œ μ•ˆμ˜ μž‘μ—…λ“€μ΄ Main Threadμ—μ„œ μž‘μ—…ν•΄μ•Όν•˜λŠ” 것(ν† μŠ€νŠΈ 호좜 λ“±)κ³Ό Threadμ—μ„œ μž‘μ—…ν•΄μ•Όν•˜λŠ” 것(둜컬 데이터 μ‚­μ œ λ“±)으둜 λ‚˜λ‰˜μ–΄μ Έ μžˆλŠ”λ°, μ½”λ“œ μž‘μ„± λ‹Ήμ‹œ 이것듀을 κ³ λ €ν•˜μ§€ μ•Šμ•˜λ‹€. 🀦‍♀️ 

이번 이슈둜 λ‚΄κ°€ λ†“μΉœ 뢀뢄을 λ°œκ²¬ν•΄μ„œ 닀행이닀.

 

μ½”λ“œ μˆ˜μ •μ€ λŒ€λž΅ μ΄λŸ¬ν•˜λ‹€.

fun Throwable.handleErrorIfMyCustomException() {
	...
    
    Thread {
    	//UI
        Handler(Looper.getMainLooper()).post {
        	Timber.d("Toast Thread - ${Thread.currentThread().name}")
        	CustomToast.makeTest(context, R.string.msg, Toast.LENGTH_SHORT).show()
        }
        
        //Thread
        Timber.d("Logout Thread - ${Thread.currentThread().name}")
        LocalDataManager.deleteLocalData()
    }.start()
    ...
}

μ½”ν‹€λ¦°μœΌλ‘œ μž‘μ„±ν•œ μ½”λ“œμ΄λ‹€.

 

둜그둜 λ™μž‘ 확인 μ™„λ£Œ.

 

둜컬 데이터 μ‚­μ œλŠ” μ›Œμ»€ μ“°λ ˆλ“œμ—μ„œ λ™μž‘λ¨μ„ 확인함
ν† μŠ€νŠΈ λ…ΈμΆœμ€ 메인 μ“°λ ˆλ“œμ—μ„œ λ™μž‘λ¨μ€ 확인함