주요글: 도커 시작하기

GoF 디자인 패턴의 Template Method (템플릿 메서드) 패턴 요약




안드로이드로 앱을 개발할 때 흔히 필요한 기능이 특정 URL 이미지를 로컬 파일로 다운로드 받은 뒤에, 그 이미지를 ImageView로 출력하는 기능이다. 이미지를 다운받아 ImageView로 출력하려면 일단 이미지를 다운받아 로컬에 파일로 저장하는 기능이 필요하다.


본 글에서는 앞서 "유지보수를 고려한 안드로이드 비동기 처리 기반 코드 만들기" 글에서 비동기 처리를 위한 만들었던 AsyncExecutor를 이용해서 파일을 다운로드 하는 코드를 만들어보겠다. (앞서 글에서 AsyncExecutor의 구현을 먼저 확인해 본 뒤 본 글을 읽으면 쉽게 이해가 될 것이다.)


이 기능을 구현하기 위해 세 개의 클래스를 만들었다.

  • HttpRequestHelper: HttpClient를 이용해서 다운로드 받은 데이터를 File로 쓰는 기능 제공
  • FileDownloadCallable: AsyncExecutor에서 실행할 Callable 구현 클래스로서 HttpRequestHeloper가 제공하는 다운로드 기능을 실행
  • AsyncFileDownloader: 비동기로 파일 다운로드를 처리해주는 기능 제공

HttpRequestHelper 클래스의 파일 다운로드 기능 구현


HttpRequestHelper 클래스는 지정한 URL로부터 데이터를 읽어와 지정한 파일로 기록하는 기능을 제공한다. 다음은 구현 코드이다.


public class HttpRequestHelper {


public File download(String url, File toFile) throws IOException {

AndroidHttpClient client = AndroidHttpClient.newInstance("ANDROID");

HttpGet getRequest = new HttpGet(url);

try {

HttpResponse response = client.execute(getRequest);

checkStatusAndThrowExceptionWhenStatusIsNotOK(response);

return writeResponseToFileAndGet(response.getEntity(), toFile);

} catch (IOException e) {

getRequest.abort();

throw e;

} finally {

client.close();

}

}


private void checkStatusAndThrowExceptionWhenStatusIsNotOK(

HttpResponse response) throws IOException {

int statusCode = response.getStatusLine().getStatusCode();

if (statusCode != HttpStatus.SC_OK) {

throw new IOException("invalid response code:" + statusCode);

}

}


private File writeResponseToFileAndGet(HttpEntity entity, File toFile)

throws IOException {

InputStream in = null;

try {

IOUtils.copy(entity.getContent(), toFile);

return toFile;

} finally {

IOUtils.close(in);

entity.consumeContent();

}

}


public static HttpRequestHelper getInstance() {

return new HttpRequestHelper();

}


}


download() 메서드는 지정한 URL을 이용해서 HttpClient를 실행한다. 응답 코드가 OK가 아니면 익셉션을 발생시키고, 정상응답인 경우 응답 데이터를 지정한 파일에 저장한다.


FileDownloadCallable 클래스 구현


FileDownloadCallable 클래스는 AsyncExecutor가 비동기로 실행할 기능을 제공한다. FileDownloadCallable의 call() 메서드는 앞서 구현한 HttpRequestHelper의 download() 기능을 실행하고 그 결과를 리턴한다.


public class FileDownloadCallable implements Callable<File> {


private String url;

private File file;


public FileDownloadCallable(String url, File file) {

this.url = url;

this.file = file;

}


@Override

public File call() throws Exception {

return HttpRequestHelper.getInstance().download(url, file);

}


}


AsyncFileDownloader 클래스 구현


AsyncFileDownloader 클래스는 AsyncExecutor와 FileDownloadCallable을 이용해서 비동기로 파일을 다운받아 저장한다. 아래 코드는 구현 코드이다.


public class AsyncFileDownloader {


private Context context;


public AsyncFileDownloader(Context context) {

this.context = context;

}


public void download(String url, AsyncCallback<File> callback) {

download(url, null, callback);

}


public void download(String url, File destination, AsyncCallback<File> callback) {

try {

destination = getDestinationIfNotNullOrCreateTemp(destination, callback);

} catch (IOException e) {

callback.exceptionOccured(e);

return;

}

runAsyncDownload(url, destination, callback);

}


private File getDestinationIfNotNullOrCreateTemp(File destination,

AsyncCallback<File> callback) throws IOException {

if (destination != null) {

return destination;

}

return createTemporaryFile();

}


private File createTemporaryFile() throws IOException {

return File.createTempFile("afd", ".tmp", context.getCacheDir());

}


private void runAsyncDownload(String url, File destination, AsyncCallback<File> callback) {

Callable<File> callable = new FileDownloadCallable(url, destination);

new AsyncExecutor<File>().setCallable(callable).setCallback(callback).execute();

}


}


