안드로이드의 메모리 누수 패턴


Memory Leak Patterns in Android를 번역한 글입니다.

메모리 누수란?

모든 앱은 작업을 수행하는데 필요한 리소스로 메모리가 필요합니다. Android의 각 앱에 충분한 메모리가 있는지 확인하려면 Android 시스템에서 메모리 할당을 효율적으로 관리해야 합니다. Android 런타임은 메모리가 부족한 경우 가비지 수집(GC)을 트리거합니다. GC의 목적은 더 이상 유용하지 않은 객체를 정리하여 메모리를 회수하는 것입니다. 다음 3단계로 진행됩니다.

  1. GC 루트에서 메모리에 있는 모든 객체 참조를 나열하여 GC 루트의 참조가 있는 활성 객체를 표시합니다.
  2. 나열되지 않아 표시된 모든 객체는 메모리에서 지워집니다.
  3. 살아있는 객체를 다시정렬합니다.
GC 루트에서 나열되는 활성화된 객체 표시

간단히 말해서, 사용자에게 서비스를 제공하는 모든 것을 메모리에 기록해야 하며, 리소스를 확보하기 위해 메모리에서 모든 것을 지워야 합니다.
그러나 사용되지 않는 객체가 사용되는 객체에서 어떻게든 참조되는 좋지 않은 코드로 인해 GC는 사용되지 않은 객체를 유용한 객체로 표시하고 객체를 제거할 수 없게 됩니다. 이를 메모리 누수라고합니다.

메모리 누수

 

왜 메모리 누수는 좋지 않은가?

어떤 객체도 오랫동안 메모리에 기록되어야 합니다. 사용자들에게 실질적인 가치를 제공하기 위해 사용될 수 있는 소중한 자원을 차지합니다. 안드로이드의 경우 다음과 같은 문제가 발생합니다.

1] 메모리 누수가 발생하면 사용 가능한 메모리가 부족하게 됩니다. 결과적으로 안드로이드 시스템은 빈번하게 GC이벤트를 호출합니다. GC이벤트는 모든 이벤트를 멈추게 합니다. GC가 발생하면 UI렌더링과 이벤트 처리가 중단됩니다. 안드로이드는 화면을 16ms로 그립니다. GC가 오래 걸리면 안드로이드는 프레임을 잃어버리기 시작합니다. 일반적으로, 100~200ms이상일 때 사용자가 앱이 느리다는 것을 인지하게됩니다.

안드로이드 화면 그리기
빈번한 GC로 인한 프레임 손실

 

안드로이드에서 애플리케이션 응답은 Activity 매니저와 Window 매니저 시스템 서비스로 모니터링됩니다. 안드로이드는 다음 조건중 하나를 감시하면 특정 응용 프래그램에 대한 ANR 다이얼로그를 표시합니다.

  • 5초 내에 입력 이벤트(키 누름 또는 화면 터치 이벤트)에 대한 응답이 없음 경우.
  • BroadcastReceiver가 10초 내에 실행을 완료하지 않을 경우.
ANR

어떤 사용자도 앱이 응답지연 다이얼로그를 보고 싶어 하지 않는 것을 확신합니다.

2] 앱에 메모리 누수 있는 경우, 객체는 메모리에서 반환될 수 없습니다. 결과적으로 안드로이드 시스템은 더 많은 메모리를 요청합니다. 그러나 한계가 있습니다. 결국 시스템은 앱에 더 많은 메모리를 할당하는 것을 거부합니다. 이렇게 되면 앱은 메모리 부족으로 인해 강제 종료됩니다. 물론 강제 종료를 아무도 좋아하지 않습니다. 사용자는 당장 앱을 제거하거나 앱 리뷰를 나쁘게 줄 것입니다.

3] 메모리 누수 문제는 QA 테스트로 찾기 어렵습니다. 재현하기도 어렵습니다. 그리고 안드로이드 시스템이 메모리 할당을 거부할 때 언제 어디서나 발생할 수 있기 때문에 크래쉬 리포트로 추론하기가 어렵습니다.

 

메모리 누수 확인방법?

메모리 누수를 찾기 위해 GC가 어떻게 작동하는지 잘 이해해야 합니다. 코드 작성과 리뷰를 부지런히 노력해야 합니다. 그러나 안드로이드에는 일부 코드가 의심스러울 때 누수를 예측할 수 있는 유용한 도구가 있습니다.

