컴퓨터과학/Android 2014.11.22 23:08
 안드로이드에서 뿐만 아니라 일반적으로 프로그램을 작성할 때에 이벤트를 이용하여 처리해야 하는 부분이 있습니다.
예를 들면 버튼에 더블클릭을 구현해야 한다던지, 프로그래스바에서 진행상태를 화면에 표시하거나 하는 것들이 될 수 있습니다.

일반적으로 이벤트는  callback이라고도 하는데 어떠한 동작에 대한 피드백이라고도 할 수 있습니다. 

이벤트를 이용하여 프로그램을 작성하면 자연스럽게 뷰와 비즈니스로직이 분리되어 관리하기 쉬운 코드가 작성됩니다. 
물론 가독성도 나아지며, 코드도 훨씬 간결해집니다.

그러한 이벤트는 크게 세가지 정도로 구현 할 수 있습니다.
  • 안드로이드 이벤트 처리 방법
    1. 핸들러
    2. 옵저버
    3. 인터페이스
    4. 브로드캐스팅

이벤트 처리의 기본 구조는 메시지를 발생시키는 쪽과 구독하는 쪽이 있으며, 구독하는 쪽에서 구독 등록을 하면 발생시키는 쪽에서 이벤트 발생시 통지를 해줍니다.
코드만 다를 뿐 개념은 동일하다고 생각하면 됩니다.

 용도에 따라서 각각의 방법을 이용하면 되는데 (저는 핸들러 : 네트워크 처리, 옵저버 : 상태 관리, 브로드캐스팅 : 전역 통지가 필요한 경우에 주로 사용하고 있습니다.)

 이 포스트에서는 핸들러, 옵저버, 브로드캐스팅은 다루지 않고, 인터페이스를 이용하는 방법을 중심으로 다루도록 하겠습니다.
다른 방법은 다른 포스팅을 참조하여 주시면 감사하겠습니다.

 인터페이스를 이용하는 방법은 몇 가지 특징이 있습니다. 인터페이스 자체의 특징이라고도 할 수 있습니다. 

  • 명확함
     인터페이스를 이용하여 이벤트를 처리하는 방법은 다른 이벤트 처리 방법에 비하여 굉장히 큰 장점이 있습니다. 바로 명확하다는 것입니다.
    이름을 개발자가 원하는 대로 작성할 수 있어, (너무 제멋대로만 아니라면) 다른 개발자가 참조할 때에 해당 이벤트의 의도를 알 수 있습니다.
    버튼의 onClick() 이벤트는 누가 보아도 버튼을 클릭할 때 발생하는 이벤트라는 것을 알 수 있듯이, 다른 이벤트 처리 방법에 비해 굉장히 명확하다는 장점을 가지고 있습니다.

  • 이벤트 상속
     부모의 이벤트를 상속받아 처리 할 수 있는 장점이 있습니다. 물론 처리하지 않을 수도 있습니다. 상속관계를 통해 이벤트의 전달을 제한하거나 코드를 분리하여 작성 할 수 있습니다.

  • 추상화
     인터페이스는 추상화를 통해 실제 발생하는 이벤트를 런타임에 결정할 수 있습니다.


특징은 아무리 말해봐야 직접 느끼지 않으면 모르니 바로 코드를 뿌리겠습니다.

public class CustomCamera extends Camera {

   @Override
   protected void onPictureTaken(byte[] data, Camera camera) {
           // perform some events.
   }

     private ArrayList<ITakePictureEvent> mTakePictureEvent = new ArrayList<CustomCameraEventManager.ITakePictureEvent>(1);

     private void performPictureTakenEvent(Bitmap bitmap, Bitmap thumbnail){
          for(ITakePictureEvent event : mTakePictureEvent){
               event.onPictureTaken(bitmap, thumbnail);
          }    
     }
    
     private void performTakePictureErrorEvent(int error){
          for(ITakePictureEvent event : mTakePictureEvent){
               event.onError(error);
          }    
     }
    
     public void setOnTakePictureListener(ITakePictureEvent event){
          if(!mTakePictureEvent.contains(event)) {
               mTakePictureEvent.add(event);
          }
     }

