[Android] Multithreading For Performance (성능 향상을 위한 멀티쓰레딩 기법)

리스트뷰 등에 커스텀 레이아웃을 생성하여,
이미지와 텍스트 등의 데이터를 출력하고자 할때에 어느정도의 갯수를 보여주기 위해서는
엄청난 버벅거림과 짜증남을 감수해야 하고, 이것은 사용자에게 예외발생의 주 원인을 제공하기도 한다.

이러한 상황에 멀티쓰레딩을 사용한다면, 좀 더 유연하고 동적인 프로그램을 제공할 수 있다.
테스트를 해 보았는데, 매우 유용하게 사용이 가능할 것으로 보인다.
 
본문에도 나와있지만,
원문은 http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html 이다.

 
요청에 의해 관련 샘플코드 3가지를 링크첨부한다.
본문에 보여지는 이미지의 샘플코드는 세번째 링크이다.

Multithreading For Performance (성능 향상을 위한 멀티쓰레딩 기법)
Posted by Tim Bray on 19 July 2010 at 11:41 AM
[멀티태스크 상황에 처하는 것을 기꺼워하는 안드로이드 그룹 엔지니어 Gilles Debunne이 쓴 글. - Tim Bray]
 
응답성 있는 애플리케이션을 만드는 좋은 사례는 여러분의 메인 UI 쓰레드에게 최소한의 작업만을
시키는 것이다. 시간이 오래 걸릴 가능성이 있어서 애플리케이션에 장애가 될만한 작업은 별도의
쓰레드를 통해 처리해야 한다. 그러한 작업의 전형적인 예로는 예측하기 어려운 지연 현상들이
수반하는 네트워크 오퍼레이션을 들 수 있다. 사용자들은 일시적인 멈춤 정도라면 참아줄 수 있을 것이다.
특히, 뭔가를 진행하고 있다고 피드백을 제공해주면 인내심을 발휘할 수 있다.
그러나 아무 공지도 없이 멍 때리는 애플리케이션이라면 사용자들 입장에서 무슨 일이 벌어지는지,
얼마나 기다리면 되는 것인지 도저히 감을 잡을 수가 없을 것이다. 
 
오래 걸리는 작업을 별도의 쓰레드로 처리하는 패턴을 설명하기 위해, 이 글에서는 간단한 이미지
다운로더를 만들 것이다. 우리는 인터넷에서 다운로드받은 썸네일 이미지를 보여줄 ListView를 사용할 것이다.
백그라운드에서 다운로드 받는 비동기적인 작업을 만들면 애플리케이션이 좋은 속도를 유지하게 할 수 있다.
 
An Image downloader(이미지 다운로더)
프레임워크에 의해 제공되는 HTTP 관련 클래스들을 사용하여 웹에서 이미지를 다운로드 하는 것은
아주 간단하다. 예를 들어, 아래와 같은 구현이 있을 수 있다 :
 
static Bitmap downloadBitmap(String url) {
   
final AndroidHttpClient client = AndroidHttpClient.newInstance("Android");
   
final HttpGet getRequest = new HttpGet(url);

   
try {
       
HttpResponse response = client.execute(getRequest);
       
final int statusCode = response.getStatusLine().getStatusCode();
       
if (statusCode != HttpStatus.SC_OK) {
           
Log.w("ImageDownloader", "Error " + statusCode + " while retrieving bitmap from " + url);
           
return null;
       
}
       
       
final HttpEntity entity = response.getEntity();
       
if (entity != null) {
           
InputStream inputStream = null;
           
try {
                inputStream
= entity.getContent();
               
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
               
return bitmap;
           
} finally {
               
if (inputStream != null) {
                    inputStream
.close();  
               
}
                entity
.consumeContent();
           
}
       
}
   
} catch (Exception e) {
       
// Could provide a more explicit error message for IOException or IllegalStateException
        getRequest
.abort();
       
Log.w("ImageDownloader", "Error while retrieving bitmap from " + url, e.toString());
   
} finally {
       
if (client != null) {
            client
.close();
       
}
   
}
   
return null;
}

위에서 클라이언트와 HTTP request가 생성되었다. Request가 성공하면 이미지를 보유하고 있는
response 개체 스트림이 디코딩되어 비트맵 결과를 생성한다.
이를 위해, 여러분 애플리케이션의 manifest는 INTERNET 퍼미션을 가져야 한다.