1] Square에서 만든 Leak Canary라는 메모리 누수를 감지하는데 유용한 도구가 있습니다. 앱의 액티비티들에 대해 약한 참조를 만듭니다. (다른 객체에 감시 기능을 추가하여 커스컴 할 수도 있습니다.) 그런 다음 GC후에 참조가 지워졌는지 확인합니다. 그렇지 않으면. hprof파일로 힙을 덤프 하고 분석하여 누수가 발생했는지 확인합니다. 있는 경우 알림이 표시되고 별도의 앱을 통해 누수가 발생된 위치를 트리 형태로 표시됩니다.

개발자/테스트 빌드에만 Leak Canary를 설치하는 것이 좋습니다. 사용자 빌드를 만들기 전에 개발자와 QA가 미리 메모리 누수를 찾기 위해서입니다.

Leak Canary

2] 안드로이드 스튜디오에는 메모리 누수를 감지하기 위한 좋은 툴이 있습니다. 앱의 일부 Activity가 누수되는 것이 의심된다면 이를 수행하면 됩니다.

1단계: 컴퓨터에 기기 또는 에뮬레이터에서 디버그 모드로 실행합니다.
2단계: 의심스러운 Activity로 이동하고 이전으로 돌아간 뒤 다시 실행합니다.
3단계: 안드로이드 모니터 창의 메모리 섹션에서 GC 시작(Initiate GC) 버튼을 누릅니다. 그 후 Java 힙 덤프(Dump Java Heap) 버튼을 누릅니다.

4단계: Java 덤프 버튼을 누르면 안드로이드 스튜디오에서 덤프 된. hprof 파일을 엽니다. hprof파일 뷰어에는 메모리 누수를 확인할 수 있는 몇 가지 방법이 있습니다. 오른쪽 상단에 있는 Analyzer Tasks 도구를 사용하여 누수되는 Activity를 자동으로 탐지할 수 있습니다. 또는 왼쪽 상단 Class List View를 Package Tree View로 변경하여 Destory 해야 하는 Activity를 찾을 수 있습니다. Activity 객체의 총개수를 확인하세요. 인스턴스가 하나 이상 있으면 누수가 있음을 의미합니다.

5단계: 누수되는 Activity를 찾았다면 하단의 참조 트리(reference tree) 창에서 Activity를 참조하고 있는 객체를 찾으세요.

더 많은 정보는 ‘HPROF Viewer and Analyzer‘에서 확인하실 수 있습니다.

 

일반적인 누수의 패턴은?

안드로이드에서 메모리 누수가 발생하는 이유는 여러 가지가 있습니다. 요약하면 3가지 카테고리로 나눌 수 있습니다.

  1. 정적 참조에 대한 Activity 누수
  2. 작업 스레드에 대한 Activity 누수
  3. 스레드 자체 누수

Github의 SinsOfMemoryLeaks에서 다양한 방식으로 메모리 누수를 시키는 간단한 앱을 만들었습니다.

LEAK 브랜치에서는 메모리 누수가 되는 코드를 볼 수 있습니다. 앞에서 언급한 안드로이드 스튜디오에서 모니터링 툴을 통해 누수를 추적할 수도 있습니다. FIXED 브랜치에서는 누수가 어떻게 수정되었는지 확인할 수 있습니다. 확신이 들지 않는다면 앞서 언급한 도구를 사용하여 실제로 수정되었는지 확인할 수 있습니다. 두 가지 브랜치는 서로 다른 앱 ID이기 때문에 동일한 기기에 설치하여 나란히 사용할 수 있습니다.

다양한 원인을 3가지 카테고리로 나누었는데 하나씩 알아보겠습니다.

정적 참조에 대한 Activity 누수

정적 참조는 앱이 메모리에 있는 한 계속 유지됩니다. Activity는 일반적으로 여러 번 파괴되고 다시 생성되는 생명주기를 가지고 있습니다. 정적 참조에서 Activity를 참조하는 경우 Activity는 생명주기에 의해 Destory 된 후에 GC 되지 않습니다. Activity는 콘텐츠에 따라 수 킬로 바이트에서 많은 경우 메가바이트까지 다양합니다. 복잡한 뷰 계층 구조나 고해상도의 이미지의 경우 많은 양의 메모리가 누수될 수 있습니다.

이 카테고리에서는 다음과 같은 항목이 있습니다.

정적 뷰에 Activity를 참조

정적 변수에 Activity를 참조

싱글톤 객체에 Activity를 참조

Activity의 내부 클래스를 정적으로 참조

작업 스레드에 대한 Activity 누수

Activity는 작업 스레드보다 오래 지속될 수 있습니다. Activity보다 더 오래 작업하는 스레드에서 Activity를 참조하면 누수가 발생합니다. 이 카테고리에도 다음과 같은 몇 가지 항목이 있습니다.

스레드에서 Activity 참조

Handler에서 Activity 참조

