Android

[스레드 오류 해결 방법] CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

여러개의 버튼에 이미지 효과를 줄 때 갭을 주기 위해, TimerTask를 사용하였다. 메인 스레드가 아닌 서브스레드에서 UI의 상태를 변경하려고 할때 발생하는 오류인데, 이게 모든 휴대폰에서 발생하는 오류는 아니다.  심각한 오류라면 모든 휴대폰에서 오류가 발생해야 정상인데, 그렇지 않다.  안드로이드 운영체제 버전이 낮은 경우에 주로 발생되고 있다.

Fatal Exception: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
       at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
       at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
       at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
       at android.view.View.invalidateInternal(View.java:12719)
       at android.view.View.invalidate(View.java:12683)
       at android.view.View.invalidateParentIfNeeded(View.java:12872)
       at android.view.View.clearAnimation(View.java:19121)
       at ddolcat.app.battery.fast.charger.MainActivity.makeFastBlanking(MainActivity.java:834)
       at ddolcat.app.battery.fast.charger.MainActivity$6.run(MainActivity.java:856)
       at java.util.Timer$TimerImpl.run(Timer.java:284)ㅁㄴㄹ

■오류가 발생한 코드

    public void makeBlanking(Button bar) {
        bar.clearAnimation();
        bar.setAnimation(AnimationUtils.loadAnimation(this, R.anim.bar_fast_fade));
    }
    
	
	private void runAnimaion(){ 
		Button sunder_1 = view.findViewById(R.id.sunder_1);
		Button sunder_2 = view.findViewById(R.id.sunder_2);

		Timer time1 = new Timer();
		time1.schedule(new TimerTask() {
			@Override
			public void run() {
				makeBlanking(sunder_1); 
			}
		}, 100);

		Timer time2 = new Timer();
		time2.schedule(new TimerTask() {
			@Override
			public void run() {
				makeFastBlanking(sunder_2);
			}
		}, 1000); 
    }

■오류를 수정한 코드

    public void makeBlanking(final Button bar) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                bar.clearAnimation();
                bar.setAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.bar_fast_fade));
            }
        });
    }

메인스레드에서 처리하면 해결된다. runOnUiThread를 사용하거나 Handler를 사용해서 해결한다.

핸들러 사용시에는 주의가 필요하다. 아래 코드 처럼 Handler를 사용하면 또 다른 오류가 발생한다. Looper가 준비되지 않았다는 오류가 뜨게 된다.

    public void makeBlanking(final Button bar) {
        final Handler mHandler = new Handler();
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                bar.clearAnimation();
                bar.setAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.bar_fast_fade));
            }
        });
    }
UncaughtException: java.lang.RuntimeException: 
Can't create handler inside thread Thread[Timer-4,5,main] that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:207)
        at android.os.Handler.<init>(Handler.java:119)
        at com.test.Fragment$1$1.run(ChatFragment.java:144)
        at java.util.TimerThread.mainLoop(Timer.java:562)
        at java.util.TimerThread.run(Timer.java:512)

메인스레드에서 핸들러가 작업이 진행될 수 있도록 Handler 선언시 Looper.getMainLooper()를 사용하여 메인 루퍼를 가져온다.

    public void makeBlanking(final Button bar) {
        final Handler mHandler = new Handler(Looper.getMainLooper());
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                bar.clearAnimation();
                bar.setAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.bar_fast_fade));
            }
        });
    }

[관련자료]
https://stackoverflow.com/questions/3280141/calledfromwrongthreadexception-only-the-original-thread-that-created-a-view-hie

https://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi

https://itmining.tistory.com/5

https://itmining.tistory.com/6

 

Leave a Reply

error: Content is protected !!