AsyncFileDownloader의 download() 메서드는 다운로드 받을 URL(url 파라미터), 다운받은 파일을 보관할 경로(destination 파라미터), 그리고 파일 다운이 완료될 때 호출할 콜백(callback 파라미터)를 전달받는다. download() 메서드의 실행 순서는 다음과 같다.

  • getDestinationIfNotNullOrCreateTemp() 메서드를 이용해서 destination이 null 이면 임시 파일을 생성한다.
    • 임시 파일을 생성하는 도중에 익셉션이 발생하면 callback 객체에 에러 사실을 알리고 종료한다.
    • 임시 파일은 캐시 디렉토리에 생성한다.
  • runAsyncDownload() 메서드를 실행해서 비동기 다운로드를 실행한다.
runAsyncDownload() 메서드는 AsyncExecutor 객체를 이용해서 비동기로 파일 다운로드를 처리한다.


AsyncFileDownloader 사용해서 다운로드 파일 사용하기


다음 코드는 AsyncFileDownloader의 사용 예이다.


public class DetailActivity extends Activity {

...

private void downloadFile() {

new AsyncFileDownloader(this).download(someImageUrl, fileDownCallback);

}


private AsyncCallback<File> fileDownCallback = new AsyncCallback.Base<File>() {

@Override

public void onResult(File result) {

// result 파일을 사용해서 처리

}


@Override

public void exceptionOccured(Exception e) {

// 익셉션 처리

}

};



관련 자료



  1. andu 2013.05.16 17:40

    AsyncFileDownloader의 사용 예시에 new AsyncCallback.Base<File>()가 사용되는데,
    AsyncCallBack 클래스에는 Base() 없네요.
    이것도 추가됐나요?

CentOS에 설치한 Confluence의 chart 플러그인이 한글을 제대로 출력하지 않아, 그 문제를 해결하고자 구글신께 물어보고 아래와 같이 간단하게 해결할 수 있다.


먼저 한글 폰트가 없다면 root 계정으로 한글 폰트를 설치한다.


$ yum install fonts-korean


위 명령을 실행하면 /usr/share/fonts/korean/TrueType 디렉토리가 생성되고, 그 디렉토리에 batang.ttf, dotum.ttf 등의 한글 폰트 파일이 생성된 것을 확인할 수 있다.


한글 폰트를 설치했으면 다음은 java를 설정할 차례이다. 여기서는 자바 1.6 버전을 /usr/local/java/에 설치했다고 가정한다. /usr/local/java/jre/lib 디렉토리에 위에 설치한 한글 폰트를 사용하도록 font.properties 파일을 생성해주면 된다. 이 파일을 가장 쉬운 방법은 다음과 같이 기존에 존재하는 파일을 하나 복사하는 것이다.


$ cd /usr/local/java/jre/lib

$ cp fontconfig.RedHat.properties.src fontconfig.properties


이걸로 끝이다. fontconfig.properties 파일을 열어보면 다음과 같이 한글 설정 및 폰트 경로가 지정된 것을 확인할 수 있다. 만약 다른 언어들에 대한 설정이 거슬리면 지워주면 된다.


# Component Font Mappings

serif.plain.korean-iso10646=-misc-baekmuk batang-medium-r-normal--*-%d-*-*-c-*-iso10646-1

serif.bold.korean-iso10646=-misc-baekmuk batang-medium-r-normal--*-%d-*-*-c-*-iso10646-1

... [기타 폰트 설정들]


# Font File Names

filename.-misc-baekmuk_batang-medium-r-normal--*-%d-*-*-c-*-iso10646-1=/usr/share/fonts/korean/TrueType/batang.ttf

filename.-misc-baekmuk_gulim-medium-r-normal--*-%d-*-*-c-*-iso10646-1=/usr/share/fonts/korean/TrueType/gulim.ttf

... [기타 폰트 설정들]


# AWT X11 font paths

awtfontpath.korean-iso10646=/usr/share/fonts/korean/TrueType

... [기타 폰트 설정들]




만약 다른 경로에 한글 폰트를 설치했다면 경로를 알맞게 설정해주면 된다.




+ Recent posts