AsyncTask에서 Activity참조
스레드에서 Activity 참조와 동일하게 AsyncTask의 기술인 스레드풀, ExecutorService에도 동일한 원칙이 적용됩니다.

스레드 자체 누수

Activity에서 스레드를 시작할 때마다 스레드를 직접 관리해야 합니다. 스레드는 Activity보다 오래 작업할 수 있기 때문에 Activity가 소멸되면 스레드를 중지시켜야 합니다. 이렇게 하지 않으면 스레드가 누수될 위험이 있습니다.

 

까다로운 메모리 누수?

이상적으로 메모리 누수를 일으키는 코드를 작성하지 말아야 하며 존재하는 메모리 누수 문제를 수정해야 합니다. 그러나 실제로 다른 작업으로 인해 메모리 누수 수정의 우선순위를 판단하기 힘든데, 다음 3가지 목록을 통해 심각도를 평가할 수 있습니다.

1. 누수된 메모리는 얼마나 큰가?
모든 메모리 누수는 동일하지 않습니다. 일부는 몇 킬로바이트만 누설합니다. 일부는 많은 양의 메가바이트까지 유출할 수 있습니다. 앞서 언급한 도구를 사용하여 누설되는 메모리 크기를 측정하여 사용자의 기기에서 얼마나 중요한 용량인지 여부를 통해 결정할 수 있습니다.

2. 누수된 객체는 얼마나 오랫동안 메모리에 상주하는가?
스레드를 통한 누수는 스레드의 작업이 완료될 때까지 지속됩니다. 스레드가 최악의 시나리오에서 얼마나 오랫동안 지속되는지 검사를 해야 합니다. 예제에서 스레드는 무한루프이기 때문에 누수되는 객체는 영원히 메모리에 상주합니다. 실제로 대부분의 스레드가 파일 시스템에 액세스 하거나 네트워크 호출을 하는 등의 작업을 수행하기 때문에 일반적으로 시간이 제한되어 있는 짧은 시간일 것입니다. 발생할 수 있는 최대 시간은 메모리 누수를 수정의 우선순위를 결정할 때 고려해야 할 사항입니다.

3. 얼마나 많은 객체가 누수되는가?
예제의 정적 참조와 같이 메모리 누수는 하나의 객체에서만 나타납니다. 새로운 Activity가 생성되자 말자, 레퍼런스는 새로운 Activity를 참조되기 시작됩니다. 이전 Activity는 GC수집 대상이 됩니다. 따라서 최대 누출은 개수는 Activity 인스턴스 한 개입니다. 그러나 누수가 있는 경우 새 객체가 생성될 때마다 Activity 인스턴스 개수가 늘어납니다. 예제에서도 Activity가 생성될 때마다 스레드에 Activity에 참조가 걸려 누수가 발생됩니다. 따라서 기기를 20번 회전시키는 동안 20개의 스레드가 누수됩니다. 새로운 인스턴스가 GC에 의해 정리되지 않는 다면 사용 가능한 모든 메모리가 점점 줄어들기 때문에 정말로 나쁜 상황이 생길 수 있습니다.

 

고치거나 피하는 방법은?

Activity 클래스에서 정적 변수를 사용할 때 매우 주의해야 합니다. 정적 변수가 Activity를 직접 또는 간접적으로 참조할 가능성이 있는 경우 onDestory에서 참조를 끊어야 합니다. Manager 인스턴스 또는 싱글톤 객체에 리스너로 Activity를 전달할 때, 전달한 Activity 인스턴스로 다른 객체가 무엇을 하는지 알고 있어야 합니다. 필요한 경우 onDestory에서 리스너를 null로 설정하세요.

Activity 클래스에 내부 클래스를 만들 때 가능한 정적으로 만듭니다. 내부 클래스와 익명 클래스에는 암시적 참조가 있습니다. 따라서 내부/익명 클래스의 인스턴스가 포함된 클래스보다 오랫동안 유지되면 문제가 발생합니다. 누수의 위험을 피하기 위해 내부/익명 클래스가 아닌 정적 클래스를 사용하면 됩니다.

싱글톤 또는 Manager클래스를 만들 경우 Listener 인스턴스의 참조를 저장하여 관리할 수 있게 해야 하며, 사용자에 의해 참조를 관리할 수 없는 경우 Listener를 WeakReference로 관리하게 합니다. WeakReference는 GC에서 해당 대상을 지우지 않고 다시 회수하지 못하게 합니다. 이 기능은 메모리 누수를 막는데 큰 도움이 되지만 참조된 객체가 필요할 때 사용하지 못하는 부작용도 있을 수 있습니다. 따라서 메모리 누수를 수정하기 위해 가장 마지막 수단으로 사용해야 합니다. Activity에서 시작한 스레드 작업은 onDestory에서 항상 종료하세요.

 

