2012년 9월 19일 수요일

안드로이드 디컴파일 방지 방법


Android How to use Proguard in the Eclipse
이글은 http://developer.android.com/guide/developing/tools/proguard.html 의 내용을 기반으로 작성되었습니다.


 프로가드는 널리 사용되고 있는 코드 난독화 툴로, 여러분의 어플리케이션을 크래커의 공격으로 부터 보다 안전하게 보호하고 동시에, 코드 사이즈를 줄여주며, 그리고 약간의 최적화도 곁들여 주는 아주 아주 훌륭한 오프소스 툴입니다. 프로가드는 GPL 라이센스를 갖는 도구로 해당 코드를 수정하거나 툴 자체를 재배포할 수는 없지만, 공개된 프로가드를 이용하여 여러분의 어플리케이션을 난독화 하는데에는 어떠 제약 조건 없이 사용하실 수 있습니다. 구글 팀은 LVL 라이브러리를 공개 하고,  라인센싱 서비스를 시작한 직 후 부터, 코드 난독화 툴 - 특히 프로가드 툴을 사용할 것을 권장했으며, 마침내 새롭게 등장한 진저브레드 버전 부터는 아예 ADT 단에서 프로가드 사용을 지원하고 있습니다. 따라서, 개발자분들은 새롭게 업데이트된 ADT 를 사용하는 한, 꼭 2.3 타겟의 어플리케이션을 만드는 경우가 아니더라도 정말로 손쉽게 프로가드 툴을 사용하실 수 있습니다. 그 방법에 관하여, 구글 개발자 가이드 문서에 나온 내용을 기반으로 간단하게 그 사용법을 정리해 보았습니다.

프로가드 사용하기

 우선 당연히 현재 사용하고 계신 ADT 를 최신 버전(8.0.1 이 후 버전)을 설치 하셔야합니다. 기존에 설치된 ADT 가 있으시면 최신 버전으로 업데이트 하시면 됩니다. ADT 를 업데이트 하는 것과, 어플리케이션 타겟 플랫폼을 결정하는 것은 별다른 상관이 없으니 호환성에 대하여 큰 걱정 하지 않고 과감하게 업데이트 하셔도 좋습니다.


 ADT 업데이트를 마친 후에, 새로운 안드로이드 프로젝트를 만드시면, 프로젝트 루트 폴더에 기존과 다른 proguard.cfg 라는 파일이 생성되는 것을 확인 하실 수 있습니다. 프로가드 환경 설정 파일이라고 생각하시면 되며, 기본적으로 안드로이드에 알맞은 설정 값(메니페스트에서 이름으로 참조하는 클래스는 난독화 하지 않는 등,...)을 갖고 있음으로, 개인 개발자 분들이라면 별다른 수정 없이 해당 설정 파일을 사용하시면 될 듯 합니다. 
그리고 마지막으로, 프로젝트 루트 폴더의 default.properties (프로젝트 설정 파일이조) 파일을 열어서 아래와 같이 프로가드를 사용하겠다고 설정 값을 추가하시면 됩니다.  
proguard.config=proguard.cfg
 네...이게 전부입니다. 이 상황에서 여러분이 릴리즈 버전으로 어플리케이션을 익스포트 하시면, 빌드과정에서 자동으로 프로가드 툴이 동작합니다. 정말 쉬어졌습니다. 만세~! 그리고, 프로가드 툴이 정상적으로 동작하고 나면, 여러분의 프로젝트 폴더에 proguard 라는 하위 폴더가 새롭게 생성되며 해당 폴더 내에, 다음의 네 가지 파일이 생성됩니다. 

  • dump.txt : 여러분의 어플리케이션에서 사용중인 클래스들의 내부 구조에 대한 대략적인 정보를 나타냅니다. 말그대로 난독화 하기 위해 소스를 분석하는 과정에서 나오는 덤프값들이겠조. 뭐. 
  • mapping.txt : 이 파일은 중요합니다. 난독화 과정에서 기존 클래스 혹은 메서드가 어떤 새로운 난독화된 이름으로 매핑되었는지 그 목록을 표시해 줍니다. 난독화 된 어플리케이션에 발생하는 로그나, 스택 트레이스 들을 분석하기 위해서 꼭 챙겨 두셔야 합니다.
  • seeds.txt : 난독화 되지 않은 클래스와 멤버들의 목록입니다.
  • usage.txt : 사용되지 않기 때문에, apk 파일에서 제거된 코드들의 목록입니다. 혹시 제거되서는 안되는 메서드나 클래스가 제거되었는지 꼭 확인해 봐야 합니다.

