안드로이드 프로그래밍[JAVA Code]/Thread

Thread - AsyncTask

훈츠 2019. 12. 6. 17:36
반응형

Thread 에서 핸들러를 사용해보면 코드가 조금 복잡해 보이는데, 안드로이드에서는 핸들러를 사용하지 않고 좀더 간단하게 작업하는 방법도 있다. AsyncTask 클래스를 상속하여 새로운 클래스를 만들면 그 안에 스레드를 위한 코드와 UI 접근 코드를 한꺼번에 넣을 수있다. 따라서 스레드로 처리 해야 하는 코드를 하나의 AsyncTask 클래스로 정의 할수 있다는 장점이 생긴다. 예를 들어, 웹서버에서 고객 이름을 가져오는 작업과 웹서버에서 제품이름을 가져오는 작업을 서로 다른 코드로 분리시키고 싶다면 두개의 AsyncTask 상속 클래스를 만든 후 각각의 코드를 넣으면 된다. AsyncTaks 객체를 만들고 execute() 메서드를 실행하면 이 객체는 정의된 백그라운드 작업을 수행하고 필요한 경우에 그 결과를 메인 스레드에서 실행 하므로 UI 객체에 접근하는 데 문제가 없다. 

AsyncTask 클래스를 상속하여 새로운 클래스를 정의 하면 그 내부에서 필요한 경우마다 콜백 메서드를이 자동으로 호출된다. doInBackground()  메서드에는 새로 만들어진 스레드에서 실행되어야 할 코드들을 넣을 수 있다. 즉 스레드에서 동작하는 것이다. 하지만 onPreExecute(), onProgressUpdate(), onPostExecute() 메서드는 새로 만든 스레드가 아닌 메인 스레드에서 실행 된다. 따라서 UI 객체에 자유롭게 접근 할수 있다. 결국 하나의 클래스 안에 스레드에서 동작해야 하는 작업과 그 작업의 결과를 UI 객체에 반영하는 코드를 같이 구현 할수 있다. 따라서 스레드로 수행해야할 어떤 기능을 하나의 클래스만으로 만들 수있다는 장점이 있다. AsyncTask 객체의 cancel() 메서드를 호출하면 작업을 취소 할수있다. 이때 호출되는 메서드는 onCancelled()메서드 이다. 작업의 진행 상황을 확인 하고 싶을때는 AsyncTask 객체의 getStatus() 메서드를 이용할수 있다. 각각의 상태는 PENDING, RUNNING, FINISHED로 구분된다. 

1) Main XML 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
        >
    <Button
        android:id="@+id/start"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="start"/>
    <Button
        android:id="@+id/stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="stop"/>

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:max="100"
        />

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        />


</LinearLayout>

2) BackgroundThread 생성 AsyncTest 상속, 여기서 AsyncTask 매개 변수는 사용하지 않으면 빈칸으로 둬도 된다. 하나하나하나 설명 하자면 첫번째 매개변수는 doInBackground, onProgressUpdate, onPostExecute의 매개 변수 이다. 

   class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {
      
   }
   

3) Thread 안에서 implement mehod 를 눌러서 doInBackground 를 호출 한후 Thread에서 처리해야할 코드를 작성한다. publishProgress () 메소드를 호출 하면 onProgressUpdate 메소드가 호출된다. 

@Override
	protected Integer doInBackground(Integer... values) {

	for(int i=0; i <=100; i++){
	val++;
	publishProgress( val ); //onProgressUpdate 블럭 호출 
	try{
	Thread.sleep( 100 );
	} catch (Exception e){
	e.printStackTrace();
	}
}
return val;
}

4) onProgressUpdate 메소드는 Main UI 에 접속이 가능하며, values 값으로 오는 배열값을 이용해도 되고 다음 코드처럼 val 값을 선언 후 이용해도 된다. 

@Override
	protected void onProgressUpdate(Integer... values) {
	//progressBar.setProgress(values[0].intValue()); //values 값을 이용해됨			
	//textView.setText( String.valueOf( values[0].intValue()));

	progressBar.setProgress(val); //val 값을 선언 후 이용해됨			
	textView.setText( String.valueOf( val ));
}

5) onPostExecute 메소드는 doInBackground 가 종료되는 시점에 호출 된다. onProgressUpdate 메소드와 마찬가지로 Main UI 에 접근이 가능하다. 말미에 호출되는것을 알고 프로그램 해주면 된다. 

@Override
   protected void onPostExecute(Integer result) {
      progressBar.setProgress(0);
      textView.setText( String.valueOf( val ) );
   }

6) Cancel메소드는  cancel 호출 되었을때 나오는 메소드 이다. 

@Override
protected void onCancelled() {
	Toast.makeText( getBaseContext(),"start onCancelled",Toast.LENGTH_LONG ).show();
	val = 0 ;
	progressBar.setProgress(0);
	textView.setText( String.valueOf( val ) );
}

7) PreExecute 메소드는 AsyncTest 호출시 이전에 호출되는 메소드이다. 

 @Override
 protected void onPreExecute() {
 	super.onPreExecute();
 	Toast.makeText( getApplicationContext(),"start AsyncTest",Toast.LENGTH_LONG ).show();

}

8) 코드 전체 

public class MainActivity extends AppCompatActivity {
    ProgressBar progressBar;
    Button start, stop;
    BackgroundTask task;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        setContentView( R.layout.activity_main );
        progressBar = (ProgressBar) findViewById( R.id.progressBar );
        start = (Button) findViewById( R.id.start );
        stop = (Button) findViewById( R.id.stop );
        textView = (TextView) findViewById( R.id.textView );

        start.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                task = new BackgroundTask();
                task.execute(1);
            }
        } );

        stop.setOnClickListener( new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                task.cancel( true);
            }
        } );
    }

    class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {

        int val = 0;
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            Toast.makeText( getApplicationContext(),"start AsyncTest",Toast.LENGTH_LONG ).show();

        }
        @Override
        protected Integer doInBackground(Integer... values) {

                for(int i=0; i <=100; i++){
                    val++;
                    publishProgress( val );
                    try{
                        Thread.sleep( 100 );
                    } catch (Exception e){
                        e.printStackTrace();
                    }
                }
            return val;
        }
        @Override
        protected void onProgressUpdate(Integer... values) {
            //progressBar.setProgress(values[0].intValue());
            //textView.setText( String.valueOf( values[0].intValue() ) );

            progressBar.setProgress(val);
            textView.setText( String.valueOf( val ));
        }

        @Override
        protected void onPostExecute(Integer result) {
            progressBar.setProgress(0);
            textView.setText( String.valueOf( val ) );
        }

        @Override
        protected void onCancelled() {
            Toast.makeText( getBaseContext(),"start onCancelled",Toast.LENGTH_LONG ).show();
            val = 0 ;
            progressBar.setProgress(0);
            textView.setText( String.valueOf( val ) );
        }

    }
}

9) UI 확인 

 

결론 : AsyncTest 를 이용하면 하나의 class 처럼 Thread 및 기능을 한데 모을수 있는 장점이 있다. 사용법도 어렵지 않으므로 현재 코드를 반복해서 흐름을 이해하고 사용하면 될것 같다. 

반응형