마치며

우리는 메모리 누수가 무엇인지, 어떻게 발생하는지, 안드로이드 시스템에서 어떤 결과가 발생하는지에 대해 알아보았습니다. 메모리 누수를 탐지하고 식별하는 도구와 안드로이드의 일반적인 메모리 누수 패턴을 검사하는 방법, 심각도를 평가하는 방법과 피하거나 수정하는 방법을 소개하였습니다. Github저장소에서 일반적인 메모리 누수 패턴 및 수정에 대한 코드 예제를 한 번씩 확인해보세요. 모두 행복한 안드로이드 앱을 만드세요:)

 

참고:
https://developer.android.com/training/articles/perf-anr.html
https://www.dynatrace.com/resources/ebooks/javabook/how-garbage-collection-works/
https://developer.android.com/studio/profile/am-hprof.html
https://developer.android.com/reference/java/lang/ref/WeakReference.html
https://medium.com/google-developer-experts/finally-understanding-how-references-work-in-android-and-java-26a0d9c92f83#.h9w7hp13h




안드로이드 개발력 향상하기


안드로이드 앱개발에 필요한 팁과 학습방법에 관한 글입니다. 아래 정보를 잘 활용하여 안드로이드 개발 학습에 도움이 되거나 실무에 적용하여 업무에 도움이 되기를 바랍니다.

안드로이드 스튜디오의 “라이브 템플릿”을 사용하여 개발 향상

아래 링크는 안드로이드 스튜디오 팁을 모아둔 곳입니다.
https://plus.google.com/u/0/collection/wtO0PB
여기서 가장 쉽고 빠르게 쓸 수 있는 기능 중 하나는 라이브 템플릿 기능으로, 반복되는 메서드를 임의로 지정한 약자를 입력하면 풀네임으로 자동변경되는 기능입니다.
ex) fbc + Enter -> findViewById로 변환

라이브 템플릿 기능에 대해서 아래 링크를 참고하면 좀 더 많은 정보를 확인할 수 있습니다.
https://www.bignerdranch.com/blog/android-studio-live-templates/

안드로이드에서 기본적으로 지원해주는 라이브 템플릿외에도 사용자가 커스텀하게 만들 수도 있습니다. 아래 링크는 많이 사용되는 메서드들에 대한 라이브 템플릿을 커스텀하여 공개하고 있습니다.
https://github.com/keyboardsurfer/idea-live-templates

라이브 템플릿은 반복되는 메서드 입력을 최대한 줄여 개발 시간을 단축시킬 수 있는 가장 좋은 방법입니다.
https://medium.com/@aditlal/must-have-tools-for-android-development-d76ae66f409f#.qhhck9bvk

앱을 디버깅하는 동안 안드로이드 스튜디오와 함께 사용할 수 있는 도구

Library methods count – 안드로이드의 DEX파일 포맷 구조상 65만 개의 메서드로 제한되어 있습니다. 해당 툴을 이용하면 안드로이드 라이브러리의 메서드 개수를 확인 할 수 있습니다.

Stetho – 페이스북에서 만든 안드로이드 앱을 쉽게 감시할 수 있는 라이브러리입니다. 네트워크 트래픽을 디버깅하는데 가장 좋습니다. 이뿐만 아니라 SQLite 데이터베이스, 쉐어드 프리퍼런스도 쉽게 감시할 수 있습니다. 크롬 브라우저의 인스팩트 기능을 이용하기 때문에 웹 개발경험이 있다면 훨씬 쉽게 사용할 수 있습니다.

LeakCanary – 메모리 릭을 감지해주는 라이브러리로 코드 한 줄만으로 쉽게 사용 가능합니다. 릭 탐지 시 별도의 UI화면으로 발생되는 위치와 경로를 알려줍니다.

Gradle, Please – Gradle기반의 라이브러리를 쉽게 찾아줍니다. 라이브러리 이름만 입력하면 최신 버전으로 라이브러리를 컴파일할 수 있는 구문을 찾아줍니다.

Android Arsenal – 안드로이드와 관련된 라이브러리를 한 번에 볼 수 있습니다. 항상 최신의 버전을 유지하며 카테고리 기반으로 빠르게 업데이트됩니다.

Android UI OpenSource – 안드로이드 UI와 관련된 오픈소스를 한 번에 볼 수 있습니다.

AndroidTool Mac – 맥 개발자들을 위한 안드로이드 툴입니다. UI 환경에서 빠르게 화면 캡처, 비디오 캡처, APK 끌어서 설치를 할 수 있습니다.