     public void removeOnTakePictureListener(ITakePictureEvent event){
          mTakePictureEvent.remove(event);
     }

     public void removeAllOnTakePictureListener(){
          mTakePictureEvent.clear();
     }

     public interface ITakePictureEvent{
          public void onPictureTaken(Bitmap result, Bitmap thumbnail);
          public void onError(int error);
     }

}

 위 코드는 카메라에서 사진이 찍혔을 때에 그 결과를 반환하는 코드입니다. (전체 코드가 아닌 일부라 저 코드만으로는 동작하지 않습니다.)
그리고 편의를 위해 코드를 하나의 클래스에 넣었습니다.

우리는 byte[]로 나오는 결과를 적절하게 비트맵으로 변환하여 전달하는 이벤트를 만들어 볼 것입니다.
물론 그냥 카메라 클래스에서 제공하는 결과를 비트맵으로 변환해주는 메서드를 만들어도 되겠지만, 학습을 목표로 하는 것이니만큼 그냥 진행하도록 하겠습니다.

우선 코드를 하나씩 잘라서 설명하도록 하겠습니다.



  • 인터페이스(이벤트 리스너)
public interface ITakePictureEvent{
          public void onPictureTaken(Bitmap result, Bitmap thumbnail);
          public void onError(int error);
}

사진 찍을 때 사용하는 이벤트라는 느낌이 드는 인터페이스입니다.
onPictureTaken() 메서드는 사진을 찍은 결과 비트맵과 썸네일 비트맵을 이벤트 구독하는 쪽에 넘겨 줄 것입니다.
onError() 메서드는 에러 발생시 에러 아이디를 넘결 줄 것처럼 생겼군요.


  • 이벤트 배열
private ArrayList<ITakePictureEvent> mTakePictureEvent = new ArrayList<CustomCameraEventManager.ITakePictureEvent>(1);

구독 신청한 이벤트를 관리하기 위해 사용하는 배열입니다. 저는 ArrayList를 사용하였으나, 편의를 위해 한 것일 뿐 별다른 의도는 없습니다.
하나 이상의 이벤트를 동시에 구독할 수 있게 하기 위해 배열을 이용한 것이기 때문에 구독자가 반드시 하나라는 보장이 있다면 배열을 사용하지 않아도 됩니다.
외부에서 이벤트 구독자를 핸들링 할 수 없도록  접근 지정자를 private으로 지정하였습니다.


  • 이벤트의 구독 신청
public void setOnTakePictureListener(ITakePictureEvent event){
          if(!mTakePictureEvent.contains(event)) {
               mTakePictureEvent.add(event);
          }
}

외부에서 이벤트 구독 신청을 할 수 있도록 노출하는 메서드입니다. 중복된 이벤트는 추가하지 않는 구조입니다.


  • 이벤트 구독 취소
public void removeOnTakePictureListener(ITakePictureEvent event){
          mTakePictureEvent.remove(event);
}

public void removeAllOnTakePictureListener(){
          mTakePictureEvent.clear();
}

이벤트의 구독을 취소할 수 있도록 외부에 노출하는 메서드입니다. 해당 이벤트만 구독을 취소하거나 모든 이벤트를 취소할 수 있습니다.


  • 이벤트의 통지
private void performPictureTakenEvent(Bitmap bitmap, Bitmap thumbnail){
          for(ITakePictureEvent event : mTakePictureEvent){
               event.onPictureTaken(bitmap, thumbnail);
          }    
}
    
private void performTakePictureErrorEvent(int error){
          for(ITakePictureEvent event : mTakePictureEvent){
               event.onError(error);
          }    
}

이벤트를 통지하여 구독자에게 전달하는 메서드입니다. 이 메서드는 클래스의 내부에서만 호출하기 위해 접근지정자를 private으로 설정하였습니다.

이제 카메라 어디에선가 저 메서드를 호출하기만 하면 이벤트가 발생됩니다.

음...그런데 최초 이벤트는 어디서 발생시켜야 할까요?

안드로이드에서 제공하는 카메라의 경우는 onPictureTaken(byte[] data, Camera camea)의 형태를 가진 이벤트를 제공하고 있습니다.
카메라가 사진을 찍으면 그 결과를 반환하는 이벤트죠.
우리는 그 이벤트 안에서 우리가 만든 이벤트를 발생시키도록 하겠습니다.


  • 이벤트의 발생
