TIL

24.11.18

re트 2024. 11. 18. 17:18
728x90

Callable 인터페이스 (in java.util.concurrent)

: 스레드에서 실행할 작업을 정의

: Callable<T>에서 T call() 메서드의 반환 타입

: 기본적으로는 ExecutorService를 쓴다고 하지만 내가 보던 코드에서는 A11이후로 deprecated AsyncTask를 활용하길래 찾아봤더니 정리를 잘 해둔 곳이 있었다.(참고: https://javacan.tistory.com/entry/maintainable-async-processing-code-based-on-AsyncTask) 그리고 이걸 ExecutorService로 변경해봤다.(도움: chatGPT)

// * AsyncTask 사용하던 코드
private AsyncExecutor.AsyncCallback<TestWrapper> getTestCommand
        = new AsyncExecutor.AsyncCallback<TestWrapper>() {
    @Override
    public void onResult(TestWrapper result) {
        if (result != null) {
            // 비동기 처리 완료 시 진행할 내용
    }
    @Override
    public void exceptionOccured(Exception e) {
        // 예외가 발생했을 시 진행할 내용
    }
    @Override
    public void cancelled() {
        // 비동기 작업 취소 시 진행할 내용
    }
};

private void testCommand(final int c) {
    Callable<TestWrapper> callable = () -> {
        TestWrapper wrap = new TestWrapper();
        // 비동기로 작업할 내용 추가
        return wrap;
    };
    new AsyncExecutor<TestWrapper>().setCallable(callable)
            .setCallback(getTestCommand).execute();
}

===============================================================================================
import android.os.AsyncTask;
import android.util.Log;

import java.util.concurrent.Callable;

public class AsyncExecutor<T> extends AsyncTask<Void, Void, T> {

    private static final String TAG = "AsyncExecutor";
    private AsyncCallback<T> callback;
    private Callable<T> callable;
    private Exception occuredException;

    public AsyncExecutor<T> setCallable(Callable<T> callable) {
        this.callable = callable;
        return this;
    }

    public AsyncExecutor<T> setCallback(AsyncCallback<T> callback) {
        this.callback = callback;
        return this;
    }

    @Override
    protected T doInBackground(Void... params) {
        try {
            return callable.call();
        } catch (Exception ex) {
            Log.e(TAG, "exception occured while doing in background: " + ex.getMessage(), ex);
            this.occuredException = ex;
            return null;
        }
    }

    @Override
    protected void onPostExecute(T result) {
        if (isCancelled()) {
            notifyCanceled();
        }
        if (isExceptionOccured()) {
            notifyException();
            return;
        }
        notifyResult(result);
    }

    private void notifyCanceled() {
        if (callback != null)
            callback.cancelled();
    }

    private boolean isExceptionOccured() {
        return occuredException != null;
    }

    private void notifyException() {
        if (callback != null)
            callback.exceptionOccured(occuredException);
    }

    private void notifyResult(T result) {
        if (callback != null)
            callback.onResult(result);
    }

    public interface AsyncCallback<T> {
        public void onResult(T result);

        public void exceptionOccured(Exception e);

        public void cancelled();
    }
}
// * ExectorService를 사용하는 코드
private AsyncExecutor.AsyncCallback<TestWrapper> getTestCommand
        = new AsyncExecutor.AsyncCallback<TestWrapper>() {
    @Override
    public void onResult(TestWrapper result) {
        if (result != null) {
            // 비동기 처리 완료 시 진행할 내용
    }
    @Override
    public void exceptionOccured(Exception e) {
        // 예외가 발생했을 시 진행할 내용
    }
    @Override
    public void cancelled() {
        // 비동기 작업 취소 시 진행할 내용
    }
};

private void testCommand(final int c) {
    Callable<TestWrapper> callable = () -> {
        TestWrapper wrap = new TestWrapper();
        // 비동기로 작업할 내용 추가
        return wrap;
    };
    new AsyncExecutor<TestWrapper>().setCallable(callable)
            .setCallback(getTestCommand).execute();
}
===============================================================================================
import java.util.concurrent.*;

public class AsyncExecutor<T> {

    private static final String TAG = "AsyncExecutor";
    private AsyncCallback<T> callback;
    private Callable<T> callable;
    private Exception occuredException;

    public AsyncExecutor<T> setCallable(Callable<T> callable) {
        this.callable = callable;
        return this;
    }

    public AsyncExecutor<T> setCallback(AsyncCallback<T> callback) {
        this.callback = callback;
        return this;
    }

    public void execute() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        Future<T> future = executorService.submit(callable);

        new Thread(() -> {
            try {
                T result = future.get();
                if (Thread.currentThread().isInterrupted()) {
                    notifyCanceled();
                } else {
                    notifyResult(result);
                }
            } catch (InterruptedException e) {
                notifyCanceled();
            } catch (ExecutionException e) {
                occuredException = e.getCause();
                notifyException();
            } finally {
                executorService.shutdown();
            }
        }).start();
    }

    private void notifyCanceled() {
        if (callback != null) {
            callback.cancelled();
        }
    }

    private boolean isExceptionOccured() {
        return occuredException != null;
    }

    private void notifyException() {
        if (callback != null) {
            callback.exceptionOccured(occuredException);
        }
    }

    private void notifyResult(T result) {
        if (callback != null) {
            callback.onResult(result);
        }
    }

    public interface AsyncCallback<T> {
        void onResult(T result);
        void exceptionOccured(Exception e);
        void cancelled();
    }
}

 

 

- Runnable과의 차이점

 1) Callable call()은 작업을 실행하고 결과를 반환하지만 Runnable run()은 결과 반환하지 않음

 2) Callable은 예외를 던질 수 있지만 Runnable은 예외를 던지지 않음

 