ButterKnifeZelezny – 안드로이드 스튜디오 플러그인으로 선택한 레이아웃 XML에서 버터나이프 인젝션으로 변환해줍니다.

Adb-idea – ADB명령을 쉘이 아닌 안드로이드 스튜디오의 액션 창에서 작동할 수 있는 플러그인입니다.

AndroidWeekly – 매주 최신의 안드로이드 관련 소식을 메일링으로 받아 볼 수 있습니다.

Android Developers Youtube Channel – 구글 안드로이드 공식 유튜브 채널이며, 가장 최신의 안드로이드 기술을 접할 수 있습니다.

완성된 앱으로 학습하기 

Plaid – 디자인 뉴스와 영감을 제공하기 위한 안드로이드 앱입니다. 안드로이드 UI처리에 대해 전반적인 학습을 할 수 있습니다.

Kickstarter – 구글의 킥스타터 앱으로 예술, 디자인, 영화, 게임 음악 등으로 구성된 수천 개의 프로젝트를 탐색할 수 있습니다. 디자인 가이드라인부터 최신 기술을 한 번에 공부할 수 있습니다.

참고

CoordinatorLayout과 Behavior의 관계

머트리얼 디자인 가이드 라인중 스크롤시 다양한 반응을 위한 테크닉인 Behavior라는 개념이 도입 되었습니다. 기본적으로 액션바를 확장하여 스크롤시 액션바를 줄여들게 하도록 AppBarLayout의 ScrollingViewBehavior와 스크롤시 하단으로 숨기게 하기위해 BottomSheetBehavior를 서포트라이브러리에서 제공하고 있습니다.

  • android.support.design.widget.AppBarLayout$ScrollingViewBehavior
  • android.support.design.widget.BottomSheetBehavior

Behavior를 사용하기 위해서는 CoordinatorLayout을 통해서 사용되는데, CoordinatorLayout은 자식뷰의 스크롤의 변화 상태를 다른 자식뷰들에게 전달 해주는 역할을 합니다. 좀더 쉽게 말해 NestedScrollView나 RecyclerView등에 스크롤의 상태를 판단하여 정의된 반응을 하기위한 View에 Behavior를 등록하면 됩니다.

이해를 돕기위해 안드로이드 서포트 라이브러리에서 제공해주는 Behavior를 한번 보겠습니다. NestedScrollView에 layout_behavior에 AppBarLayout$ScrollingViewBehavior가 정의가 되어있습니다. NestedScrollView의 반응에 따라 AppBarLayout이 반응됩니다.

CoordinatorLayout는 NestedScrollView가 스크롤시 layout_behavior에 정의된 레이아웃으로 스크롤 정보를 전달 하는 역할을 합니다. 그럼 AppBarLayout의 ScrollingViewBehavior가 정보를 받아서 AppBarLayout 자신을 변형하도록 하는 구조입니다.

CoordinatorLayout이 스크롤되는 것은 Behavior에 구현된 NestedScrollingParent를 통해 전달 됩니다. 즉, CoordinatorLayout는 NestedScrollingParent가 구현되어 있으며 스크롤 되는 View들은 NestedScrollingChild가 구현되어 있어야 Behavior가 전달 됩니다. 그렇기 때문에 기존의 ScrollView나 ListView는 NestedScrollingChild가 구현되어 있지 않아 Behavior를 통해 스크롤 정보전달이 되지 않습니다.

이렇게 CoordinatorLayout의 역할과 Behavior의 관계를 알고 있다면 Behavior를 커스텀해서 구현하는데 전혀 문제 없을 것입니다.

레이아웃 비동기로 인플레이트하기

안드로이드 서포트 라이브러리 리비전 24부터 레이아웃을 비동기로 인플레이트 할 수 있는 클래스가 추가되었다.

AsyncLayoutInflater

어싱크 레이아웃 인플레이터를 사용하면 무거운 인플레이션이 수행되는 동안 UI스레드에 대한 응답성을 보장할 수 있다. 기존의 레이아웃 인플레이터를 사용하는 경우 UI스레드에서 인플레이션 작업이 수행되어 무거운 레이아웃의 경우 화면이 끊기거나 심한 경우 ANR이 발생된다. 이러한 문제를 보완하기 위해 AsyncLayoutInflater를 제공한다. 별도의 스레드에서 인플레이션을 수행하고 생성된 뷰를 UI스레드로 콜백 받을 수 있다.

사용방법은 레이아웃 인플레이터와 동일하게 AsyncLayoutInflater로 클래스를 생성 후 inflate() 메서드를 호출하면 된다.

AsyncLayoutInflater(@NonNull Context context)