프로가드 설정 하기

 왠만한 경우에는 구글에서 기본적으로 설정한 proguard.cfg 가 잘 동작하겠지만, 몇몇 경우 프로가드 툴이 난독화 과정에서 잘못된 클래스나 멤버를 난독화 하거나 실재로는 사용되는 메서드를 제거하는 등의 오류를 일으킬 수 도 있습니다. 예를 들자면,

  • 실제 코드가 아니라 AndroidManifest.xml 파일 내에서만 참조되는 클래스
  • JNI 형식으로만 호출되는 메서드
  • 동적으로 참조되는 필드 값이나 메스드들 

 프로가드 툴을 적용한 후 어플리케이션을 실행시켰을 때, ClassNotFoundException 등의 예외가 발생한다면 위의 경우를 의심해 봐야 합니다. 이처럼 프로가드 툴이 건들지 말아야할 코드를 건들여서 문제가 발생하는 것을 방지하기 위해, proguard.cfg 파일을 열어, -keep 항목을 추가로 선언할 수 있습니다.
-keep public class <MyClass>
 -keep 속성과 함께 사용할 수 있는 다양한 옵션 값들이 있습니다. 이에 관한 보다 상세한 내용은 프로가드 메뉴얼 문서를 직접 참조하시면 좋을 것 같습니다. 

난독화된 어플리케이션을 디버깅 하기

 난독화된 어플리케이션의 스택 트레이스 정보는 엉망 진창이 되기 쉽상입니다. 온갖 메서드 이름과 클래스 이름이 전부 난독화되었을테니 말입니다. 디버깅이 불가능한 것은 아니지만, 결코 쉬운 일은 아닐테조. 하지만 앞서 이야기 한 것 처럼, 난독화 과정에서 생성되는 mapping.txt 파일에 원본 이름이 어떻게 변경되었는지에 관한 내용이 저장됩니다. 프로가드 툴과 함께 제공되는 retrace.bat 혹은 retrace.sh 스크립트는 바로 이 mapping.txt 파일의 내용을 기반으로 난독화된 스택 트레이스 정보를 자동으로 변환시켜 줍니다. <sdk_root>/tools/proguard/ 디렉토리에 위치하며 다음과 같이 사용하시면 됩니다.
retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
For example:
retrace.bat -verbose mapping.txt obfuscated_trace.txt
출시한 어플리케이션 디버깅 하기

 한가지 주의 점이 있습니다. 난독화 과정에서 생성되는 mapping.txt 파일은 기존 파일을 덮어 쓰게되어있습니다. 따라서, 개발자분들은 릴리즈 버전에 해당하는 mapping.txt 파일을 주의깊게 보존하셔야 합니다. 만일 그렇지 않을경우, 사용자들이 어플리케이션 사용주에 발생하는 크래쉬나 ANR 문제들에 관한 귀중한 정보를 보내온다 하더라도, 해당 내용을 해석할 수 없어서 발을 동동 구르게 될지도 모릅니다.

2012년 9월 12일 수요일

WBS에 대하여


# WBS (Work Breakdown Structures)란?
여기서는 프로젝트 관리의 측면이 아니라 각 멤버가 프로젝트에 있어서 자신의 작업목표를 관리하는 데에 WBS를 어떻게 활용할 것인가를 생각해 보기로 한다.
WBS는 한마디로 말하면 프로젝트 목적달성에 필요한 작업을 정의하기 위한 툴이라 할 수 있다.
다음의 4스텝을 거쳐서 작성한다.

1. 프로젝트의 목적을 정한다.
2. 프로젝트에서 작성할 프로덕트, 서비스, 결과 등의 성과물을 구체적으로 정한다.
3. 요소 성과물이나 중간 성과물, 전체 성과물에 공통적인 작업항목을 빠짐없이 정한다.
4. 2와 3의 항목을 분해해서 계획 및 컨트롤에 적절한 볼륨이 될 때까지 계속 분해한다.
4의 결과 분해된 최후의 항목을 워크패키지라고 부른다.

여기서 포인트는 WBS의 각 요소는 명사와 수식어로 표현한다는 것이다.
워크패키지까지 분해하고 나면 그 다음에는 워크패키지를 액티비티라고 하는 작업레벨로 분해한다. 액티비티는 [~을 수행한다]란 표현이 되도록 한다.

작성한 WBS는 요원조달, 예산작성, 스코프 설명 등 프로젝트의 여러 방면에 사용되며, 멤버의 역할과 책임은 워크패키지 또는 액티비티 단위로 주어진다.
이처럼 WBS는 프로젝트의 전체상을 나타내는 지도의 역할을 한다.

# WBS (Work Breakdown Structures)의 작성 방법
프로젝트 전체의 WBS는 프로젝트 관리팀에서 작성하며 각 멤버가 직접 관여하는 일은 많지 않다.
그리고, 작성된 WBS에 의해 멤버에게 역할과 책임이 주어지면 작업을 실행하게 된다. 즉, 개개의 멤버는 워크패키지 또는 액티비티로 구체화 되어진 후에 비로서 WBS와 관계를 갖게 된다. 그리하여 워크패키지나 액티비티의 수행에 책임을 지니게 되는 것이다.

