24.11.18
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());
}
}
}