@Override
protected void onPictureTaken(byte[] data, Camera camera) {
     Bitmap bitmap = getBitmapFromByteArray(data);
     Bitmap thumbnail = getBitmapThumbnail(bitmap);

     performPictureTakenEvent(bitmap, thumbnail);
}

​이 부분이 이벤트의 최초 시작점입니다.
getBitmapFromByteArray(), getBitmapThumbnail() 메서드는 그냥 가상으로 만든 메서드입니다. 의미상으로 이해하면 될 것 같습니다.


  • 이벤트의 구독과 처리
public class CameraActivity extends Activity implements ITakePictureEvent{

       CustomCamera mCamera = new CustomCamera();
       ImageView mImageView;
       ImageView mThumbImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_main);
        mImageView = (ImageView) findViewById(R.id.image_view);
        mThumbImageView = (ImageView) findViewById(R.id.image_thumb_view);

        // 이벤트 구독 신청
        mCamera.setOnTakePictureListener(this);  
    }

       // 이벤트 발생시 이곳으로 통지가 옵니다.
      @Override
      public void onPictureTaken(Bitmap bitmap, Bitmap thumbnail) {
              // 이벤트에서 넘어온 부분을 처리
              mImageView.setImageBitmap(bitmap);
              mThumbImageView.setImageBitmap(thumbnail);
      }

      @Override
      public void onError(int error) {
               // 에러 처리
      }
}

이벤트를 구독 신청하고 통지된 이벤트를 처리하는 코드입니다. 
액티비티에서 이벤트를 구독신청하고, 이벤트 인터페이스를 구현하여 이벤트 발생시 통지를 통해 원하는 처리를 할 수 있습니다.

이러한 이벤트 처리를 통해 결과적으로 우리는 뷰(액티비티)와 비즈니스로직(byte[] to Bitmap)의 코드를 분리하였습니다.

다음 포스트에서는 커스텀 컨트롤을 작성하면서 커스텀 컨트롤에 대한 이해와 이벤트의 처리를 응용하는 방법을 소개하겠습니다.

감사합니다.



posted by 연식킴
컴퓨터과학/Android 2014.04.24 23:06

안드로이드 sdk에서 제공하는 나인패치툴(draw9patch)를 실행해 보면 상단에 show / hide bad patch 라는 버튼이 있다.

그 버튼을 누르면 화면에 빨간색 네모 박스가 보였다 / 사라졌다 하는데...

(bad patch가 없는 경우는 물론 안 보인다.)

과연 bad patch란 무엇일까?



간단히 설명하면 9패치 이미지가 늘어날 때에 그려지는 가변 구간이다.
bad patch가 존재하면 이미지가 늘어날 때 이미지의 일관성(Visual coherence)을 유지할 수 없다고 매뉴얼에 써있다.ㅋ


음... 여튼 왼쪽 부분의 1픽셀만 선택하면 bad patch가 사라지더라.


posted by 연식킴
컴퓨터과학/Linux 2014.04.18 11:43

우분투 14.04 LTS버전이 출시되었다.

현재 기본적으로 ibus 1.5.5가 설치되어 있는데,

기본적으로는 Ctrl + Space 가 한/영 번환 단축키로 지정되어 있다.


나는 이클립스에서 Ctrl + Space를 코드 자동 완성 단축키로 사용하고 있기 때문에 두 키가 겹쳐 변경을 해야했다.


그리고 텐키리스 키보드를 사용하고 있어 오른쪽 Alt키와 한/영키를 함께 쓰고 있는데 키보드 단축키 설정시 Alt R로 설정이 되었다.


ibus는 버전이 바뀔 때마다 설정 방법이 매번 바뀌어서 불편하지만...

이번에는 좀 나아진 듯한 느낌이다.


1. [시스템 설정]에 들어가서 [키보드]메뉴를 선택한다.



2. [바로가기] 탭 선택 후 왼쪽에 [자판 입력] 항목을 선택한다.