통상적으로 WBS에서는 다음과 같은 요소 성과물을 고려해서 분해/구체화한다.
1. 프로덕트를 분해
2. 서비스를 분해
3. 프로젝트 결과를 분해
4. 프로젝트 전체의 횡단적 요소
5. 프로젝트 매니지먼트 요소

그러나, 멤버에게 있어서는 분해/구체화의 기준이나 그 레벨보다는 구체화된 WBS항목(워크패키지)의 구체적인 내용이 더욱 중요하다. 이 내용을 WBS사전이라 부른다.
즉, 프로젝트 전체의 WBS를 작성하는 것 그 자체보다는 담당부분의 WBS와 전체와의 정합성을 맞추어서 작성하는 것이 멤버력향상을 위해서는 보다 더 중요하다는 것이다.

# WBS 사전에 쓸 항목
WBS사전에 어떤 항목이 필요한지는 프로젝트의 종류나 성격에 따라 다르긴 하나 최소한 다음의 항목은 필요하다.
1. WBS 항목 번호
2. WBS 항목 명칭
3. WBS 항목의 내용
4. 필요한 액티비티 (작업내용)
5. 작업에 필요한 성과물
6. 견적 공수
7. 개시 예정일과 완료 예정일
8. 담당자명
9. 예상되는 리스크

그리고, 프로젝트에 있어서 멤버가 책임을 갖는다는 것은 담당하는 WBS 항목에 대하여 요구되어지는 성과를 주어진 시간내에 달성한다는 것이다. 즉, 담당 작업에 대하여 커밋한다는 것을 의미한다.

이를 위해서도 담당할 작업의 WBS 부분을 작성하는 것은 커밋이 보다 원할하게 이루어질 수 있으며, 이것은 담당부분의 WBS 작성에 멤버가 참여함으로써 얻을 수 있는 잇점이라 할 수 있을 것이다.


성과를 달성을 위한 각 멤버의 셀프 매니지먼트에 대하여 기술한다.

프로젝트에 있어 멤버가 성과를 달성한다고 하는 것은 담당 작업에 요구되는 성과를 주어진 시간 안에 확실히 달성하는 것이다. 이를 위해서는 다음의 사항을 고려하여 작업을 수행해야 한다.

1. 시간의 사용법
2. 작업의 효율화
3. 현상을 정확하게 보고하기
4. 확실한 달성을 위하여 자신의 작업에 숨어 있는 리스크에 대한 관리

WBS 사전에는 작성할 성과물, 필요한 작업, 견적공수, 개시 예정일과 완료 예정일 이 기재되어 있다. 그것을 바탕으로 위 1~3을 고려하여 자신이 수행해야 할 작업을 breakdown한다.

그것은 먼저 작업의 우선순위를 고려한다는 것이며, 하지 않아도 되는 작업은 하지 않는 것이다.
일반적으로 중요한 작업이 무엇인가를 생각하여 우선순위를 매기는 작업에 착수하도록 한다.
그 "중요한 작업"을 판단하는 과정에서 '여지껏 계속 해왔으니까'라든가 '나중의 작업이 편하니까'라는 등의 습관처럼 행하고 있는 일이 포함되는 경우가 꽤 있으리라 본다.

일단 자기가 올려야 할 성과를 올리기 위해서 이 작업이 과연 필요한가라는 자문을 해봅시다.

여지껏 중요하다고 생각했던 작업이라도 '성과달성에 필요한가'라는 자문의 결과 필요없다고 판단되면 과감히 버릴 필요가 있다.

이제부터는 WBS사전의 내용 중에서 자기가 해야 할 작업을 breakdown함에 있어서 습관적으로 하고 있던 작업에 대하여 정말로 필요한 작업인가를 자문해보기로 하자.

또한, 효율적인 작업이 되기 위해서는 시간관리를 통하여 집중하는 것이 중요하다. 여기서 시간관리라 함은 '집중할 수 있는 시간'을 말하는 것으로 1시간~2시간이 적당하다. 이 시간동안에 할 작업을 정하여, 이 정해진 시간에 작업을 완료하였나 여부데 "작은 성공, 달성감을 얻는 것"에서부터 작업의 효율화를 꾀해보도록 하자.

그리하여 일정시간에 수행한 작업의 완료여부를 모아서 현황보고를 한다.


또한, WBS항목 중의 「예상가능한 리스크」에 대해서는 리스크를 항상 고려하도록 하자.
그리하여 리스크에 민감해지고 리스크발생을 가능한한 신속히 파악하여, 팀장 및 프로젝트 매니저에게 보고 / 의논 / 대처함으로써 위의 3, 4번을 실현할 수 있다.

