안드로이드 프로그래밍/[ Android Framework ]

[ Android ] 10. 싱글쓰레드와 멀티쓰레드(Handler, AysncTask)

kim.svadoz 2020. 8. 12. 14:33
반응형

Thread(쓰레드)

  • 각각의 프로세스들은 서로 관여할 수 없다.
  • 지금까지 배운 것 하나의 프로세스에서는 하나의 실행흐름만 처리할 수 있었다.
  • 하나의 프로세스에서 여러 개의 실행흐름을 동시에 관리하는 것 => 멀티쓰레드 프로그래밍
  • 여러 공유데이터에 관한 스케쥴링이 필요하다.(게임하기, 채팅하기, 쪽지, 음악듣기, 인쇄하기, .... )

<< 실습 >>

  1. 쓰레드 프로그래밍을 구현하세요
    • 1부터 100까지 출력하는 DigitThread
    • A부터 Z까지 출력하는 AlphaThread
    • ThreadExam01의 main메소드에서 DigitThread와 AlphaThread를 동시에 실행해보자.
class DigitThread extends Thread{
    public DigitThread() {

    }
    public void run() {
        for(int i=1; i<100; i++) {
            System.out.println(i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

class AlphaThread extends Thread{
    public AlphaThread() {

    }

    public void run() {
        for(char i='A'; i<='Z'; i++) {
            System.out.println(i);
            try {
                Thread.sleep(150);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

}

public class ThreadExam01 {
    public static void main(String[] args) {
        DigitThread dt = new DigitThread();
        AlphaThread at = new AlphaThread();
        dt.start();
        at.start();
        System.out.println("작업중......!!");
        for(int i=1; i<=10; i++) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("=======main======");
        }
        System.out.println("*************프로그램종료**********");

    }

}

20-04-21 화

  • 푸쉬 알림 FCM ?! notification?!

Thread

  • 웹은 알아서 자동으로 쓰레드 처리를 해주기 때문에 웹에서는 안해도 됐다.
  • run 메소드가 아닌 start메소드를 호출해야 적절한 시점에 메소드를 자동으로 호출해준다.
  • 쓰레드는 한 번 종료된 것은 못살리고 새로 만들어주어야 한다.

<< 실습 >>

ThreadTest02를 RunnablTest02로 변경

  • Runnable을 상속받도록 구현
  • AlphaThread -> AlphaThread2
  • DigitThread -> DigitThread2

쓰레드 종료

  1. 임의의 변수를 선언해서 종료

    • flag 변수
      • 변수에 저장된 값에 따라서 처리할 수 있도록 구현(실행 or 종료)
      • 변수값을 체크(오래걸리는 작업이 있는 경우 중간에 이 값을 체크해서 쓰레드를 종료할 수 있다.)
    class StopThread01 extends Thread{
        private boolean state = true;
        public void run() {
            while(state) {
                System.out.println("쓰레드 실행 중~~");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("현재상태:종료상태........");
        }
        //쓰레드의 상태를 조절할 수 있는 변수의 값을 변경하는 메소드
        public void stopThread() {
            state = false;
        }
    }
    public class StopThreadTest01 {
        public static void main(String[] args) {
            System.out.println("main쓰레드 시작");
            StopThread01 t1 = new StopThread01();
            t1.start();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            t1.stopThread();
            System.out.println("main쓰레드 종료**");
        }
    }
  1. 인터럽트를 발생시키고 현재 상태를 확인하고 작업하기

    • isInterrupted() 메소드를 이용해서 현재 쓰레드의 상태가 인터럽트 상태인지 파악
      • 인터럽트 상태이면 true 리턴

쓰레드가 공유객체에 접근할 때 다른 객체가 들어오지 못하게 Lock을 걸어주어야 한다.

=> synchronized를 이용해서 Lock을 걸어 놓는다.

<< 실습 >>

  1. 계좌이체 쓰레드
  2. 두사람의 잔액 대조
  • 어떤 코드의 어디에다가 쓰레드를 적용하지? => 제일 중요함

Handler

  • UI의 변경은 UI쓰레드에서만 작업야 한다.

    => Handler에게 의뢰한다.

  • handler : 지속적으로 몇가지 처리를 반복적으로 작업으로 뷰를 변경할 때 쓰이는 것

안드로이드에서 쓰레드 처리하기

1. Handler를 이용

오랜시간 처리해야 하는 작업은 할 수 없다.

  1. 동시 실행흐름로 처리할 내용을 쓰레드 객체로 구현
  2. UI쓰레드에서 Handler객체를 생성(하위객체를 구현)
    • onCreate메소드 내부에서 처리
  3. worker thread에서 Handler객체에게 작업을 의뢰
    1. HandlerExam1 방법
    2. postMethod를 이용
  4. handler 객체에서 worker thread로부터 의뢰받은 내용을 처리
    • handleMessage메소드를 이용해서 처리(오버라이딩해서 구현)
    • worker Thread에게 전달받은 값으로 view를 변경
    • 쓰레드로부터 요청이 올 때마다 handleMessage메소드가 호출된다.
public class MainActivity extends AppCompatActivity {
    ProgressBar progressBar;
    TextView textView;
    int progressVal;
    Handler handler1;
    Handler handler2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        progressBar = findViewById(R.id.progressBar);
        textView = findViewById(R.id.textView);

        //worker thread의 요청을처리할 handler객체를 정의
        //Handler의 하위객체를 익명으로 정의하고 생성
        handler1 = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.d("mythread","handleMessage요청");
                textView.setText("progressbar진행률:"+progressVal+"%");
                progressBar.incrementProgressBy(1);
            }
        };
        handler2 = new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                /*Log.d("mythread",msg.what+"");*/
                if(msg.what==1){
                    int val = msg.arg1;
                    textView.setText("progressbar진행률2:"+val+"%");
                    progressBar.incrementProgressBy(val);
                }
            }
        };
    }
    /*
            [화면을 변경하는 작업을 다른 메소드에서 처리 ]
            긴 시간동안 실행하며 view를 변경하려고 하는 경우
            실행되는 동안 다른 작업을 할 수 없다. 실행이 되는 동안 사용자의 이벤트가
            발생하고 이벤트에 5초 동안 반응하지 않으면 안드로이드 OS는 어플리케이션을 강제 종료한다.
            => "ANR" (Application Not Responding)
            오랫동안 처리해야 하는 작업을 UI쓰레드에 정의하면 안된다.
                    -------------------------------------------
                      => 별도의 작업 쓰레드를 정의하고 동시에 실행
    */
    public void btnNoThread(View view){
        for(progressVal=1; progressVal<=100; progressVal++){
            progressBar.setProgress(progressVal);
            SystemClock.sleep(1000); //1초동안 쉬게(1초동안 멈춰있는 효과)
        }
    }
    //개발자가 마든 쓰레드 안에서 UI를 변경
    //- 잠정적인 문제점을 갖고 있는 방법(UI의 변경은 UI쓰레드에서만 작업)
    public void useThread(View view){
        //프로그래스바에 진행상태가 출력되도록 설정
        //프로그래스바의 progress가 변경되는 것을 쓰레드로 만들어서 실행
        //개발자가 만든 쓰레드 - worker thread라고 한다.
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(progressVal=1; progressVal<=100; progressVal++){
                    progressBar.setProgress(progressVal);
                    textView.setText("progressbar진행률:"+progressVal+"%");
                    SystemClock.sleep(1000); //1초동안 쉬게(1초동안 멈춰있는 효과)
                }
            }
        }).start();
    }
    //작업쓰레드가 핸들러에게 View에 대한 변경을 요청한다.
    //핸들러는 작업쓰레드로부터 받은 요청정보를 꺼내서 뷰를 변경
    public void userHandler(View view){
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(progressVal=1; progressVal<=100; progressVal++){
                    //handler가 갖고 있는 Mesaage객체를 매개변수로 전달
                    handler1.sendMessage(handler1.obtainMessage());
                    SystemClock.sleep(100); //1초동안 쉬게(1초동안 멈춰있는 효과)
                }
            }
        }).start();
    }

    //핸들러를 이용해서 UI변경 요청
    //작업쓰레드에서 값을 핸들러에게 넘기기
    //핸들러에게 작업을 의뢰할 때 Message객체를 생성해서 전달
    public void useMessageHandler(View view){
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=1; i<=100; i++){
                    //변경할 뷰의 정보나 Handler에게 전달한 데이터를 Message객체로 생성
                    Message msg = new Message();
                    // handler에게 작업을 의뢰한 쓰레드를 구분하기 위한 코드
                    msg.what = 1;
                    msg.arg1 = i; // 전달할 데이터
                    //Message객체를 전달하여 핸들러에게 작업을 의뢰
                    handler2.sendMessage(msg);
                    SystemClock.sleep(100); //1초동안 쉬게(1초동안 멈춰있는 효과)
                }
            }
        }).start();
    }