주: 이전 버전의 BitmapFactory.decodeStream을 쓰면, 네트워크 연결이 느린 경우 이 코드가
제대로 작동하지 않을 수도 있다. 이 문제를 해결하려면 이것 대신 새로운 FlushedInputStream(inputStream)을
디코드하라. 이 헬퍼 클래스의 구현은 아래와 같다:

static class FlushedInputStream extends FilterInputStream {
   
public FlushedInputStream(InputStream inputStream) {
       
super(inputStream);
   
}

   
@Override
   
public long skip(long n) throws IOException {
       
long totalBytesSkipped = 0L;
       
while (totalBytesSkipped < n) {
           
long bytesSkipped = in.skip(n - totalBytesSkipped);
           
if (bytesSkipped == 0L) {
                 
int byte = read();
                 
if (byte < 0) {
                     
break;  // we reached EOF
                 
} else {
                      bytesSkipped
= 1; // we read one byte
                 
}
           
}
            totalBytesSkipped
+= bytesSkipped;
       
}
       
return totalBytesSkipped;
   
}
}

이 코드에서는 EOF에 도달하지 않으면 skip() 메쏘드가 제공된 바이트 수만큼 실제로 스킵 하도록 보장한다.

이 메쏘드를 여러분의 ListAdapter의 getView 메쏘드에 직접 사용하고자 한다면, 스크롤링 결과는
불쾌할 정도로 버벅거릴 것이다. 새로운 뷰를 디스플레이할 때마다 이미지 다운로드를 대기해야 하고,
부드럽게 스크롤링할 수 없을 것이다.
 
이것은 정말 좋지 않은 방법이므로, AndroidHttpClient는 그 자신이 메인 쓰레드에서 시작되는 것을 불허한다.
위의 코드는 “이 쓰레드는 HTTP 요청을 허용하지 않습니다”라는 오류 메시지를 보일 것이다.
그렇다고 AndroidHttpClient 대신 DefaultHttpClient를 쓰면 정말로 후회할 일이 생길 것이다.

Introducing asynchronous tasks(비동기적 태스크 도입)
AsyncTask 클래스는 UI 쓰레드에서 새로운 작업으로 분기하게 하는 가장 간단한 방법들 중 하나를 제공해준다.
이러한 작업을 생성하는 임무를 띠는 ImageDownloader 클래스를 만들어보자.
이 클래스는 특정 URL로부터 다운로드된 이미지 하나를 ImageView 하나에 할당하는 다운로드 메쏘드를 제공한다.

public class ImageDownloader {

   
public void download(String url, ImageView imageView) {
           
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
            task
.execute(url);
       
}
   
}

   
/* class BitmapDownloaderTask, see below */
}
BitmapDownloaderTask는 실제적으로 이미지를 다운로드하는 AsyncTask이다. 이것은 execute를 통해
시작되고, execute는 즉시 리턴된다. 따라서 이 메쏘드가 아주 빨리 실행될 수 있다.
이 메쏘드는 UI 쓰레드로부터 호출될 것이므로, 이러한 속도 향상은 바로 우리가 목표하는 바이다.
이 클래스에 대한 구현은 아래와 같다: 

class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
   
private String url;
   
private final WeakReference<ImageView> imageViewReference;

   
public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference
= new WeakReference<ImageView>(imageView);
   
}

   
@Override
   
// Actual download method, run in the task thread
   
protected Bitmap doInBackground(String... params) {
         
// params comes from the execute() call: params[0] is the url.
         
return downloadBitmap(params[0]);
   
}

   
@Override
   
// Once the image is downloaded, associates it to the imageView
   
protected void onPostExecute(Bitmap bitmap) {
       
if (isCancelled()) {
            bitmap
= null;
       
}

       
if (imageViewReference != null) {
           
ImageView imageView = imageViewReference.get();
           
if (imageView != null) {
                imageView
.setImageBitmap(bitmap);
           
}
       
}
   
}
}

 doInBackground 메쏘드는 자기 자신의 프로세스에서 태스크에 의해 실제적으로 실행되는 메쏘드이다.
이 메쏘드는 단순히 이 글 서두에 구현했던 downloadBitmap 메쏘드를 사용한다.

