데브팜Android [Android] Thread 관련 에러 (Only the original thread that created a view hierarchy can touch its views.)퓨새 2017. 6. 1. 20:07 <Error Code> android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. <해석> Original Thread으로만 UI(view heirarchy: 화면 체계)를 변경시킬 수 있어. <원인> Main Thread 외의 새로 생성한 Thread를 이용하여 임의로 UI를 변경시키려고 했기 때문이에요. <해결 방법> Handler를 이용하여 Main Thread를 간접적으로 사용하면 해결할 수 있습니다. 저같은 경우 동적으로 일정한 시간마다 화면을 변환시켜야해서 Handler와 Timer 같이 이용한 코드를 작성했습니다. final Handler handler = new Handler(){ Timer timer = new Timer(true); @Override 위의 코드를 보면 알 수 있듯, Handler를 생성해서 원하는 동작을 선언해 놓고, Timer를 카운트 할 때마다 handler를 작동시키는 것을 볼 수 있습니다! 좀 더 간결하게 표현하면 Handler handler = new Handler(){ public void handleMessage(Messaage msg){ // 내가 원하는 UI 변경 작업!! } } . . . 코드 Run하는 중 UI 동작 변경하고 싶을 때 Message msg = handler.obtainMessage(); handler.sendMessage(msg); 해당 동작의 handler를 호출하면 됩니다!! 와웅!!! I've built a simple music player in Android. The view for each song contains a SeekBar, implemented like this: public class Song extends Activity implements OnClickListener,Runnable { private SeekBar progress; private MediaPlayer mp; // ... private ServiceConnection onService = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder rawBinder) { appService = ((MPService.LocalBinder)rawBinder).getService(); // service that handles the MediaPlayer progress.setVisibility(SeekBar.VISIBLE); progress.setProgress(0); mp = appService.getMP(); appService.playSong(title); progress.setMax(mp.getDuration()); new Thread(Song.this).start(); } public void onServiceDisconnected(ComponentName classname) { appService = null; } }; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.song); // ... progress = (SeekBar) findViewById(R.id.progress); // ... } public void run() { int pos = 0; int total = mp.getDuration(); while (mp != null && pos<total) { try { Thread.sleep(1000); pos = appService.getSongPosition(); } catch (InterruptedException e) { return; } catch (Exception e) { return; } progress.setProgress(pos); } } This works fine. Now I want a timer counting the seconds/minutes of the progress of the song. So I put a TextView in the layout, get it with findViewById() in onCreate(), and put this in run() after progress.setProgress(pos): String time = String.format("%d:%d", TimeUnit.MILLISECONDS.toMinutes(pos), TimeUnit.MILLISECONDS.toSeconds(pos), TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes( pos)) ); currentTime.setText(time); // currentTime = (TextView) findViewById(R.id.current_time); But that last line gives me the exception:
Yet I'm doing basically the same thing here as I'm doing with the SeekBar - creating the view in onCreate, then touching it in run() - and it doesn't give me this complaint. |