. 스레드 (Thread) 란? 스레드를설명하기전에이글에서언급되는용어들에대하여알아보도록하겠습니다. - 응용프로그램 ( Application ) 사용자에게특정서비스를제공할목적으로구현된응용프로그램을말합니다. - 컴포넌트 ( component ) 어플리케이션을구성하는기능별요소로써안드로이드시스템에서는 Activities, Services, Content Providers, Broadcast Receivers 라는네가지유형의컴포넌트있습니다. - 프로세스 ( process ) 응용프로그램이메모리에로드되어실행가능한상태로재구성된것을말합니다. 즉, 응용프로그램이실행되면프로세스가된다고생각하면됩니다. 그런데응용프로그램은여러개의컴포넌트들로구성되어있기때문에컴포넌트가실행되어도프로그램이실행되는것과동일합니다. 하지만안드로이드시스템은동일한프로그램에대해서하나의프로세스로유지하기때문에컴포넌트가실행된다고해서반드시프로세스가생성되지는않습니다. 일반적으로스레드는프로세스내에서실행되고있는흐름을말합니다. 기본적으로프로세스가생성되면하나의스레드가같이생성되는데이것을메인스레드라고합니다. 이스레드는동일프로세스내에실행되는모든컴포넌트를직접실행하며 UI 에서수행되는그리기작업이나컨트롤들에대한모든이벤트들을직접처리합니다. 이와같은특징때문에메인스레드를 UI 스레드라고도합니다. 이처럼메인스레드는프로세스내에서기본적으로많은일을처리하기때문에많은리소스를필요로하거나반복적인작업을오랫동안지속해야하는경우에는어떤이벤트나작업도처리하지못하는응용프로그램응답없음상태에종종빠지기도합니다.
그래서프로그램에부하를주는작업을하는경우에는메인스레드가아닌다른스레드에서해당작업을수행할수있도록별도의스레드를생성하는것을권장하고있습니다. 하지만이렇게스레드를추가해서사용하게되면스레드간충돌에대해서안드로이드시스템이안전을보장하지않기때문에프로그래머는아래와같은 2 가지사항을주의해야합니다. - 메인스레드에긴작업시간을요구하는작업을시켜서는안된다. 메인스레드가반응하지못하는상태에빠지면해당프로그램은응답없음상태를피할수없다. - 추가된스레드에서는 UI 관련함수 (Android UI toolkit) 를직접사용하면안된다. 반드시메인스레드에원하는작업의수행을요청해야한다. 2. 스레드생성하기 응용프로그램에서스레드를추가하여사용한다는것은내가원하는루틴이메인스레드와동시에수행된다는의미입니다. 스레드를추가할때에는 Thread 클래스를사용하는데이클래스내부에는사용자가원하는작업을미리정의해둘수없기때문에 Runnable 이라는인터페이스를사용하여해당인터페이스의 run 메소드를통해작업을정의하도록하고있습니다. Runnable 인터페이스는유일하게 run 메소드만정의하여사용하도록하고있으며 Thread 클래스또한이인터페이스를가지고있습니다. 그래서 Thread 클래스를상속받는사용자정의클래스를만들거나 Thread 객체를생성하면서클래스내의 run 메소드를직접재정의하기도하고, Thread 객체를생성할때생성자에 run 메소드가정의된 Runnable 객체를인자로넘겨주기도합니다. 지금까지설명한스레드를추가하는방법에대하여자세하게알아보도록하겠습니다. ( Thread 클래스를상속받는클래스를만들어서 run 메소드를정의하는방법은제외하였습니다. ) 2.1 Runnable 객체를멤버변수로선언하여 Thread 에설정하기 Runnable 객체를멤버변수로선언하고, run 메소드를미리정의해두어서 Thread 객체를생성할때객체생성자에선언해둔 Runnable 멤버변수를인자로넘겨줍니다. // Runnable 객체를생성하고 run 메소드정의한다. Private Runnable m_run_code = new Runnable() ; // Thread 객체를생성하고생성자에 Runnable 객체를넘겨주어 // 해당스레드에서 m_run_code 에정의된 run 메소드를수행할수있도록한다. Thread my_thread = new Thread(m_run_code);
2.2 Thread 객체생성자에 Runnable 인터페이스를직접생성하기 Runnable 인터페이스를따로선언해두지않고, Thread 객체를생성할때 new 키워드를이용하여생성하고, run 메소드를바로정의하는방법입니다. // Thread 객체를생성하고 run 메소드를정의한 Runnable 객체를생성하여생성자로 // 넘겨주어해당스레드에서 run 메소드를수행할수있도록한다. Thread my_thread = new Thread(new Runnable() ); 2.3 Thread 클래스에정의된 run 메소드재정의하기 Thread 클래스도 Runnable 인터페이스를포함하고있기때문에 Thread 클래스의 run 메소드를직접재정의할수있습니다. Thread 객체를생성하고이어서바로 run 메소드를재정의합니다. // Thread 객체를생성하면서, Thread 클래스내의 run 메소드를재정의한다. Thread my_thread = new Thread() ; 2.4 메인클래스에 Runnable 인터페이스를추가하여 run 메소드정의하기
Thread 객체를생성할클래스에 implements 키워드로 Runnable 인터페이스를추가하여 run 메소드를해당클래스에서재정의합니다. // 이클래스에서 Runnable 인터페이스의 run 메소드를재정의할수있도록한다. implements Runnable // Thread 를생성하고, 생성자에이클래스를넘겨준다. // 이클래스에는 Runnable 인터페이스가포함되어있어서 Thread 생성자내부에는 // 아래의 run 메소드가정의된 Runnable 인터페이스로받게된다. Thread my_thread = new Thread(this); // my_thread 스레드가수행하게되는 run 메소드 위와같은여러가지방법으로추가된스레드는 Thread 클래스의 start 메소드를이용하여 run 메소드에정의된루틴들을실행하도록할수있습니다. Thread my_thread = new Thread(...); my_thread.start(); 3. 생성된스레드에서 UI 관련작업하기 앞에서설명했듯이응용프로그램에서 UI 에관련된작업은메인스레드가처리합니다. 메인스레드가아닌추가된스레드에서 UI 에관련된작업을직접수행하는것은메인스레드와의충돌위험성등이존재하여안전하지않기때문에다른스레드에서해당작업을수행하고자할때에는메인스레드에서대신처리할수있도록처리작업을넘겨주기도합니다. 추가된스레드에서메인스레드로작업을넘기는방법은 View 클래스의 post 메소드를사용하는방법과 Handler 클래스의 post 메소드를사용하는방법이있으며이번강좌에서는 View 클래스의 post 메소드를사용하는방법만언급하도록하겠습니다. 다른스레드에서메인스레드로작업을넘기는것은스레드가수행할루틴을 Runnable 인터페이스의 run 메소드에정의하여 Thread 객체에넘기는것과동일하게생각하시면됩니다. 즉, post 메소드도인자로 Runnable 객체를받아서해당객체에정의된 run 메소드를메인스레드가수행하도록하기때문입니다. 그렇기때문에 2.1 이나 2.2 와동일한방법으로메인스레드가처리했으면하는작업들을 run 메소드에구성하면되고, post 메소드는대부분컨트롤의상위클래스인 View 클래스의메소드이므로컨트롤객체를얻어서객체명.post(Runnable 객체 ) 로호출하면됩니다.
예를들어, 네트워크프로그램을만든다고가정하면데이터를수신하는부분이긴시간을요구하는작업이될수있기때문에메인스레드가응답없음상태가될수있습니다. 이러한문제를해결하기위해서스레드를추가하여데이터를수신하는부분을해당스레드가수행하도록하면됩니다. 하지만수신된데이터를텍스트뷰와같은컨트롤에출력하고싶다면데이터를수신한스레드가메인스레드가아니기때문에직접 UI 함수를사용하면충돌하여문제가발생합니다. 이런경우아래와같이 post 메소드를사용하여메인스레드로작업수행을요청해서해결할수있습니다. ( 아래의코드는추가된스레드의 run 메소드에서메인스레드가처리해야할코드를 VIew 의하위클래스인 TextView 객체를이용해서전달하는예제입니다. ) implements Runnable Thread my_thread = new Thread(this); my_thread.start(); // 메인스레드에서수행하고자하는루틴을 run 메소드에정의하여 Runnable 객체를선언한다. Runnable main_run = new Runnable() // 메인스레드가처리해야할루틴... ; // TextView 클래스는 View 클래스의하위클래스이므로 post 메소드가지고있다 TextView tv = (TextView) findviewbyid(r.id.id_tv); // 메인스레드에게 main_run 객체의 run 메소드에정의된루틴을수행하도록전달한다. tv.post(main_run);