Executor 인터페이스

: 매개변수로 들어온 Runnable 객체를 실행하는 객체

: 명시적인 스레드 생성 대신 사용됨

: 보통은 이 인터페이스 자체를 쓰기보다는 확장된 인터페이스들을 사용

 

ExecutorService 인터페이스

: Task만 지정해주면 알아서 스레드풀을 이용해 Task를 실행하고 관리해줌

: Task Queue로 관리됨

: Task submit하면 개발자가 생명주기 관리할 필요없이 내부에서 해당 Task를 스케줄링하면서 적절하게 일을 처리

- 객체 생성 방법

 1) 구현체인 ThreadPoolExecutor로 초기화

: new ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>)

: new ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, RejectedExecutionHandler)

: new ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory, RejectedExecutionHandler)

 2) Executors 클래스에서 제공하는 정적 팩토리 메소드로 초기화

: Executors.newCachedThreadPool()

: Executors.newFixedThreadPool(int)

: Executors.newSingleThreadExecutor()

- CachedThreadPool

 1) 일정시간동안 스레드를 검사하여 60초동안 Task가 없으면 스레드풀에서 제거

 2) 스레드 제한 없이 무제한 생성 가능

- FixedThreadPool

 1) 고정된 스레드 개수를 가지는 스레드풀

 2) CPU 코어수 기준으로 생성 시 효율 상승

- SingleThreadExecutor

 1) 스레드가 한 개인 스레드풀

- Task 할당을 위해 제공되는 메소드들

 1) execute()

: 반환 타입이 void

: Task의 실행 결과나 상태 인지 불가

 2) submit()

: 반환 타입이 Future<T>

: Callable을 구현한 Task를 넘겨 결과를 반환받음

 3) invokeAny()

  : 반환 타입이 Future<T>

  : Task를 리스트에 넣어서 전달하고 성공한 Task 중 하나의 결과값을 반환

 4) invokeAll()

  : 반환 타입이 List<Future<T>>

  : Task를 리스트에 넣어서 전달하고 모든 Task의 결과값을 반환

- ExecutorService 종료를 위해 제공되는 메소드들

 1) shutdown()

: 실행 중인 모든 Task가 수행되고나서 종료

 2) shutdownNow()

: 실행 중인 모든 Task를 종료시키려고 하지만 보장하지는 않고 실행되지 않은 Task를 반환

 3) awaitTermination()

  : 새로운 Task 할당을 막고 일정 시간동안 실행 중인 Task를 기다리다가 시간이 지나면 강제 종료

 

ProgressDialog

: A8(API Level 26)부터 deprecated

- 간단한 대체 코드

Dialog dialog = new Dialog(this);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.setContentView(new ProgressBar(this));
dialog.setCanceledOnTouchOutside(false);
dialog.setOnCancelListener(dialogInterface -> finish());
dialog.show();

 

비트마스크를 통한 문자의 바이트 길이 계산

// UTF-8 인코딩된 데이터의 바이트 배열
byte[] data = text.getBytes();
int i = 0;

while(I < data.length) {
    byte CharLen;
    // 8비트 중에서 가장 왼쪽 비트가 0이라면 1바이트 문자
    if ((data[i] & -128) != -128) {
        CharLen = 1;
    // 8비트 중에서 6번째 비트가 0이라면 2바이트 문자
    } else if ((data[i] & 32) != 32) {
        CharLen = 2;
    // 8비트 중에서 5번째 비트가 0이라면 3바이트 문자
    } else if ((data[i] & 16) != 16) {
        CharLen = 3;
    // 8비트 중에서 4번째 비트가 0이라면 4바이트 문자
    } else if ((data[i] & 8) != 8) {
        CharLen = 4;
    // 8비트 중에서 3번째 비트가 0이라면 5바이트 문자
    } else if ((data[i] & 4) != 4) {
        CharLen = 5;
    // 위의 조건에 해당되지 않는다면 6바이트 문자
    } else {
        CharLen = 6;
    }
}

 

UTF-8 인코딩은 가변 길이 인코딩을 사용하며, 하나의 문자가 1바이트에서 6바이트까지 사용가능

1바이트 문자 : 0xxxxxxx

2바이트 문자 : 110xxxxx 10xxxxxx

3바이트 문자 : 1110xxxx 10xxxxxx 10xxxxxx

4바이트 문자 : 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

5바이트 문자 : 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

6바이트 문자 : 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxxx

 

바이트 변수의 하위 8비트만 추출하는 방법

data & 0xff

 

바이트 변수의 값을 이용해서 2바이트(16비트) 정수를 만드는 방법

((byte) b[4] & 0xff) + ((byte) b[3] & 0xff) * 256;

((byte) b[4] & 0xff) : 하위 8비트

((byte) b[3] & 0xff) * 256 : 상위 8비트(2 9승을 곱했기 때문에)

 

간단한 지연 함수

public static void delay(long time) {
    if (time > 0) {
        try {
            Thread.sleep(time);
        } catch (InterruptionException e) {
            Log.d(TAG, e.message());
        }
    }
}
반응형

'TIL' 카테고리의 다른 글

24.06.27  (0) 2024.06.27
24.06.20  (1) 2024.06.20
24.06.17  (0) 2024.06.17
24.06.12  (0) 2024.06.12
24.06.05  (0) 2024.06.05