보고를 함에 있어서 각 멤버는 반드시 리스크에 대한 대처방법을 생각해두도록 한다.
또한, 리스크 계획 및 리스크 대처방법이 정해져 있다면 그 대처방법에 의한 리스크의 해소도나 영향도를 추정해두자.
각각의 멤버는 리스크 발생 현장에 가장 가까이 위치하므로 리스크에 관한 정보를 가장 많이 확보할 수 있는 것이다.
그 정보화 현장 담당자로서의 판단을 보고하고 의논하므로써 보다 신속하고 질 높은 리스크 대처가 가능하다.

이를 위해서는 담당작업에 있어 예상되는 리스크는 무엇인가? 그것을 신속히 파악하기 위해서는 무엇에 주의해야 하는가 등을 이해해 둘 필요가 있다.

위의 내용들을 각 멤버가 고려함으로써 보다 나은 작업성과를 달성할 수 있을 것이다.

2012년 9월 3일 월요일

앱 Proguard 적용하기


안드로이드 apk파일을 추출하여 디컴파일을 하게되면 거의 모든 소스를 볼수가 있었다.(모 저도 앱 참조할때 디컴파일 사용해서 해보기 했지만;;;)

이것을 방지하기 위한 proguard를 적용하기로 했다. 프로가드를 적용하여 소스를 보호하자.

구글을 android proguard를 검색하게 되면 많이 나오지만 기본적인 프로가드 적용방법이라... 정말로 필요한 부분만 정리하는 것이 좋을같아 정리를 해야겠다고 생각을 했다~

안드로이드 프로젝트를 생성하게 되면 proguard.cfgproject.properties 파일이 있다.


인디고에서는  project.properties 갈릴레오버전에서는 default.properties로 생성될것이다.



빨간색 밑줄 쓴 부분만 project.properties파일에 입력한다음 apk파일을 export하면 일단 성공이다.
proguard.config=proguard.cfg

이과정은 구글 검색에서 많이 나오는 부분이라 .. 두가지의 예외가 생겼었는데 하나는 proguard jar파일을 access못한다는 에러였고 또하나는 waring jar파일의 패키지명이 나온예러였다.(정확히는 생각이...)

첫번째 proguard의 jar파일을 access어쩌구저쩌구 에러가 나오게 된다면
안드로이드의 sdk가 설치된 폴더로 가서 나같은 경우는(C:\Android\sdk\tools\proguard\bin)
tools-> proguard ->bin 폴더에 있는 proguard파일을 메모장이나 edit plus와 같은 편집기를 열어

맨밑에 설정된 부분을 
call %java_exe% -jar "%PROGUARD_HOME%"\lib\proguard.jar %1 %2 %3 %4 %5 %6 %7 %8 %9
위와 같은 식으로 설정해 준다. jar %1사이의 띄어쓰기는 매우중요함..

이렇게 해도 안된다고하면 proguardgui, retrace파일까지 열어서 위의 문장이 제대로 입력되어있는지 확인하여야한다.
(저같은 경우는 3가지 파일중 하나가 jar와 %1부분이 띄어쓰기가 안되어 있어 에러가 계속;;;)

 
 (에러가 계속난다면 꼭3개의 파일을 열어 확인을..)

두번째는 Waring jar어쩌구저쩌구가 나는 에러다.
이것은 외부 Jar파일을 사용하지 않을때는 거의 안날것이다. 나같은 경우 viewpager와 navermap을 사용하기 위해 nmaps.jar, android-support-v4.jar 파일을 사용했는데 아무런 수정없이 apk를 export하게 된다면 반드시 에러가 날것이다.(아마도 내 생각에는 이미 프로가드가 적용되서 그런것 같은데 확실히는 몰르겠다)

위 두jar파일에 대해서는 proguard를 적용하지 않는다는 문구가 필요했다.


위의 그림 처럼 저렇게 nmaps.jar, android-support-v4.jar에 대한 패키지명을 지정하면 두 jar파일은 proguard를 적용시키지 않는다.
그럼 성공적으로 apk파일에 proguard가 적용될것이다.

-dontwarn android.support.v4.**
-dontwarn com.nhn.android.maps.**

p.s 만약 다른 jar를 사용하고 있다면 에러문구에 나오는 패키지부분을  -dontwarn 다음에 써주면될것 같다.
ex) warning com.hyo.itte.class, warning com.hyo.zzz.class 가 나왔다면 -dontwarn   com.hyo.**이렇게 입력하면 될것이다(공통부분만 찾아서 나머지는 **)

proguad가 적용이 잘되었다면



위와 같이 proguard폴더에 4개의 파일생성되었다면 성공한것이다. 처음 apk를 생성할때는 바로 확인이 안될수도 있다.(너무 걱정안해도 된다. 에러만 안나온다면 )
이클립스를 종료해서 다시 들어오게 된다면 생성되어 있을것이고 아님 프로젝트 폴더가 저장된 부분에 가면 확인할 수 있다. (만약 생성이 안되어 있다면 적용실패;;)