onPostExecute는 태스크가 끝날 때, 호출하는 UI 쓰레드에서 실행된다. 이 메쏘드는 결과 Bitmap을
파라미터로 취한다. 그 비트맵은 단순히 다운로드에 제공된 imageView에 연결되고
BitmapDownloaderTask에서 저장되었던 것이다. 이 ImageView는 WeakReference로 저장된 것을 주목해 보자.
이렇게 하면 진행 중인 다운로드 작업 때문에, 종료된 액티비티의 ImageView가 가비지 콜렉션되는 것을
방해하지 않게 된다. onPostExecute에서 사용을 하기 전, weak reference와 imageView
모두가 not null임을(즉, 가비지 콜렉션되지 않았다는 것) 확인해야 하는 이유가 바로 이 점 때문이다.
 
간단하게 만든 이 예제는 AsyncTask를 사용하는 방법을 보여준다. 여러분이 시도를 해본다면,
이 몇 줄 안 되는 코드도 ListView의 성능을 크게 향상시켜 스크롤이 부드럽게 되는 것을 확인할 수 있을 것이다.
AsyncTasks에 대한 보다 상세한 정보를 얻으려면 Painless threading을 읽어보라.

그런데 현재 구현에서는 ListView와 관련된 동작에 문제가 있다. 메모리 효율을 위해서 ListView는
사용자가 스크롤 할 때 뷰를 재사용한다. 사용자가 리스트를 넘길 때, 하나의 ImageView 객체가
여러 번 사용될 것이다. ImageView는 디스플레이될 때마다 이미지 다운로드 태스크 하나를 정확하게
트리깅하고, 그 결과 이미지가 적절히 변경될 것이다. 그럼 무엇이 문제인가?
대부분의 병렬 애플리케이션들과 같이, 중요한 이슈는 ‘순서’에 있다.
우리 예제의 경우, 다운로드된 태스크들이 시작 순서대로 끝난다는 보장이 없다.
그 결과, 리스트에서 최종적으로 디스플레이되는 이미지가 이전 이미지의 것일 수 있다.
다운로드하는 시간이 오래 걸리기만 하면 이런 일이 얼마든지 발생될 수 있다.
다운로드된 이미지들을 각각 특정한 ImageView들에 영속적으로 바운드시킨다면 문제가 되지 않겠지만,
리스트에서 사용되는 이 흔한 케이스를 위해, 진짜 해결책을 마련해보자.
 
Handling concurrency(동시성 처리하기)
이 문제를 해결하기 위해 우리는 다운로드 순서를 기억해야 한다. 그래서 맨 끝에 다운로드를 시작한
이미지가 디스플레이되게 해야 한다. 각각의 ImageView가 자신의 마지막 다운로드를 기억하게 하면 충분하다.
우리는 전담 Drawable 서브클래스를 사용하여 이 추가 정보를 ImageView에 더할 것이다.
이 Drawable 서브클래스는 해당 이미지에 대한 다운로드가 진행되는 동안 일시적으로 ImageView에 바인드될 것이다.

DownloadedDrawable 클래스의 코드는 아래와 같다:
 
static class DownloadedDrawable extends ColorDrawable {
   
private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

   
public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
       
super(Color.BLACK);
        bitmapDownloaderTaskReference
=
           
new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask);
   
}

   
public BitmapDownloaderTask getBitmapDownloaderTask() {
       
return bitmapDownloaderTaskReference.get();
   
}
}

구현 클래스 DownloadedDrawable는 ColorDrawable의 지원을 받아, 다운로딩이 진행되는 동안 ImageView가 검정색 백드라운드를 디스플레이하게 될 것이다. 이것 대신 “다운로드 진행 중”임을 알리는 이미지를 사용하면
사용자에게 작업 상황을 피드백할 수 있을 것이다. 여기서도 객체의 의존성을 줄이기 위해 WeakReference를
사용하고 있음을 주목하라.
 
이 새로운 클래스를 고려하여 우리의 코드를 수정해보자. 먼저, 다운로드 메쏘드는 이제 이 클래스의 인스턴스를
생성하여 imageView와 바인드시킨다.
 
public void download(String url, ImageView imageView) {
     
if (cancelPotentialDownload(url, imageView)) {
         
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView);
         
DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task);
         imageView
.setImageDrawable(downloadedDrawable);
         task
.execute(url, cookie);
     
}
}