20-04-22 수

< post를 활용한 핸들러 처리방법 >

public class HandlerExam2 extends AppCompatActivity {
    int num;
    TextView textView;
    Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_exam);
        textView = findViewById(R.id.textView);
        handler = new Handler();
    }
    public void btn_click(View view){
        //버튼을 누르면 쓰레드를 start
        new NumThread().start();
    }
    //TextView의 값을 지속적으로 변경하는 쓰레드
    class UIUpdateThread implements Runnable{
        @Override
        public void run() {
            textView.setText(num+"");
        }
    }
    //지속해서 값을 만드는 쓰레드
    class NumThread extends Thread{
        public void run(){
            for(int i=1; i<=10; i++){
                num = i;
                //핸들러에게 UI를 변경하는 쓰레드를 전달하며 요청
                handler.post(new UIUpdateThread());
                SystemClock.sleep(1000);
            }
        }
    }
}

2. AsyncTask를 이용

시간이 오래 걸리는 작업도 가능하다.

UI를 변경하는 작업도 가능.

AsyncTask는 "Generic"이기 때문에 정의하는 타입이 다 다르다.

  1. AsyncTask를 상속받는 클래스를 정의

    • AsyncTask에 제네릭을 적용해서 변수 세 개의 타입을 정의(사용자가 임의로)

      • 첫 번째 제네릭 : execute를 호출해서 AsyncTask를 실행할 때 필요한 매개변수의 타입

        => 이 매개변수가 doInBackground를 호출할 때 전달

      • 두 번째 제네릭 : publishProgress의 매개변수 타입

        => publishProgress가 호출할 onProgressUpdate의 매개변수

        => 즉, doInBackground메소드 내부에서 발생되는 값들로 화면에 출력되기 위해 필요한 값

      • 세 번째 제네릭 : doInBackground가 종료되고 리턴되는 값의 타입

        => doInBackground가 종료되면 자동으로 onPostExecute가 호출되며 매개변수로 전달된다.

  2. 메소드를 오버라이딩

    • doInBackground ( 매개변수가 가변형으로 배열로 처리 ) ***
      • Background에서 실행될 작업을 정의.
      • 일반 쓰레드에서 run메소드에 정의했던 코드를 구현
      • 네트워크 처리, 시간이 오래 걸리는 작업을 여기서 처리
      • 화면관련 처리는 할 수 없다.(오직 백단처리)
    • onPreExecute
      • doInBackground 메소드가 호출되기 전에 실행되는 메소드
      • 일반쓰레드로 처리할 일들이 실행되기 전에 사전작업을 해야하는 경우 구현
      • 메인쓰레드(UI쓰레드)에서 호출되는 메소드이므로 화면처리 가능
      • UI쓰레드에서 호출하기 때문에 시간이 오래 걸리는 작업을 하면 안된다.
    • onProgressUpdate
      • doInBackground가 실행되는 중에 UI를 변경해야 할 일이 있는 경우에 호출되는 메소드
      • doInBackground 내부에서 화면을 변경해야 할 일이 생기면 publishProgress메소드를 호출하면 자동으로 onProgressUpdate가 호출된다.
    • onCancelled
      • 작업이 취소되는 경우 호출되는 메소드
    • onPostExecute
      • doInBackground메소드의 처리가 끝나면 호출되는 메소드
      • UI쓰레드에서 호출하기 때문에 시간이 오래 걸리는 작업을 하면 안된다.(뷰를 변경할 수 있다.)
  3. AsyncTask의 하위객체를 생성

  4. 생성된 AsyncTask를 실행

    • AsyncTask의 execute메소드를 호출

image-20200422104403524
image-20200422105740606
image-20200422111447655

3. RunOnUiThread

화면처리에 대한 작업이 여기에서만 사용되는 경우

반응형