정말로 확인을 위해서는 프로가드를 적용하지 않은 apk파일과 적용한 후의 apk파일을 똑같이 디컴파일하게되면 확실히 알 수가 있다. 디컴파일 부분은 다음시간에 정리할 것이다.

출처 : http://hyojoong2-dev.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9CProguard-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0

2012년 7월 5일 목요일

[Android] 동적으로 다음페이지를 로딩하는 ListView 구현

아이폰의 수많은 UITableView를 활용하는 어플리케이션을 보면 참 퀄리티 높게 잘 만든것이 자동으로 리스트의 가장 아래로 내려가면 알아서 다음페이지를 로딩하는 기능이 아닐까 싶습니다. 안드로이드에서도 요즘은 많은 어플리케이션이 해당 기능을 구현하고 있습니다. 안드로이드에서는 리스트뷰와 데이터간에 Adapter라는 디자인패턴을 활용하고 있어 아이폰의 그것과는 같은 기능이라도 구현하는 방식이 다릅니다.

안드로이드에서는 좀 더 적극적으로 Adapter를 활용하여 이 기능을 구현해야 합니다. 어찌보면 조잡하고 어찌보면 더 쉽게 구현할 수 있습니다. 길게 이야기할것 없이 예제 소스를 보여드리겠습니다.
public class DynamicListViewActivity extends Activity implements OnScrollListener
{
  private static final String LOG = "DynamicListViewActivity";
  private CustomAdapter mAdapter;
  private ListView mListView;
  private LayoutInflater mInflater;
  private ArrayList<String> mRowList;
  private boolean mLockListView;
  
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // 멤버 변수 초기화
        mRowList = new ArrayList<String>();
        mLockListView = true;
        
        // 어댑터와 리스트뷰 초기화
        mAdapter = new CustomAdapter(this, R.layout.row, mRowList);
        mListView = (ListView) findViewById(R.id.listView);
        
        // 푸터를 등록합니다. setAdapter 이전에 해야 합니다. 
        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        mListView.addFooterView(mInflater.inflate(R.layout.footer, null));
        
        // 스크롤 리스너를 등록합니다. onScroll에 추가구현을 해줍니다.
        mListView.setOnScrollListener(this);
        mListView.setAdapter(mAdapter);
        
        // 데미데이터를 추가하기 위해 임의로 만든 메서드 호출
        addItems(50);
  }

  @Override
  public void onScroll(AbsListView view, int firstVisibleItem,
    int visibleItemCount, int totalItemCount)
  {
    // 현재 가장 처음에 보이는 셀번호와 보여지는 셀번호를 더한값이
    // 전체의 숫자와 동일해지면 가장 아래로 스크롤 되었다고 가정합니다.
    int count = totalItemCount - visibleItemCount;

    if(firstVisibleItem >= count && totalItemCount != 0
      && mLockListView == false)
    {
      Log.i(LOG, "Loading next items");
      addItems(50);
    }  
  }

  @Override
  public void onScrollStateChanged(AbsListView view, int scrollState)
  {
  }
  
  /**
   * 임의의 방법으로 더미 아이템을 추가합니다.
   * 
   * @param size
   */
  private void addItems(final int size)
  {
    // 아이템을 추가하는 동안 중복 요청을 방지하기 위해 락을 걸어둡니다.
    mLockListView = true;
    
    Runnable run = new Runnable()
    {
      @Override
      public void run()
      {
        for(int i = 0 ; i < size ; i++)
        {
          mRowList.add("Item " + i);
        }
        
        // 모든 데이터를 로드하여 적용하였다면 어댑터에 알리고
        // 리스트뷰의 락을 해제합니다.
        mAdapter.notifyDataSetChanged();
        mLockListView = false;
      }
    };
    
    // 속도의 딜레이를 구현하기 위한 꼼수
    Handler handler = new Handler();
    handler.postDelayed(run, 5000);
  }
}


여기서 주목할 부분은 onScroll 메서드 입니다. 스크롤이 일어날때마다 해당 메서드가 호출이 되며 위의 소스에서는 가장 마지막셀이 디스플레이 되었는지를 검사하게 됩니다. 마지막 셀이 나왔다면 현재 리스트가 Lock상태인지를 체크 합니다. 여기서 쓰이는 멤버 변수가 mLockListView 입니다.

해당 변수를 사용하여 리스트에 데이터가 변화하는 순간에는 스크롤 이벤트를 막아 이벤트의 중복 요청을 막게 됩니다. 위에서 Inflater를 활용하여 FooterView를 붙이는 과정이 있는데요 이것이 사용자로 하여금 페이지 로딩중임을 알리게 되는 중요한 요소입니다. 소스를 올려둘테니 받으셔서 테스트해보시기 바랍니다.

사용자 삽입 이미지