cancelPotentialDownload 메쏘드는 이 imageView에 다운로드를 진행하고 있을지도 모르는 잠재적 작업을
중단시킨다. 곧 새로운 이미지를 위한 다운로딩을 시작할 것이기 때문이다. 그러나 이것만으로는 항상
최종적으로 다운로드하는 이미지를 디스플레이 하도록 보장할 수는 없다. 만약 그 태스크가 끝이 나
onPostExecute 메쏘드에서 기다리고 있다가, 새로 다운로드한 것 이후에 실행될 수도 있기 때문이다. 

private static boolean cancelPotentialDownload(String url, ImageView imageView) {
   
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

   
if (bitmapDownloaderTask != null) {
       
String bitmapUrl = bitmapDownloaderTask.url;
       
if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            bitmapDownloaderTask
.cancel(true);
       
} else {
           
// The same URL is already being downloaded.
           
return false;
       
}
   
}
   
return true;
}

cancelPotentialDownload는 AsyncTask 클래스의 cancel 메써드를 사용하여 진행 중인 다운로드를 멈추게 한다. 이 메쏘드는 대부분의 경우 true를 리턴하여 그 다운로드를 시작할 수 있게 한다. 유일한 예외 상황은
동일한 URL로부터 이미 다운로드가 진행되고 있는 경우로서, 이 때에는 취소시키는 대신 다운로드를 계속할 수
있게 해야 한다. 이 구현의 경우는 ImageView 하나가 가비지 콜렉션되었다면, 그와 연관된 다운로드는
중단되지 않는다는 점을 주목하자. 그러한 용도를 위해서는 RecyclerListener가 사용될 수 있다.
 
이 메써드는 헬퍼인 getBitmapDownloaderTask 메쏘드를 사용한다. 이 메쏘드는 다음과 같이 매우 직관적이다:
 
private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) {
   
if (imageView != null) {
       
Drawable drawable = imageView.getDrawable();
       
if (drawable instanceof DownloadedDrawable) {
           
DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable;
           
return downloadedDrawable.getBitmapDownloaderTask();
       
}
   
}
   
return null;
}

 마지막으로, onPostExecute를 변경하여 이 ImageView가 아직 이 다운로드 프로세스에 연결되어 있을 때에만
Bitmap을 바인드하도록 만들어야 한다.
 
if (imageViewReference != null) {
   
ImageView imageView = imageViewReference.get();
   
BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
   
// Change bitmap only if this process is still associated with it
   
if (this == bitmapDownloaderTask) {
        imageView
.setImageBitmap(bitmap);
   
}
}

이렇게 수정하여 이제 ImageDownloader 클래스는 우리가 기대하는 기본적인 서비스를 수행할 수 있다.
여기에서 다룬 예제, 혹은 이를 통해 설명한 비동기식 패턴을 자유롭게 사용하여 여러분의 애플리케이션들의
응답성을 높일 수 있기를 바란다.
 
