runOnUiThread

 

UI쓰레드 = 메인 쓰레드 (onCreate를 실행하는 쓰레드)

다운로드 받을 때에는 무조건 새로운 쓰레드(I/O)

새로운쓰레드로 다운로드 -> 쓰레드 마지막에 콜백

다운로드 다받은 데이터를 메인쓰레드의 변수에서 쓰기 어렵다(다운로드 받는 걸 메인에서 기다리지 않기 때문)

그래서 runOnUiThread 나 핸들러를 사용해야한다.

사용하지 않을려면

메인쓰레드에 함수에 넘겨야하는데 자바는 함수를 넘길 수 없으므로 클래스(인터페이스)를 넘겨야한다

asyncTask를 이용하여 메인에서 다운로드 데이터를 받는 타이밍을 잡아야한다

원리를 이해하고 나서 라이브러리를 사용하자

 

--------------

asyncTask

두잇 안드 490페이지 메서드(doInBackground)

-----------------------

메인쓰레드

이벤트핸들러 쓰레드

-----------------------

다른 쓰레드에서 이벤트핸들러쓰레드를 통해서 메인쓰레드의 이미지를 변경한다

어떻게 하면 다른 쓰레드가 이벤트핸들러에 접근할 수 있을까

1. 쓰레드 안에 runOnUiThread()를 하나더 만들어서 이용 - 핸들러에게 던짐

2. 핸들러를 이용하는 방법 handler.post() (Do it 안드 478페이지)

3.

-----------------------

 

프로그레스 진행 만들기

-----------------------

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">


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

    <Button
        android:id="@+id/btn_execute"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="실행" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="중지" />


</LinearLayout>

 

MainActivity

package com.jaybon.asynctask;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {

    private Button btnExecute, btnStop; //실행 중지버튼
    private ProgressBar progressBar; // 진행바
    private int value = 0; // 진행바의 퍼센테이지
    private Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mInit(); // 함수 사용할 때 내가 만든 것은 m을 붙이고 오버라이딩은 그대로

        progressBar.setProgress(value);

        mListenser();

    }

    private void mListenser() {
        btnExecute.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() { // 여기다가 데이터변환을 쓰면 안된다(runOnUiThread나 핸들러)
                    @Override
                    public void run() {
                        // runOnUiThread는 잘 안쓰니까 핸들러를 사용하자
                        handler.post(new Runnable() { // 핸들러한테 던지는 타겟을 넣기
                            @Override
                            public void run() {
                                while (true) {
                                    value = value + 5;
                                    progressBar.setProgress(value);
                                    try {
                                        Thread.sleep(500);
                                    } catch (InterruptedException e) {
                                        e.getStackTrace();
                                    }
                                    if(value >= 100){
                                        break;
                                    }
                                }
                            }
                        });
                    }
                }).start();
            }
        });
    }

    private void mInit() {
        btnExecute = findViewById(R.id.btn_execute);
        btnStop = findViewById(R.id.btn_stop);
        progressBar = findViewById(R.id.progressBar);
    }
}

 

결과

진행이 안되고 한번에 쫙 100퍼가 채워진다

쓰레드가 종료되야 핸들러가 실행되기 때문

 

데이터는 변경된다! 다만 UI에 그리는건 핸들러 쓰레드가 끝나야 되는 것!

 

 

------------------------

 

asyncTask로 변경하여 만들기

 

블록지정 부분 삭제

 

 

 

 

 

 

제네릭 값이 전달된다

 

 

 

 