inflate() 메서드 호출 시 인플레이트 대상의 레이아웃 리소스와 인플레이트 된 뷰를 속하게 하고 싶은 뷰 그룹과 콜백 받기 위한 OnInflateFinishedListener의 파라미터가 필요하다.

inflate(@LayoutRes int resid, @Nullable ViewGroup parent, @NonNull OnInflateFinishedListener callback)

생성이 완료되면 OnInflateFinishedListener의 onInflateFinished()를 통해 뷰를 전달된다.

AsyncLayoutInflater은 UI스레드가 아닌 별도의 스레드를 통해서 레이아웃을 인플레이트 하기 때문에 무거운 레이아웃으로 인해 화면의 응답성이 떨어지는 경우 사용할만하다. 액티비티 시작 시 레이아웃이 너무 큰 경우라도 화면의 전환 속도가 느리다면 로딩화면을 심플하게 구현 후 다음 비동기 처리를 통해 뷰를 생성하는 것도 하나의 방법이 될 수 있을 것이다.

구현 예시

안드로이드 7.1 앱 바로가기 기능 구현

안드로이드 7.1에 앱 바로가기(Shortcut)기능이 추가되었다. 구글의 픽셀런처에만 지원하고 있지만 앞으로 안드로이드 7.1이 올라가면서 제조사의 런처에서도 지원될 가능성이 높다.

픽셀런처의 홈 화면에서 아이콘을 길게 누르면 앱 바로가기 목록이 펼쳐지며, 이동을 위해 움직이게 되는경우 비활성화 되는 방식이다. 애플 iPhone의 3D터치와 기능은 같으나 작동방법은 소프트웨어로 구현되었다는 점에서 다르다.

구현방법은 아주간단하다. 구글에서 정해둔 규약을 잘 따라 메니페스트에 메타데이터를 정의하면 된다. 에버노트의 경우 검색 바로가기, 노트바로 작성하기등 앱에 진입하지 않고 빠르게 특정한 작업을 할 만한 사항들의 기능을 넣었다.

정해진 이름으로 메타 데이터에 앱 바로가기를 위한 리소스를 추가후 바로가기 아이템을 xml에 하나씩 정의하면된다. 앱에 넣을 기능의 커스텀 스킴을 정의 해두었다면 intent의 action에 스킴을 호출하면 아주 간단하게 추가할 수 있다. 그렇지 않은 경우 실행할 Activity 클래스명을 적어주면된다.

동적 구현방법
xml을 통해 정적으로 구현할 수도 있으나 상황에 따라 앱 바로가기 아이템을 추가하거나 삭제 하고 싶은 경우 ShortcutManager를 통해서 추가하거나 삭제 또는 업데이트할 수 있다.

앱 바로가기 기능을 통해 앱에 진입 하지 않도 빠르게 검색을 한다거나 재생목록을 재생하는등 다양하게 활용하여 사용편의성을 높일 수 있다. 이미 이런 사용성을 생각한 개발자라면 런처 바로가기 기능을 통해 이미 구현을 했을 것이고 앱 바로가기 기능은 쉽게 구현 할 수 있을 것이라 생각된다.

참고: https://developer.android.com/preview/shortcuts.html

Android Support Annotations

안드로이드 개발 시 Annotation을 사용할 수 있도록 서포트 라이브러리를 지원한다. Annotation을 통해 코드의 설명을 간단명료하게 표시할 뿐 아니라 제약할 수도 있다. 즉, 해당 메서드나 멤버 변수를 다른 사람 또는 내가 재사용할 때 필요한 규약을 지정하여 예외적인 사항을 컴파일 에러를 통해 바로 확인이 가능하다.

왜 사용해야 하는가?

런타임시 발생되는 예외사항을 빌드 전 컴파일 에러를 통해 대부분 막을 수 있기때문에 앱의 개발 속도 향상은 물론 코드를 설명하기위한 긴 설명의 주석을 대체할 수 있다. 또한 다른 사람이 작성한 코드를 사용하거나 반대로 내가 작성한 코드를 다른사람이 사용하거나 분석할때 시간을 줄 일 수 있다.

안드로이드 Annotation 라이브러리 추가(build.gradle)

dependencies { 
    compile 'com.android.support:support-annotations:24.2.0' 
}

@NonNull / @Nullable

메서드의 파라미터에 null이 허용되는 경우도 있고, 반드시 값이 필요한 경우도 있다. 자신이 개발한 코드가 아니거나 또는 개발을 했음에도 코드를 살펴보고 null체크를 하는지 판단을 해야 하는 경우 사용하면 된다. 파라미터에 @NonNull을 사용하면 null값을 허용하지 않으며, 임으로 null을 넣는 경우 컴파일 에러가 난다. 반대로 메서드 내부적으로 null체크를 하여 해당 기능을 분기하는 경우 null이 허용 가능함으로 @Nullable를 사용하면 된다.
 