Demo(데모)  
이 글의 소스 코드는 Google Code(http://code.google.com/p/android-imagedownloader/)에서 얻을 수 있다.
여러분은 이 글에서 설명한 세 가지 상이한 구현들(비동기 태스크를 쓰지 않는 경우,
태스크에 비트맵이 바인딩되지 않은 경우, 그리고 최종 수정 버전)을 비교하고 변환해 보아도 좋다.
여기서 다룬 문제를 좀더 잘 보여주기 위해 캐쉬 크기를 10개의 이미지로 제한하였다.

 
 
Future work(향후 과제)
이 코드는 병행성 측면에 초점을 맞추고자 간소화된 것이며 많은 유용한 기능들이 빠져 있다.
우선, ImageDownloader 클래스에 캐쉬를 사용하면 유익을 얻을 것이 분명하다. 특히 ListView와 함께
사용될 때는 더욱 그러하다. 사용자가 앞뒤로 스크롤할 때, 동일한 이미지를 여러 번 디스플레이할 가능성이
높기 때문이다. 이는 URL을 Bitmap SoftReferences에 맵핑하는 LinkedHashMap의 지원을 받는
LRU(Least Recently Used, LRU) 캐쉬를 사용하여 쉽게 구현될 수 있다. 좀 더 복잡한 캐쉬 메커니즘을
만드는 것은 이미지의 로컬 디스크 저장소에 영향을 받는다. 필요하다면 썸네일 생성과 이미지 리사이징
기능도 더할 수 있다.

우리가 구현한 코드로도 다운로드 오류와 타임아웃을 정확히 처리할 수 있다. 이런 경우 null Bitmap이
리턴될 것이다. 원하는 경우 오류가 난 이미지를 디스플레이할 수도 있다.

이 글에서 다룬 HTTP request는 매우 간단한 것이다. 개발자들 중에는 특정 웹사이트의 요청에 따라
request에 파라미터나 쿠키를 더하고자 할 수도 있다.
 
이 글에서 사용된 AsyncTask 클래스는 매우 편리하며, 시간이 걸리는 작업들을 UI 쓰레드에서 우회시킬 수 있다.
보다 정교하게 작업을 컨트롤하기 위해 Handler 클래스 사용을 원할 수도 있다.
예를 들어, 이 예제의 경우, 병렬로 실행되고 있는 다운로드 쓰레드의 총 개수 등을 컨트롤하는 기능을 더할 수 있다.

020_Thread01.zip
021_Thread02_AsyncTask.zip
MultiThread_ImageDownloader.zip
MultiThread_ImageDownloader_AsyncTask.zip

출처  : http://www.kandroid.org/board/board.php?board=kandori&command=body&no=52

[Ubuntu Diary] OS 다운로드 및 설치

우분투 서버 구축일기에 대한 두 번째 포스트를 작성하고 있습니다.

사실 아래 기재할 내용은 몇일전에 메모해놓은 내용인데요. 약간 살을 붙여서 보기 쉽도록 작성해 보겠습니다.
일단 제목에서처럼 우분투 서버버전으로 OS를 다운로드 받아야 겠습니다.
주소는 http://www.ubuntu.com/download/ubuntu/download 입니다.
우분투 데스크탑이 아닌 서버버전을 설치할 것이므로 상단 메뉴의 서브탭에서 Ubuntu Server를 선택합니다.
일반버전과 LTS버전으로 나뉘어 있는데요. 사실 저도 잘 몰라서 찾아보았습니다.

10.04 LTS 에서 LTS는 Long Term Support의 줄임말입니다.
일반 버전은 2년간 지원을 해주고 LST 버전은 데스크탑 버전은 3년, 서버 버전은 5년간 지원을 해줍니다.

위와같은 내용의 글들이 확인되었구요.
OS의  패키지 구성은 아주 미세하고, 일반버전은 약간 실험적인 기능들이 존재할 수 있지만
LTS버전은 안정성이 보장된 패키지들로 구성된다고 합니다.
저는 'ubuntu-11.04-server-i386.iso' 파일을 다운로드 받아, 시디로 구웠습니다.
설치할 테스크탑은 여자친구가 오래 사용했던 피씨를 전달받아 사용하고 있었습니다.
(기존에는 CentOS가 설치되어 있던 데스크탑이였죠)

전에도 우분투 데스크탑용 OS를 설치해본 경험이 있어서인지, 아니면 원래 심플한것인지
우분투의 설치는 윈도우보다도 쉽다고 할 수 있습니다.
단, 설치과정중에 파티션 구성에 대해 물어옵니다. 저는 '자동'으로 구성하는 항목을 선택하였습니다.
그리고 프록시 설정에 대한 내용도 존재하는데요. 사용하지 않을 것이므로 공란으로 비워놓고 진행하였습니다.

설치 하다보면, 어떠한 패키지들을 기본적으로 설치할 것이냐고 묻는 과정이 있습니다.
저 같은 경우에는 LAMP 패키지만을 선택하고, 나머지 패키지들은 추후에 직접 설치해보기로 하였습니다.
 LAMP 패키지만 설치하여 계속 진행하였습니다.

자. 설치가 완료되었습니다.
어라. 근데 우분투 데스크탑에서 보아왔던 GUI 환경은 어디로 가고 까만화면에 하얀 텍스트만 출력됩니다.
거기다가 당황스럽게도 저에게 뭔가 바라고 있는것 같아보이는 프롬프트가 꿈뻑거리고 있구요.

검색을 통해 찾아보니, 우분투 서버버전은 기본적으로 GUI 환경을 제공하지 않는다고 합니다.
그래서 이건 안돼! 라는 생각으로 GUI 환경을 설치해야겠다는 생각을 했습니다.

sudo su -
비밀번호입력
apt-get update (패키지 소스리스트 업데이트 명령어 - 금방되더군요)
apt-get diet-upgrade (각 패키지에 대한 의존성검사를 수행하면서 업그레이드 명령어 - 좀 오래걸립니다)
자. 이제 GUI 환경을 사용할 수 있는 패키지를 설치해야겠습니다.
그놈이라는 패키지를 설치할 것인데요. 그놈이 참 이름이 거시기 합니다.
그놈에 대한 정보 : http://ko.wikipedia.org/wiki/GNOME

그놈이 뭔지 알았으니, 이제 설치를 해봐야겠죠?
일단 아래의 명령어를 이용해 기본적인 그래픽 시스템을 설치합니다.
apt-get install xorg gdm
/etc/init.d/gdm start
패키지 설치 중간에 Y/N 하며, 묻는 과정이 있는데요.
Y입력 후 엔터 사뿐히 눌러주시면 됩니다.

기본 시스템을 설치했으니, 이제 그놈을 설치해야 합니다.
아래의 명령어를 이용합니다.
apt-get install gnome-session gnome-menus gnome-panel metacity nautilus
'apt-get auto remove' 어쩌구 하는 문자열이 출력되면서 끝났습니다.
검색을 통해 또 찾아보니, 아래의 권장되는 관련 패키지들을 함께 설치해주는 것이 좋답니다.

권장되는 패키지들(apt-get install 뒤에 설치할 패키지들의 이름을 써서 설치함)

     - gnome-applets

     - gnome-control-center

     - gnome-volume-manager

     - libsmbclient

     - gnome-screensaver

     - menu

     - network-manager-gnome

     - gnome-utils

     - gnome-system-tools

     - gnome-netstatus-applet

     - gnome-nettool

     - libgnomevfs2-extra


선택적으로 설치한 유용한 패키지들

     - gnome-terminal

     - gedit

     - firefox


데스크탑 테마설치함

     - ubuntu-artwork

     - ubuntu-sounds

     - human-theme

     - usplash-theme-ubuntu


패키지 관리자 설치함

     - apt-get install gnome-app-install update-manager restricted-manager

GUI 환경에 대한 패키지 설치가 끝났나요?
아래의 명령어를 통해 GUI 환경으로 시작해 봐야겠습니다.
/etc/init.d/gdm start
두근두근~ 재부팅 완료!

어랏.
GUI 환경에서의 로그인 과정이후, 무슨 오류메시지가 츌력됩니다.
유틸리티를 수행 할 수 있는 하드웨어가 존재하지 않는다는 내용과 전통적인 환경을 사용하라는...
싫어. GUI 쓸거야!라고 하면서 무시하고 계속 진행했습니다.

뜨악. 한글이 네모상자로 표시됩니다. ㅠㅠ
우분투에는 기본적으로 '시냅틱 패키지 관리자'라는 유틸리티가 존재하는데요.
이 메뉴가 네모네모네모... ㅠㅠ

이런 뭐가 잘못된건가? 라며 속상해했습니다. 하지만 한글폰트가 설치되지 않았기 때문이구요.
아래의 명령어를 통해 폰트를 설치하시면 됩니다.
sudo apt-get install ttf-unfonts
저는 위 폰트 패키지 설치 후, 한글이 정상적으로 출력되었는데 어떠신가요?

이상으로 기본적인 OS설치와 필수 패키지들을 설치하는 과정을 기재해 보았습니다.
저도 리눅스에 대해 많이 알지는 못하지만, 기존에 사용했던 CentOS에 비하자면 우분투는 정말 사용하기 편리한 녀석인 것 같습니다. 아직까지는요.

이번 포스트는 이만 마치고, 다음번엔 LAMP에 대한 내용과 JVM을 설치하는 과정을 기재해 보겠습니다.
과연 누가 이 포스트를 보시고 도움이 되실지 궁금합니다.
(정답 : 나)

[Ubuntu Diary] 시작하며...

안녕하세요. 블로그 운영자입니다.

2011/10/06부터 시작한 '우분투 서버 구축'에 대한 첫 포스트를 작성합니다.
리눅스를 공부하는 마음으로 하나하나 깨우쳐가며, 마치 일기 쓰듯이 글을 작성하겠습니다.
제 자신에게도 도움이 될 것이고, 저같은 분들에게도 마찬가지로 도움이 될 수 있을 것 같아서요.

그럼 첫 글을 간단히 마치고,
그동안 기록했던 내용들을 하나하나 적어보겠습니다.

[Android] ListView, transcriptMode

android:transcriptMode에 대해서 알아보자.

android:transcriptMode
Sets the transcript mode for the list. In transcript mode, the list scrolls to the bottom to make new items visible when they are added.

Must be one of the following constant values.

Constant     Value     Description
disabled        0          Disables transcript mode. This is the default value.
normal          1          The list will automatically scroll to the bottom when a data set change
                                 notification is received and only if the last item is already visible on screen.
alwaysScroll  2         The list will automatically scroll to the bottom,
                                 no matter what items are currently visible.

 위에 자료는 안드로이드 API에 있는 내용이다. 간단하게 설명을하자면
 transcriptMode 속성은 3가지의 값을 정의해줄수 있고 각각의 설명은 아래와 같다. 

 1. disabled : 스크롤이 되지 않음
 2. nomal : 현재의 포커스에서 추가된만큼의 포커스가 이동
 3. alwaysScroll : 마지막에 추가된곳으로 포커스 이동

 제가 만들프로그램은 마지막에 추가된곳으로 포커스가 이동을 해야하기 때문에 alwaysScroll을 줘야한다.

getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_ALWAYS_SCROLL);