package com.jaybon.asynctask;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";

    private Button btnExecute, btnStop; //실행 중지버튼
    private ProgressBar progressBar; // 진행바
    private int value = 0; // 진행바의 퍼센테이지
    private Handler handler = new Handler();

    class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {  // 이것이 쓰레드가 됨

        @Override // 타겟 호출 직전
        protected void onPreExecute() {
            super.onPreExecute();
            progressBar.setProgress(value);
            Log.d(TAG, "onPreExecute: ");
        }

        @Override // 타겟 (run()과 같음)
        protected Integer doInBackground(Integer... integers) { // 스레드 실행시 인수 받기
            Log.d(TAG, "doInBackground: ");

            while (value < 100){

                value = value + 5;
                // 이 값은 onProgressUpdate로 넘어간다
                // 넘어가서 바로 그려짐
                publishProgress(value);

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            // 이 값이 onPostExecute의 매개변수로 넘어간다
            return 1; // 정상적으로 수행됐으면 1, 안됐으면 -1
        }

        @Override // UI쓰레드에 그림을 그려주는 메서드
        protected void onProgressUpdate(Integer... values) { // publishProgress 리턴 값 받기
            super.onProgressUpdate(values);
            Log.d(TAG, "onProgressUpdate: ");
            progressBar.setProgress(values[0]); // Integer...는 배열이라서 0번 주소 = Integer[]
        }

        @Override // 타겟 호출 이후
        protected void onPostExecute(Integer integer) { // doInBackground 리턴 값 받기
            // 리턴값 받아서 값에 따라 분기
            super.onPostExecute(integer);
            Log.d(TAG, "onPostExecute: ");
            Toast.makeText(MainActivity.this, "다운로드 완료", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mInit(); // 함수 사용할 때 내가 만든 것은 m을 붙이고 오버라이딩은 그대로

        mListenser();

        

    }

    private void mListenser() {

        btnExecute.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                new BackgroundTask().execute(); // execute(여기의 값이 doInBackground 의 매개변수로 감)

            }
        });

        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
    }

    private void mInit() {
        btnExecute = findViewById(R.id.btn_execute);
        btnStop = findViewById(R.id.btn_stop);
        progressBar = findViewById(R.id.progressBar);
    }
}

 

 

 

 

이렇게 짜면 중지 할 수 없다

중지하려면 전역변수로 잡아줘야한다

 

 

 

 

 

멈추기

 

 

멈추기는 가능하나 다시 실행이 안된다

 

 

중지 재시작

package com.jaybon.asynctask;

import androidx.appcompat.app.AppCompatActivity;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "Main_Activity";

    private Button btnExecute, btnStop; //실행 중지버튼
    private ProgressBar progressBar; // 진행바
    private int value = 0; // 진행바의 퍼센테이지
    private  BackgroundTask task;

    class BackgroundTask extends AsyncTask<Integer, Integer, Integer> {  // 이것이 쓰레드가 됨

        @Override // 타겟 호출 직전
        protected void onPreExecute() {
            super.onPreExecute();
            progressBar.setProgress(value);
            Log.d(TAG, "onPreExecute: ");
        }

        @Override // 타겟 (run()과 같음)
        protected Integer doInBackground(Integer... integers) { // 스레드 실행시 인수 받기
            Log.d(TAG, "doInBackground: ");

            while (isCancelled() == false){

                value = value + 5;

                if(value >= 100){
                    break;
                }

                // 이 값은 onProgressUpdate로 넘어간다
                // 넘어가서 바로 그려짐
                publishProgress(value);

                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            // 이 값이 onPostExecute의 매개변수로 넘어간다
            return 1; // 정상적으로 수행됐으면 1, 안됐으면 -1
        }

        @Override // UI쓰레드에 그림을 그려주는 메서드
        protected void onProgressUpdate(Integer... values) { // publishProgress 리턴 값 받기
            super.onProgressUpdate(values);
            Log.d(TAG, "onProgressUpdate: ");
            progressBar.setProgress(values[0]); // Integer...는 배열이라서 0번 주소 = Integer[]
        }

        @Override // 타겟 호출 이후
        protected void onPostExecute(Integer integer) { // doInBackground 리턴 값 받기
            // 리턴값 받아서 값에 따라 분기
            super.onPostExecute(integer);
            Log.d(TAG, "onPostExecute: ");
            Toast.makeText(MainActivity.this, "다운로드 완료", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mInit(); // 함수 사용할 때 내가 만든 것은 m을 붙이고 오버라이딩은 그대로

        task = new BackgroundTask();

        mListenser();

    }

    private void mListenser() {

        btnExecute.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if(task == null || task.isCancelled() == true){
                    task = new BackgroundTask();
                    task.execute(); // execute(여기의 값이 doInBackground 의 매개변수로 감)
                }


            }
        });

        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                task.cancel(true);


            }
        });
    }

    private void mInit() {
        btnExecute = findViewById(R.id.btn_execute);
        btnStop = findViewById(R.id.btn_stop);
        progressBar = findViewById(R.id.progressBar);
    }
}

 

 

 

--------------

쓰레드종료 interrupt()를 걸면된다. (무조건 sleep 주자)

task.cancel = 잠깐 멈춰서 interrupt()

--------------

 

 

 

+ Recent posts