출처 : http://theeye.pe.kr/entry/Android-%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%8B%A4%EC%9D%8C%ED%8E%98%EC%9D%B4%EC%A7%80%EB%A5%BC-%EB%A1%9C%EB%94%A9%ED%95%98%EB%8A%94-ListView-%EA%B5%AC%ED%98%84

2012년 7월 4일 수요일

Fragment 프래그먼트 개념


프래그먼트(Fragment)의 등장 배경

안드로이드 3.0(허니컴)이 공개되면서 태블릿에 적합한 여러 UI들이 공개되었는데, 그 중에서 대표적인 것이 바로 프래그먼트(Fragment) 입니다.

그럼, 과연 왜 프래그먼트라는 것이 추가되었을까요? 간단히 말하자면, 프래그먼트는 태블릿과 같은 큰 화면을 가지는 단말에서 애플리케이션이 화면을 더 효율적으로 활용할 수 있도록 도와줍니다. 기존에는 애플리케이션 화면을 구성하는 큰 틀이 액티비티(Activity) 하나였고, 이 안을 여러 뷰로 구성하여 정보를 표시하고, 상호작용을 수행했습니다.

그런데, 뷰만을 사용해서 다양한 내용을 보여주기는 매우 어려웠습니다. 특히나 전체적인 UI 틀은 고정되어 있으면서 특정 부분만 변화하며 다른 내용을 표시하도록 하려면 매우 복잡한 구성이 필요했죠. 또한, 서로 다른 역할을 하는 코드들이 같은 곳에 있게 되어서 가독성도 떨어지고 유지보수에도 악영향을 미쳤습니다. 

이 때문에 대부분 애플리케이션에서는 뷰 처리의 어려움도 피하고, 코드도 분리하게 위해 액티비티 전환을 사용했습니다. 아래의 GMail 앱을 통해 전형적인 액티비티 구성 방식을 볼 수 있습니다.


G메일 앱의 액티비티 구성


이 방식은 상당히 깔끔했습니다. 애플리케이션의 흐름 구성도 자연스럽게 잡히게 되었구요. 하지만, 화면이 큰 태블릿 단말들이 소개되면서 이 방식의 구성이 화면을 쓸데없이 많이 차지한다는 것을 느끼게 되었습니다. 호환성 문제는 없었지만, 큰 화면을 효율적으로 활용하지 못하는 것이죠.

트위터 공식앱을 실행한 모습. 태블릿에 최적화되지 않아 공간을 낭비하고 있습니다.


즉, 이제는 화면을 어떻게 하면 효율적으로 사용할 수 있는가? 를 고민할 차례가 되었습니다. 그럼, 화면을 효율적으로 활용한다는 것은 과연 어떤 것을 의미하는 것일까요? 여러 가지 방법이 있겠지만, 가장 대표적인 것은 한 화면에 가급적 다양한 정보를 표시하는 것입니다. 다음 안드로이드 3.0 버전의 GMail 앱을 보도록 하죠.


안드로이드 3.0의 G메일. 넓은 화면을 효율적으로 사용하고 있습니다.

기존 버전에서는 메일 리스트와 내용이 각각 전체 화면으로 표시되었던 것에 반해 안드로이드 3.0용에서는 메일 리스트와 내용이 동시에 표시되는 것을 확인할 수 있습니다. 넓은 화면을 효과적으로 활용하는 것이죠. 


넓은 화면을 효율적으로 사용하기

그렇다면, 넓은 화면을 효율적으로 사용하려면 단순히 한 화면에 여러 요소들을 구겨넣으면 되는 것일까요? 그렇지 않습니다. 한 화면에 여러 요소가 표시됨과 동시에 각 요소들을 조작하는 코드들은 각각 분리되어 있어야 합니다. 어떻게 보면 기존에 액티비티로 구현되던 요소들이 한 화면에 표시되는 것이죠. 

프래그먼트(Fragment)는 이러한 요구사항들을 잘 충족합니다. 액티비티처럼 관련된 코드들을 한곳에 묶을 수도 있고, 일반 뷰처럼 애플리케이션 레이아웃에 프래그먼트를 자유롭게 배치할 수도 있습니다. 다음 화면을 통해 프래그먼트의 배치를 확인할 수 있습니다.

G메일 앱의 프래그먼트 배치 모습


이것으로 프래그먼트의 등장 배경과 간략한 특징에 대해 알아보았습니다. 다음 글에서는 프래그먼트의 더 자세한 특징 및 사용 방법에 대해 알아보도록 하겠습니다.

출처 : http://androidhuman.tistory.com/entry/%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8Fragment-%EC%A0%95%EB%B3%B5-1-%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EB%84%88%EB%8A%94-%EB%88%84%EA%B5%AC%EB%83%90


프래그먼트를 가장 간단하게 표현하자면 '뷰(View)처럼 사용할 수 있는 액티비티(Activity)'라 할 수 있습니다. 즉, 액티비티와 뷰의 특징을 모두 가지고 있습니다. 프래그먼트는 뷰에게 레이아웃 내에 자유롭게 배치될 수 있는 특징을 물려받았는데, 그렇다면 액티비티에서는 어떤 특징을 물려받았을까요? 바로 '생애주기'를 갖는 특징입니다.