3. [구성키]를 [오른쪽 Alt]로 설정하고 [다음 입력 소스로 전환]을 선택하여 한/영키를 누르면 [Alt r]이 아닌 [Multi_key]로 설정이 된다. 



4. 저장하고, 바로 반영이 안되는 경우 재 로그인을 하면 된다.



posted by 연식킴
컴퓨터과학/Linux 2014.04.14 16:07

dpkg --list |grep "^rc" | cut -d " " -f 3 | xargs sudo dpkg --purge

데비안 계열 리눅스에서 설정 파일이 남아있는 삭제된 패키지를 완전 삭제.



posted by 연식킴
컴퓨터과학/Android 2014.02.13 19:20

간혹 ImageView가 정사각형으로 나왔으면 하는 경우가 있다.

단순히 정사각형이라면 layout_width와 layout_height를 같은 수치로 넣어주면 되지만...

그런 경우 동적으로 사이즈가 늘어나진 않아 불편하다.

아래 사이트에 답이 있다.


http://android-layouts.com/category/tags/square


위 내용을 참고하여 간단히 정리하면 ImageView를 상속받는 클래스를 생성 후 onMeasure() 메서드를 오버라이딩하고 width와 height를 동일하게 맞춰주면 된다. (가로 세로 둘 중에 짧은 길이로 통일)


protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

 
	    //Get canvas width and height
	    int w = MeasureSpec.getSize(widthMeasureSpec);
	    int h = MeasureSpec.getSize(heightMeasureSpec);
 
	    w = Math.min(w, h);
	    h = w;
 
	    setMeasuredDimension(w, h);
	}


이 방법은 ImageView 뿐만 아니라 대부분의 컨트롤에 적용 가능하다.


<com.lovejb.example.Views.SquareImageView android:layout_width="match_parent" android:layout_height="match_parent"

 />


xml layout에서는 위와 같은 식으로 사용하면 된다. 

posted by 연식킴
컴퓨터과학/Android 2014.02.04 20:52

안드로이드에서 SQLiteDatabase 객체의 insert()메서드는 long형 값을 반환한다.

그 메서드 주석에 보면 해당 값을 row ID라고 표현하는데, 실제로 어떤 값인지 자세히 설명되어 있지 않다.


해당 값은 실제 SQLite에서 정의하는 ROWID와 동일한데 간단히 말하면 INTEGER형으로 선언된 PRIMARY KEY는 내부적으로 ROWID를 앨리어스 하게 된다. 


즉, SQLite에서 데이터 INSERT시 INTEGER PRIMARY KEY AUTOINCREMENT로 선언된 필드가 있다면 자동으로 증가된 ROWID값이 반환된다.


자세한 내용은 아래 링크를 참조하시라.

http://www.sqlite.org/autoinc.html


posted by 연식킴
컴퓨터과학/Java 2014.01.22 11:13

자바에서 abstract interface와 그냥 Interface의 차이는 없다.

모든 interface는 public abstract이다.

단지 old 버전 자바에서 interface에 abstract를 사용했으나..

현재는 사용하지 않는다.

그리고 interface에 있는 모든 멤버 또한 public abstract 이다.

즉..


public abstract interface MyInterface {

public abstract void method1();

public void method2();

abstract void method3();

void method4();

}


interface MyInterface {

void method1();

void method2();

void method3();

void method4();

}


위 두 코드는 완벽하게 동일하다.

'컴퓨터과학 > Java' 카테고리의 다른 글

[JAVA] abstract interface에 관하여  (0) 2014.01.22
posted by 연식킴
컴퓨터과학/Server 2013.10.01 17:29

- IIS 6.0 사용자들은 해당이 없는 포스트입니다.


AjaxMethod를 사용하는데 로컬에서 테스트 할 때에는 잘 되다가 서버에 적용하면 객체를 참조하지 못하는 오류가 나서 한참 헤맸다.

Web.Config를 수정하라는 사람도 있고 많은데...

내 경우는 아래와 같은 방식으로 해결되었다.


IIS 관리자 - 응용프로그램 풀 - 해당사이트선택- 관리되는 파이프라인 모드 - [클래식] 선택