위와같이 Java단에서 설정을 해줘도 되고 xml에서 설정을 해줘도 된다.
설정이 올바르게 끝났으면 정상적으로 밑에 화면과 같이 작동을 할것이다.

[PHP] Eclipse + PHP + RSE 환경설정

11

오늘은 무슨 바람이 들었는지, PHP 환경설정을 해보기로 했습니다.

이클립스 공식 사이트에 가보니, PHP 개발용 IDE 지원이 종료되었나봅니다.
저렴한 영어실력으로 본 내용이니 실력좋으신 분들은 가서 직접 보고 오시면 좀 더 좋을 것 같네요.

기본 이클립스에 PDT를 설치하면, PHP 개발이 가능하다고 들었습니다.
그래서 PDT를 찾아다니다가 검색을 통해 zend-eclipse-php-helios-win32-x86.zip 파일을 다운로드 받았습니다.

해당 파일은 검색하면 많이 나옵니다. 예를들면...

http://downloads.zend.com/pdt/all-in-one/helios/

자료를 찾으면서 RSE에 대해서 알 수 있었습니다.
Remote System Explorer 라는 녀석이였는데, 플러그인 형태로 제공됩니다.
공식 사이트는 아래의 경로이구요.

http://www.eclipse.org/tm/.

제가 살펴본 바로는 이클립스를 사용하면서 FTP / Linux / SSH / Telnet / Unix / Windows 등과 연동하여 사용할 수 있도록 돕는 플러그인으로 확인했습니다. (눈으로만)