프래그먼트의 생애주기

 프래그먼트는 액티비티와 같이 프래그먼트의 상태가 계속해서 변하며, 상태가 변할 때마다 그에 해당하는 생애주기 메서드(콜백 메서드)가 호출됩니다. 프래그먼트의 생애주기 메서드 및 각 메서드의 호출 순서는 다음과 같습니다.

프래그먼트의 생애주기 메서드

각 생애주기 메서드에 대해 더 자세히 알아보도록 하겠습니다. 액티비티의 생애주기 메서드와 매우 유사한 형태를 띄고 있으며, 뷰 생성과 관련된 몇몇 메서드가 더 추가되어 있습니다.

onAttach(Activity)
프래그먼트가 액티비티 레이아웃에 포함되는 순간 호출됩니다. 액티비티 레이아웃에 프래그먼트를 정적으로 배치했다면 액티비티가 시작될 때 같이 호출되며, 동적으로 레이아웃에 추가할 땐 프래그먼트를 레이아웃에 추가하는 순간 호출됩니다.


onCreate(Bundle)
액티비티의 onCreate() 콜백 메서드와 유사하게 프래그먼트가 최초로 생성될 때 호출됩니다.


onCreateView(LayoutInflater, ViewGroup, Bundle)
프래그먼트의 UI를 구성하는 뷰(View)를 반환합니다. UI를 가지지 않는 프래그먼트일 경우 null을 반환할 수도 있습니다.


onStart()
프래그먼트가 화면에 표시될 때 호출됩니다. 하지만, 아직 사용자와 상호작용은 할 수 없는 상태입니다.


onResume()
프래그먼트가 사용자와 상호작용을 할 수 있게 되었을 때 호출됩니다. 즉, 프래그먼트가 완전히 화면에 표시되어 제 역할을 수행할 수 있게 된 상태입니다.


onPause()
액티비티의 onPause()와 유사하게 프래그먼트가 사용자와 상호작용을 할 수 없게 될 때 호출됩니다. 프래그먼트가 아직 화면에 표시되고 있는 상태이나, 다른 요소에 의해 프래그먼트가 가려져 상호작용을 하지 못하는 상태입니다.


onStop()
프래그먼트가 화면에서 보이지 않게 될 때 호출됩니다. 액티비티가 화면에서 보이지 않게 될 때 onStop() 메서드가 호출되는 것과 유사합니다.


onDestroyView()
프래그먼트가 화면에서 사라진 후, 뷰의 현재 상태가 저장된 후 호출됩니다. 여기에서 저장된 뷰의 상태는 액티비티와 유사하게 Bundle 형태로 저장되며, 저장된 뷰의 상태는 onCreate() 및 onCreateView()에서 다시 불러들일 수 있습니다.


onDestroy()
프래그먼트가 더 이상 사용되지 않을 때 호출됩니다. 


onDetach()
프래그먼트가 액티비티 레이아웃에서 제거될 때 호출됩니다. 


위와 같이 프래그먼트의 생애주기는 액티비티와 매우 유사합니다. 때문에 기존에 액티비티로 작성되어 있던 코드를 쉽게 프래그먼트로 옮겨올 수 있습니다. 

프래그먼트에는 위의 생애주기 메서드 외에도 프래그먼트를 포함하고 있는 액티비티의 생성이 완료되었을 때 호출되는 콜백 메서드도 포함하고 있으며, 그 메서드는 다음과 같습니다.

onActivityCreated(Bundle)
프래그먼트를 포함하고 있는 액티비티의 생성이 완료되었을 때, 즉 액티비티의 onCreate() 메서드가 끝났을 때 호출됩니다.


프래그먼트를 레이아웃에 추가하기 (XML 사용)

프래그먼트의 특징에 대해 간단히 알아보았으니, 우선 프래그먼트를 레이아웃에 추가하는 방법에 대해 알아보도록 하겠습니다. 프래그먼트를 레이아웃에 추가하는 방법은 여러 가지가 있지만, 그 중에서도 가장 간단한 방법인 XML 레이아웃을 사용하여 추가하는 방법을 알아보겠습니다.

[어플리케이션 정보]

액티비티
  • Main (Main.java)

레이아웃
  • main.xml (Main)
  • fragment_one.xml (FragmentOne 프래그먼트)

API Level
  • Android 3.0 (API Level 11)


우선, 프래그먼트에 표시할 레이아웃 파일을 작성합니다. 다음과 같이 fragment_one.xml 레이아웃 파일을 추가한 후,TextView 하나를 추가합니다.