참고로 IIS 7에서는 응용프로그램 풀 생성시 파이프라인 모드가 [통합]으로 기본 설정된다.


통합 : 통합된 IIS 및 ASP.NET 요청 처리 파이프라인을 사용

클래식 : IIS 요청 처리 모드와 ASP.NET 요청 처리 모드를 개별적으로 사용


posted by 연식킴
컴퓨터과학/Microsoft 2013.09.24 17:37

닷넷에서 메일 전송시 첨부파일 명이 깨지는 현상이 간혹 발생한다.

이유는 대충 아래와 같은데 (더 있으면 댓글로 알려주세요...)

1. 파일명이 수신 메일서버가 지원하지 않는 문자셋으로 인코딩 된 경우

2. 파일명이 non-ASCII 문자를 포함하면서 파일명을 UTF-8로 인코딩한 길이가 42바이트 이상인 경우


1의 경우는 문자셋을 지정하여 보내면 되는데...참 난감한 것이..

수신자의 메일서버가 어떤 문자셋을 지원하는지 알수가 없는 것이다.


그러나 일반적으로 첨부파일명을 EUC-KR로 인코딩하면 한글 첨부파일을 주고받는데에는 무리가 없다.

attatchment.NameEncoding = System.Text.Encoding.GetEncoding("euc-kr"); // euc-kr 인코딩


참고로 아웃룩으로 발송시 CP949로 인코딩 되는 것을 확인하였는데..

이는 발송자의 운영체제(윈도우)에서 사용하는 기본 문자셋을 이용하여 인코딩하는 것으로 보인다. 


attatchment.NameEncoding = System.Text.Encoding.Default; // 현재 운영체제의 기본 인코딩

attatchment.NameEncoding = System.Text.Encoding.GetEncoding(949); // CP949 인코딩


2의 경우는 좀 답답하다.

.NET Framework 4.0의 버그인데 파일명이 이런식으로 보이는 경우다.

보여지는 파일명

=?ks_c_5601-1987?Q?130917_=C0=CC=BD=C3=BD= BA=C5=DB?= =?ks_c_5601-1987?Q?.xls?=


메일에서의 소스

Content-Type: application/vnd.ms-excel;

 name="=?utf-8?B?PT9rc19jXzU2MDEtMTk4Nz9RPzEzMDkxN189QzA9Q0M9QkQ9QzM9QkQ9?=\

\

 =?utf-8?B?QkE9QzU9REI/PQ0KID0/a3NfY181NjAxLTE5ODc/UT8ueGxzPz0=?="

Content-Transfer-Encoding: base64

Content-Disposition: attachment


파일명의 인코딩이 두번 된 것인데 위의 경우는 CP949로 인코딩 된 후 그게 다시 한 번 UTF-8로 인코딩 된 경우였다.


2번 타이틀에 쓴것 처럼 파일명에 non-ASCII 문자가 포함하고 있으면서 파일명을 UTF-8로 인코딩한 길이가 42바이트가 넘어가면 UTF-8로 한번 더 인코딩하는 치명적인 버그다.

http://support.microsoft.com/kb/2402064/en-us


아래 참조에 공식 패치가 있으니 다운받아 설치해야한다.

https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=31723


클라이언트에 해당 기능이 포함되어 이미 배포되었다면 추후 패치시 굉장히 곤란한 경우로...-_-

.NET의 메일 발송을 이용할 것이라면 가능하면 .NET 3.5로 개발하는 것을 추천한다.





posted by 연식킴
컴퓨터과학/Database 2013.09.18 00:15

SELECT TOP 20

 [Average CPU used] = total_worker_time / qs.execution_count

,[Total CPU used] = total_worker_time

,[Execution count] = qs.execution_count

,[Individual Query] = SUBSTRING (qt.text,qs.statement_start_offset/2, 

         (CASE WHEN qs.statement_end_offset = -1 

            THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 

          ELSE qs.statement_end_offset END - 

qs.statement_start_offset)/2)

,[Parent Query] = qt.text

,DatabaseName = DB_NAME(qt.dbid)

FROM sys.dm_exec_query_stats qs

CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt

ORDER BY [Total CPU used] DESC, [Average CPU used] DESC;

posted by 연식킴