no-annotations
 
nonnull

@CheckResilt

메서드의 리턴 값을 필수적으로 받아야 할 때 사용하면 된다.
 
checkreturn.png

@StringRes / @DrawableRes / @ColorRes / @(Etc)Res

메서드에 전달 인자가 리소스 주소인 Integer인 경우 해당 리소스가 어떠한 형태인지를 미리 알려줄 수 있는 기능이다. 예를 들어 TextView의 setText(int) 메서드의 경우 String 리소스 주소를 넣어야 한다는 것을 명시할 수 있다. String리소스 주소가 아닌 Drawable리소스나 임의의 Integer값을 넣는 경우 컴파일 에러를 통해 알려준다.
 
stringres.png

@MainThread / @UiThread / @WorkerThread

메서드가 실행될 때 해당 수행되어야 할 스레드를 명시할 수 있다. AsyncTask의 스레드에서 UI를 변경하는 작업을 하는 경우 에러가 나는데, 이런 경우를 사전에 알려준다.
 

@Keep

빌드시 프로가드를 통해 난독화가 되어 사용 불가능 한 클래스의 경우 난독화를 예외처리해주어야 한다. 본인이 개발하지 않는 경우 이런 히스토리를 알기는 쉽지 않다. 이런 경우 @Keep를 통해 난독화 예외처리를 해야 하는 클래스인지 명시적으로 알려줄 수 있다.

이외에도 @CallSuper, @RequiresPermission, @Size 등 다양한 Annotation이 존재한다. 좀 더 알아보기 위해 아래 사이트를 참고하자.

참고:

빠르고 유연한 ContraintLayout

ContraintLayout?

ContraintLayout은 2016 구글 I/O를 통해 발표된 안드로이드의 새로운 레이아웃이다. 안드로이드 스튜디오(2.2 Preview2 부터)에 내장된 새로운 레이아웃 에디터(Blue Print)와 연동을 통해 이전의 레이아웃보다 쉽게 구성할 수 있다. 뷰 계층의 깊이와 복잡성을 해결하기 위해 ContraintLayout이 만들어졌으며, 앱의 UI렌더링 속도를 높일 수 있을 뿐만아니라 다양한 기기의 해상도에 최적화된 UI를 쉽게 개발 할 수도 있다. 안드로이드 서포트 라이브러리를 통해 사용가능하며 API 레벨 9부터 사용가능하다.

build.gradle에서 해당 라이브러리를 추가한다.

compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha2'

ContraintLayout 속성

  • layout_constraintTop_toTopOf
  • layout_constraintTop_toBottomOf
  • layout_constraintBottom_toTopOf
  • layout_constraintBottom_toBottomOf
  • layout_constraintLeft_toTopOf
  • layout_constraintLeft_toBottomOf
  • layout_constraintLeft_toLeftOf
  • layout_constraintLeft_toRightOf
  • layout_constraintRight_toTopOf
  • layout_constraintRight_toBottomOf
  • layout_constraintRight_toLeftOf
  • layout_constraintRight_toRightOf
  • layout_constraintCenterX_toCenterX
  • layout_constraintCenterY_toCenterY
  • layout_constraintBaseline_toBaselineOf

속성을 보면 알겠지만 많은 속성들로 인해 복잡하고 뷰들 간의 관계에 대한 속성들이 대부분이다. 자식뷰들관의 관계를 연결해주는 것을 보면 RelactiveLayout와 비슷해보이지만 ContraintLayout은 자식들과 부모관의 관계에 대한 정렬및 배치 방식과 좌표가 아닌 비율을 통해 위치가 지정되는등 다양한 기능을 가졌다.

 

레이아웃 에디터

개발자라면 그동안 레이아웃을 XML편집기를 이용하여 텍스트로 작성했을 것이다. 하지만 ContraintLayout은 무수히 많은 속성과 타겟 뷰의 ID를 값으로 주는 만큼 레이아웃구조를 텍스트로 작성하거나 또는 읽을때 예전보다는 확연히 힘들것이다. 이렇게 복잡한 레이아웃을 텍스트가 아닌 새롭게 선보인 디자인 툴(Blue Print)을 이용하면 훨씬 쉽고 간단하게 작성할 수 있다.

레이아웃 에디터는 UI설계를 하는 Design과 뷰들관의 관계를 보여주는 BluePrint 화면 2개가 나타난다. 이 2개 화면을 동시에 보거나 따로보는 방법은 레이아웃 에디터의 상단 아이콘을 이용하면 전환할 수 있다.