[fragment_one.xml]
01.<?xml version="1.0" encoding="utf-8"?>
02.<LinearLayout
04.android:layout_width="match_parent"
05.android:layout_height="match_parent">
06. 
07.<TextView android:layout_width="wrap_content"
08.android:layout_height="wrap_content"
09.android:text="Fragment One" />
10. 
11.</LinearLayout>

액티비티 코드에 프래그먼트 클래스를 추가한 후, 프래그먼트에 표시할 뷰를 지정해주기 위해 onCreateView() 다음과 같이 메서드를 오버라이드합니다. 프래그먼트에서 표시할 뷰를 반환하기 위해 onCreateVIew()의 인자 중 하나인 LayoutInflater를 사용합니다.


[Main.java]
01.public class Main extends Activity {
02. 
03.@Override
04.public void onCreate(Bundle savedInstanceState) {
05.super.onCreate(savedInstanceState);
06.setContentView(R.layout.main);
07.}
08. 
09.public static class FragmentOne extends Fragment{
10. 
11.@Override
12.public View onCreateView(LayoutInflater inflater, ViewGroup container,
13.Bundle savedInstanceState) {
14.return inflater.inflate(R.layout.fragment_one, null);
15.}
16. 
17.}
18. 
19.}

다음, 액티비티의 레이아웃을 작성합니다. 프래그먼트를 XML 레이아웃 파일에서 선언할 때는 프래그먼트의 클래스(class)와 프래그먼트를 구분할 수 있는 id 혹은 tag를 필히 지정해야 합니다. 여기에서는 프래그먼트를 구분하기 위해 id를 지정해 주었습니다.

이 예제에서 프래그먼트 클래스가 Main 클래스의 내부 클래스로 선언되어 있기 때문에 Main$FragmentOne과 같이 클래스 이름을 지정해 주었습니다.


[main.xml]
01.<?xml version="1.0" encoding="utf-8"?>
02.<LinearLayout
04.android:orientation="vertical"
05.android:layout_width="fill_parent"
06.android:layout_height="fill_parent"
07.>
08.<fragment
09.android:id="@+id/fragment_one"
10.class="com.androidhuman.example.SimpleFragment.Main$FragmentOne"
11.android:layout_width="match_parent"
12.android:layout_height="match_parent" />
13.</LinearLayout>


이것으로 모든 구현이 끝났습니다. 생각보다 간단하지요? 예제를 실행하면 다음과 같이 프래그먼트가 표시되는 것을 확인할 수 있습니다.


프래그먼트라는 것을 처음 접할 때는 아직 익숙하지 않아 어려워 보였을지도 모릅니다. 간단한 예제를 통해 알아본 것과 같이 접해보면 그리 어렵지 않습니다.

다음 포스트에서는 프래그먼트를 사용하는 가장 큰 장점 중 하나라 할 수 있는 프래그먼트 전환에 대해 다루어보도록 하겠습니다. :)

출처 :  http://androidhuman.tistory.com/entry/%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8Fragment-%EC%A0%95%EB%B3%B5-2-%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8-%EC%9E%90%EC%84%B8%ED%9E%88-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

안드로이드 3.0부터 추가된 요소인 Fragment.....

이는 독립된 로직을 가지고 있는 일련의 뷰를 포함하는 액티비티를 작성할 때
매우 유용합니다. 덕분에 제가 최근 만드는 앱에서는 프래그먼트를 절찬리(?) 사용하고 있지요.

하지만, 아직 사용법이 익숙하지 않아서인지 몰라도,
사소하지만 큰 실수를 자주 하곤 합니다.

가장 많이 접하는 경우가.....

"프래그먼트를 추가했는데, 왜 프래그먼트가 보이지 않는거지??" 

이 상황일 것으로 추측됩니다. ㅎㅎ
과연, 이유가 무엇일까요????

네... 사실 별건 없습니다.
바로 commit() 메서드를 호출하지 않았기에....ㅠㅠ

프래그먼트를 화면에 추가하거나 표시하려면 FragmentTransaction 클래스의 메서드를 사용하는데,
여기에서 추가/교체/제거 등의 작업을 한 후 반드시 commit() 메서드를 호출해야 변경 사항이 적용됩니다.
그렇지 않아면.. 백날 건드려봤자 변하는 것은 없지요...

사소한 것이지만 자주 잊기 쉬운 것이라 한번 정리해 보았습니다. ^^

ps. 한가지 예외가 있따면, 액션바의 OnTabListener 의 인자로 받는 FragmentTransaction을 사용할 때는
commit() 메서드를 호출하면 안됩니다. 자동으로 commit() 메서드를 호출해주기 때문이지요. 



출처 : http://androidhuman.tistory.com/entry/%ED%94%84%EB%9E%98%EA%B7%B8%EB%A8%BC%ED%8A%B8%EA%B0%80-%ED%91%9C%EC%8B%9C%EB%90%98%EC%A7%80-%EC%95%8A%EC%95%84%EC%9A%94