일단 설치방법에 대한 조언을 얻기 위해 아래의 블로그를 찾아가 보았는데요. 정말 도움이 많이 되었습니다.

http://byeonely.tistory.com/147

위 블로그의 도움을 얻어 환경설정하는 순서와 방법에 대한 내용을 간추려 적어보도록 하겠습니다.

1. http://www.eclipse.org/dsdp/tm/ 여기에 접속

2. Latest Release 라는 텍스트 옆의 Download 클릭

3. All-in-one (includes runtime and documentation for dstore, ftp, local, ssh, and telnet) 이라는 타이틀을 가진 항목에서 RSE-runtime-3.3.zip (Recommended for most users)를 선택하여 다운로드

4. 압축 해제 후, 설치된 이클립스의 각 폴더에 복사해 넣기(경로 그대로 해당 위치에 넣으시면 됩니다.)

5. 이클립스 구동

6. 이클립스의 퍼스펙티브에 Remote System Explorer 라든지, View에 동일항목이 추가되었는지 확인

오늘은 환경설정만 해보았고, 실제 사용하는 방법등에 대해서는 다음 포스트에 적어보도록 하겠습니다.
아참, PHP 무료 호스팅을 찾으시는 분들은 아래 사이트에 한번 가보세요.
저도 오늘 처음 가입해서 사용해 보았는데, 사용하기 편리하고 좋더군요.

http://www.zymic.com/

'기술참고자료 > PHP' 카테고리의 다른 글

[PHP] Flickr API를 이용한 갤러리 구현  (0) 2012.04.18