be44fb8e685150a5565f19c94e36e954
Design 표시 아이콘: 한번 누르면 디자인 화면으로 전환 되며 또한번 누르면 BluePrint 화면을 동시에 2개가 나타난다.

a72c79e2a3bf45f38413fec30f3dac36
BluePrint 표시 아이콘: 한번 누르면 BluePrint화면으로 전환되며 또한번 누르면 디자인 화면과 동시에 2개가 나타난다.

 

레이아웃 편집시 쓰이는 아이콘

b25f376c52508c900dbab5a8bf5d68bf
BluePrint화면의 뷰들간의 관계에 대한 정보를 숨기고 보일 수 있다.

f6ea28b2afd03030cfeed10d9b3f165a
새로운 뷰를 드래그 하는 경우 다른뷰들간의 관계를 자동으로 연결할 수 있다.

3fd3b60139267dc5bd0e2abce72f6f30
관계를 정보를 모두 삭제 한다.

4d928284744c25094798de88e6cce11d
자동으로 관계에 대한 정보를 연결한다.

7c2b5cfb7453a9614c2e5507998eff27
마진값의 단위를 선택한다. 0, 8, 16dp로 전환이 가능하다.

 

레이아웃 내에서 쓰이는 툴


하나의 뷰를 나타내며 가로/세로 크기와 다른뷰와의 관계에 대해 화살표로 설정할 수 있는데 자세히 알아보자.

99a8ebe4d5ada56e4abacb2dfedc9d50
뷰 크기 변경 컨트롤: 각모서리 가장자리에 있는 네모 모양을 통해 뷰의 크기를 늘리고 줄일 수 있다.

924dedda9eb89c0ab8f6a268c4558e13
관계 설정 컨트롤: 둥글게 생긴 부분으로 다른 뷰들관의 관계를 지정할 수 있다. 가로축의 컨트롤은 다른뷰의 가로축에만 연결되며, 세로축은 다른뷰의 세로축에만 연결된다. 이미 관계가 지정되어 있을때 클릭하면 해제된다.

a6b25ad94ce53ddfedbf2d689a506b78
베이스 라인 컨트롤: 뷰의 기본 라인을 맞춘다. 베이스 라인은 뷰내의 실제 컨텐트가 배치해있는 위치이다. 해당 컨트롤을 선택하기위해서는 커서를 몇초간 위치해있어야 한다.

 

ContraintLayout에서 뷰크기 지정

뷰의 크기는 우리가 알고 있듯이 고정된크기, 뷰의 컨텐츠에 맞게 지정되는 방식, 부모크기를 따라가는 방식이 있다. 이를 UI적으로 표현하여 좀거 쉽게 설정가능하다.

e8124ddde04b2aa76e42f79582123218

7300702340c7018202fbdcf57af5b525
고정된크기: 뷰의 사이즈가 고정되어 있다.

709b28b53490f5fb5f6dcdc5380a6fb3
부모 컨텐츠 크기: MATCH_PARENT방식으로 작동한다.

af2e72fd3b0d4188c61e06da211f9835
뷰 컨텐츠 크기: 뷰의 크기에 따라 크기가 설정된다.

edcfce9f89f19fb900de367212f55c3a
수평및 수직 정렬: 부모 뷰와 자식뷰가 연결되어 있는 경우 정렬을 퍼센테이지로 설정할 수 있다. LinearLayout의 weight와 비슷하다고 생각하면 쉽다.

팁!

처음 ContraintLayout을 접해보면 생각보다 힘든 작업이 될가능성이 높다. 이것저것 버튼을 눌러서 레이아웃을 맞추기에 생각보다 번거롭기 때문이다. 그래서 한가지 팁을 소개해보겠다. 먼저 뷰들간의 관계를 연결하지 말고 배치만으로 작업을 한다. 연결 되어 있다면 모두 끊은 상태에서 배치작업을 한뒤 자동 관계아이콘을 클릭하여 연결을 자동으로 구성 후 보정작업을 하거나 간단한 경우 직접 연결해준다.

 

ContraintLayout은 알파단계로 약간의 버그가존재하며 디자인툴이 생각보다 빨리 움직이지 않고 한번씩 다운되는등의 문제점이 발생된다. 하지만 기존의 너무 깊은 레이아웃을 구조로 앱의 성능에 문제가 생긴점을 해결해줄 속 시원한 레이아웃이 될것 같다. 그리고 BluePrint로 인해 디자이너도 툴을 쉽게 익히고 사용하는데 무리가 없을것 같다. 기존의 계층구조가 복잡한 레이아웃이 있다면 조금씩 바꿔나가는 것에 추천한다.