Dagger 2 소개, 안드로이드에서 Dependency Injection 사용하기전에

이 글은 Janishar Ali가 작성한 Introduction to Dagger 2, Using Dependency Injection in Android: Part 1을 번역하였습니다.

Android에서 Dagger2 사용법을 이해하려면 왜 필요한지를 먼저 알아야 합니다. 중요한 질문은 다음과 같습니다.

왜 의존성 주입(Dependency Injection)이 필요한가?
의존성 주입은 Inversion of Control 개념을 바탕으로합니다. 클래스가 외부로부터 의존성을 가져야합니다. 간단히 말해 클래스는 다른 클래스를 인스턴스화해야하지만 구성 클래스에서 인스턴스를 가져와야합니다.
Java 클래스가 new 연산자를 통해 다른 클래스의 인스턴스를 생성하면 해당 클래스와 독립적으로 테스트하고 사용할 수 없으며 이를 하드종속성이라고합니다.

그렇다면 클래스 외부에서 종속성을 제공하면 어떤 이점이 있을까요?
가장 중요한 장점은 클래스를 재사용 할 가능성을 높이고 다른 클래스와 독립적으로 클래스를 테스트 할 수 있다는 것입니다.
이것은 비즈니스 로직의 특정 구현이 아닌 클래스를 생성하는데 매우 효과적입니다.
이제 조금은 이해 했으므로 종속성 삽입 탐색을 진행할 수 있습니다.

큰 문제는 바로 DI(Dependency Injection)를 어떻게 할 것인가입니다.
이 질문에 답하기 위해 우리는 과거를 되돌아 봐야합니다.
종속성 컨테이너라는 프레임워크 클래스는 클래스의 종속성을 분석하는데 사용되었습니다. 이 분석을 통해 Java Reflection을 통해 클래스의 인스턴스를 만들고 정의 된 종속성에 객체를 삽입 할 수있었습니다. 이로 인해 어려운 의존성이 제거되었습니다. 그런 식으로 클래스를 독립적으로 테스트 할 수 있습니다. mock 객체를 사용합니다. 이것은 Dagger 1이었습니다.

이 과정의 주요 단점은 두 가지입니다.
첫째, Reflection 자체가 느림.
번째로, 런타임에 종속성 해결을 수행하여 예기치 않은 충돌 발생.

이것은 Dagger2의 탄생으로 이어졌습니다. Dagger2는 Google의 Square Dagger1에서 분기되었습니다.

Dagger2에서 가져온 큰 변화는 주석 처리기(Annotation Processor)를 사용하여 종속성 그래프를 생성하는 것이 었습니다. 의존성을 제공하는 클래스는 이제 javax injection 패키지를 사용하여 빌드시 생성됩니다. 이것은 응용 프로그램이 실행되기 전에 가능한 오류 검사를 용이하게합니다. 생성된 코드는 직접 작성한것 처럼 보기 쉽습니다.

참고: 주석 처리기는 프로젝트에서 사용할 소스코드 파일을 생성하기 위해 컴파일하는 동안 컴파일 된 파일을 읽는 방법입니다.
그래도 잘 이해 되지 않는다면 다음 파트2의 예제를 보기위해 기다리면 됩니다.

DI작동 방식에 대한 몇가지 정보를 드리겠습니다.
클래스의 종속성을 설명하기위한 표준 Java 주석은 Java Specification Request 330(JSR 330)에 정의되어 있습니다.

Injection 모드 :
1. Constructor Injection: 생성자 삽입.
2. Field Injection: 멤버 변수 삽입 (비공개이면 안됨).
3. Method Injection: 메소드 매개 변수 삽입.

JSR330에 따른 종속성 주입 순서
1. Constructor
2. Field
3. Method

@Inject로 주석처리된 메소드나 필드가 호출되는 순서는 JSR330에 의해 정의되지 않습니다. 메서드나 필드가 클래스에서 선언 된 순서대로 호출된다고 가정 할 수 없습니다.
생성자가 호출 된 후에 필드 및 메서드 매개 변수가 삽입되므로 생성자에서 삽입 된 멤버 변수를 사용할 수 없습니다.

Dagger2를 사용하여 종속성 주입 프로세스를 시각화하는 경향이 있습니다.

종속성 소비자는 커넥터를 통해 종속성 공급자의 종속성(Object)을 필요로합니다.

  1. Dependency provider: @Module로 주석 된 클래스는 삽입 할 수있는 객체를 제공합니다. 이러한 클래스는 @Provides로 주석 된 메소드를 정의합니다. 이 메소드의 리턴 된 오브젝트는 종속성 삽입에 사용 가능합니다.
  2. Dependency consumer: @Inject 어노테이션은 의존성을 정의하는데 사용된다.
  3. Connecting consumer and producer: @Component 주석이 달린 인터페이스는 객체 제공자(모듈)와 의존 관계를 표현하는 객체 사이의 연결을 정의합니다. 이 연결에 대한 클래스는 Dagger에 의해 생성됩니다.

Dagger2의 한계 :
1. Dagger2는 필드를 자동으로 주입하지 않습니다.
2. 비공개 필드를 주입 할 수 없습니다.
3. 필드 주입을 사용하려면 @Component 주석이 달린 인터페이스에서 멤버 변수를 삽입 할 클래스의 인스턴스를 취하는 메소드를 정의해야합니다.

ConstraintLayout으로 아름다운 애니메이션하기

이 글은 Jinyan Cao가 작성한 Beautiful animations using Android ConstraintLayout 번역글입니다.

ConstraintLayout은 날이 갈 수록 인기를 더해가고 있습니다. 수평적인 뷰 계층 구조와 성능을 향상시키고, 임의의 경계 규칙을 지원합니다. 이전 레이아웃의 단점을 모두해결 할 것입니다. ConstraintLayout의 이점 중 하나는 매우 적은 코드로 멋진 애니메이션을 수행 할 수 있습니다. 이는 대부분의 개발자들이 알지못하며, 공식 문서에도 아무것도 언급되어 있지 않습니다.

방법?

ConstraintLayout의 기본 사항을 알고 있다고 가정합니다 (예: app:layout_constraintLeft_toLeftOf 및 다른 속성). 대부분의 문서또는 사이트에서는 새로 개선 된 Android Studio 레이아웃 디자인 패널을 사용하여 다양한 Constraint를 드래그/드롭/시각화 하는 방법만 소개되어 있습니다. 애니메이션의 목적을 위해서는 Constraint를 정확하게 이해하고 있어야만 조작이 가능합니다.

가장 간단한 형식인 TransitionManager(API 19 이상 또는 서포트 라이브러리에서 사용가능)를 통해 두 가지 Constraint 집합간에 애니메이션을 적용 할 수 있습니다. 긴 설명 보다 간단한 예제를 살펴 보겠습니다.

예제

Activity 실행시 초기화되는 XML 레이아웃부터 살펴 보겠습니다.

이것은 화면의 너비와 일치하는 화면 상단에 ImageView를 정의하는 기본 XML 파일입니다. (ConstraintLayout은 match_parent를 지원하지 않습니다.) 이제 하나의 추가 Constraint를 가진 대체 XML 레이아웃을 정의합시다.

여기서 유일한 차이점은 새 XML 레이아웃이 부모의 높이와 일치하는 높이로 설정하는것 입니다. 결과적으로 ImageView는 수직으로 가운데 정렬됩니다.
이제 두 개의 서로 다른 제약 조건 세트 (하나는 ImageView를 수직으로 중심에 배치하지 않고, 하나는 수직으로 가운데 배치)간에 애니메이션을 적용하려면 다음 코드를 Activity에 추가해야합니다.
(참고: 여기서는 Kotlin에 쓰여 있습니다. Kotlin에 익숙하지 않은 분은 Java 코드와 완전히 역 호환되는 동시에 최신 프로그래밍 언어의 많은 이점을 제공하므로 시작하는 것이 좋습니다. 이제 Android에서 공식적으로 지원되는 언어입니다!)

constraintSet1과 constraintSet2는 비 수직 가운데 정렬과 수직 가운데 정렬에 대응하도록 설정합니다. 먼저 TransitionManager에 ConstraintLayout에서 지연된 전환을 시작하라고 지시합니다. 그런 다음 ConstraintLayout에 다른 Constraint 집합을 적용합니다. TransitionManager는 자동으로 애니메이션을 수행하여 Constraint의 변경 사항을 표시합니다.

XML 레이아웃을 복제합니까?

무엇을 생각하는지 알고 있습니다. 이 접근법은 Constraint 변경을 위해 레이아웃 파일을 복제해야합니다. 아무도 중복 된 코드를 좋아하지 않습니다.
이것은 실제로 생각만큼 나쁘지 않습니다. 전환을 위해 대체 XML파일 생성하는 경우 레이아웃의 모든 속성(예 : textSize)을 생략 할 수 있습니다. ConstraintSet은 각 뷰의 Constraint의 속성을 제외한 나머지 속성을 무시합니다. 이렇게하면 두 파일에서 일관된 스타일을 유지할 필요가 없습니다.(예: 원래 XML에서 textSize를 변경하면 대체 XML에서 이를 변경할 필요는 없습니다.)
XML 코드 복제를 하고 싶지 않는 경우, 코드에서 동적으로 속성을 변경하면 됩니다.
위의 예를 하나의 레이아웃 XML로 어떻게 변경하는지 살펴 보겠습니다.

위의 코드에서 constraintSet2에 속성을 코드에서 변경하여 애니메이션을 수행하고 있습니다. 이렇게하면 우리는 Constraint 속성을 그대로 유지하고 코드 복제를 피할 수 있습니다.

하지만 이미 Transition 프레임워크를 사용하여 동일하게 사용할 수 있다!
이것은 전혀 새로운 방식이 아닙니다. Transition 프레임워크 또는 animateLayoutChanges와 같은 속성을 사용하여 동일한 작업을 수행 할 수 있습니다. 그러나 Constraint를 지정할 수 있기때문에 훤씬 강력합니다.
또 다른 이 점은 많은 요소를 애니메이션으로 만들려고 할 때입니다. 이 애니메이션을 살펴 보겠습니다.

Robinhood가 ConstraintLayout을 사용하여 주문 애니메이션을 만듭니다.
Robinhood (Android)의 주문 흐름 애니메이션입니다. 페이지의 모든 단일 요소(카드, 사용자 정의 키패드, FAB 등)를 수동으로 애니메이팅 하도록 구현되어 있습니다. 이 코드는 특히 앞뒤 애니메이션을 따로 작업한다는 점을 감안할 때 읽기에 약간의 문제가 있습니다.
대신이 애니메이션에 ConstraintLayout을 사용하는 샘플 앱을 만들었습니다. 이 구현은 훨씬 간단합니다. 변경되어야 할 속성만 바꾼다음 대체 XML 레이아웃 파일을 지정하면 애니메이션 프레임워크가 모든 것을 애니메이션으로 변환합니다. UI에서 이 애니메이션을 처리하는 코드는 ~250줄에서 ~30줄 간단해졌습니다.

 

더 있다!

ConstraintLayout 애니메이션을 시작하는 데 사용하는 코드를 기억하십니까?

두번째 파라미터를 이용하여 애니메이션을 커스터마이징 할 수있습니다!(기본은 내부에 구현된 기본 Transition 사용) 예를들어 애니메이션 속도를 쉽게 변경할 수 있습니다.

사소한주의 사항

ConstraintLayout 애니메이션으로 사용해본 후, 나는 애니메이션을 구현할 때 고려해야 할 몇 가지주의 사항을 발견했습니다.

  1. ConstraintLayout은 핸들링하는 자식에 대한 속성변경을 알고 있기때문에 직접 자식에 대해서만 애니메이션을 수행합니다. 중첩 된 ViewGroups인 경우 잘 처리 되지 않음을 의미합니다. 위의 예제에서 CardView 내부의 텍스트는 외부 ConstraintLayout에 의해 처리되지 않으므로 코드에서 수동으로 애니메이션을 적용해야합니다. 이것은 아마도 중첩 된 ConstraintLayout을 사용하여 해결할 수 있지만 여기서는 사용하지 않았습니다.

  2. ConstraintLayout은 레이아웃 관련 변경 사항 만 애니메이션으로 나타냅니다. 대체 XML 파일에서 다른 속성 (예: elevation, text)을 읽을 수 없으며 프레임워크가 모든 것을 처리 합니다. ConstraintSet.clone()은 레이아웃/Constraint 변경 사항을 복사하고 다른 모든 항목은 삭제합니다.

  3. constraint-layout:1.0.2에서 ConstraintLayout 속성을 동적으로 변경하면 업데이트 된 속성을 고려하지 않고 애니메이션됩니다.(예: translationY). 즉, 애니메이션을 실행하면 변경전의 속성의 값으로 되돌아 간뒤 새로운 값으로 애니메이션이 적용됩니다.

마치며..

ConstraintLayout을 사용하는 페이지에서 애니메이션을 구현할 함으로 Transition 프레임 워크 보다 많은 기본 레이아웃 변경 애니메이션을 수행 할 수 있습니다. 이를 통해 Activity/Fragment에서 UI/애니메이션 로직을 압축하고 XML로 통합 할 수 있습니다. 또한 더 읽기 쉬운 애니메이션 논리를 만듭니다. (아무도 프로그래밍 방식으로 생성 된 애니메이션을 읽는 것을 좋아하지 않습니다).

구글 I/O 2017, Android 새로운 기능 정리

구글 I/O 2017 안드로이드 키노트 정리를 해보겠습니다. 안드로이드 O부분도 있지만, 지금 바로 사용가능한 서포트 라이브러리에 대한 소식도 많습니다.

2017-05-18 오전 10.52.39

16비트 PNG파일을 지원합니다. 새로운 유틸 클래스를 이용하면 16비트의 PNG파일을 사용 할 수 있습니다.

2017-05-18 오전 10.53.15
24비트 vs 16비트

핵심적인 컬러값만을 사용하여, 복잡하지 않은 이미지에 사용한다면 용량과 성능을 높일 수 있습니다.

2017-05-18 오전 10.53.38

멀티 윈도우를 지원하는 앱이라면, 멀티 디스플레이도 지원할 수 있습니다. 앱이 실행될때 사용자는 임의로 제어가능하며, 앱에서도 ActivityOptions를 통해 제어할 수 있습니다.

2017-05-18 오전 10.54.08

멀티 디스플레이 테스트는 adb shell 명령을 통해 멀티 디스플레이를 사용할 액티비티를 실행 할 수 있습니다.

2017-05-18 오전 10.54.51

미디어 매트릭스 API를 지원합니다. 이를 통해 미디어에 대한 정보(해상도, 프레임등)를 간단하게 얻어 올 수 있습니다.

2017-05-18 오전 10.55.11

MediaPlayer에서 버퍼링을 제어 할 수 있도록 개선되었습니다. 또한 훨신 쉽게 DRM Playback을 지원합니다.

2017-05-18 오전 10.56.08

MPEG-2 TS 포맷 지원도 합니다.

2017-05-18 오전 10.56.23

크롬 브라우저에서 지원하는 안전하지 않는 웹 페이지의 기능을 웹뷰에서도 사용가능합니다. 안드로이드 매니페스트에 메타 태그를 간단히 추가만하면 됩니다. 또한 멀티 프로세스를 위한 새로운 API도 추가되었습니다.

2017-05-18 오전 10.57.49

setCurrentPlayTime()을 통해 애니메이션할 시간을 임의로 지정 가능합니다. 이는 자유자재로 애니메이션 플레이를 제어 할 수 있습니다. 또한 reverse()를 통해 반대로 애니메이션 할수 있도록 지원합니다.

2017-05-18 오전 10.58.31

사용자 정보를 자동으로 채울 수 있는 자동완성 기능도 추가 되었습니다.

2017-05-18 오전 11.00.23

글꼴 리소스를 공식 지원합니다.

2017-05-18 오전 11.00.52

글꼴을 앱에 탑제하지 않고 구글 플레이 서비스의 폰트 프로바이더를 통해서도 사용가능합니다.

2017-05-18 오전 11.01.39

텍스트뷰의 텍스트를 자동으로 사이즈 변경기능을 지원합니다. 텍스트의 크기를 자동으로 뷰 사이즈 크기에 맞게끔 변경해줍니다. 이 모든것은 서포트 라이브러리를 통해 사용가능합니다.

2017-05-18 오전 11.02.55

접근성 서비스의 경우 언어 감지, 볼륨 컨트롤 사용자 정의, 제스쳐 사용자 정의를 지원합니다.

2017-05-18 오전 11.03.41

findViewById는 이제 자동 캐스팅을 지원합니다. 하지만 이를 대처할만한 라이브러리가 많고 하위 안드로이드 O에서만 지원하기 때문에 그렇게 유용하지는 않을듯 합니다.

2017-05-18 오전 11.04.51

반응형 아이콘을 만들수 있습니다. 아이콘은 배경과 전경으로 이루어지며, 런처는 마스크를 통해 아이콘 모양을 직접 디자인 할 수 있습니다.

2017-05-18 오전 11.06.00

바로 가기또는 위젯 생성을 위한 새로운 Action이 추가 되었습니다. 런처의 위젯 추가가 아닌 앱에서 바로 위젯을 추가 요청을 할 수 있습니다.

2017-05-18 오전 11.06.14

ShortCutManager, AppWidgetManager를 통해 바로가기, 위젯 추가 요청을 하면 Manager의 다이얼로그가 나타납니다. 이를 통해 위젯의 사용성을 향상 할 수 있습니다.

2017-05-18 오전 11.06.55

수 많은 알림은 사용자가 원하는 정보를 얻기에는 매우 어렵습니다. 또한 사용자가 임의로 제어기도 힘듭니다.

2017-05-18 오전 11.07.22

이런 문제점으로 안드로이드 젤리빈에서는 알림을 블락처리하는 기능을 추가 했었으며, 안드로이드 누가에서는 무음으로 처리하는 기능이 추가 되었습니다.

2017-05-18 오전 11.07.43

하지만 여기에도 앱내에서 기능별로 알림을 제어 할 수 없다는 문제점이 있습니다. 안드로이드 O에서는 동일한 동작을 하는 알림을 채널별로 나누어 사용자가 컨트롤 할 수 있는 기능을 추가 하였습니다.

2017-05-18 오전 11.10.13

Strict Mode에 Unbuffer I/O와 sockets, URI를 감시하는 기능이 추가 되었습니다.

2017-05-18 오전 11.14.52

구글 플레이는 설치된 모든 앱에 대해 검증된 앱인지를 판별 해주는 “구글 플레이 프로텍트”기능이 추가 되었습니다. 플레이 스토어뿐만 아니라 직접설치, 다른 스토어에서 설치된 앱도 잠재적인 위험이 있는지에 대해 스캐닝 해줍니다.

2017-05-18 오전 11.20.35

새로운 복사 수집기능이 추가 되었으며, 최적화된 힙 관리기술도 추가 되었습니다.

2017-05-18 오전 11.23.28

물리학 기반의 애니메이션 시스템을 통해 개발자는 복잡한 수학 계산필요 없이 단순히 자연어를 통해 물리학 기반의 애니메이션을 구현 할 수 있습니다.

이 외에도 Activity 라이프사이클을 좀 더 단순화하였으며, 배터리 최적화를 위해 백그라운드 처리에 대한 좀 더 타이트한 기술등 소개되었습니다.

안드로이드 어플리케이션 아키텍처

본 글은 Android Application Architecture를 번역한 글입니다.
RxJava입문자부터 MVP 기반의 아키텍처에 대해 알고 싶으신 분들이 보시기에 좋습니다.

안드로이드 개발 생태계는 매우 빠르게 움직입니다. 매주 새로운 툴이 만들어지며 라이브러리가 업데이트되며 블로그의 게시물이 올라오며 커뮤니티에는 많은 문제들로 활발히 논의중입니다. 한 달간 휴가를 다녀온다면 새 버전의 서포트 라이브러리가 당신을 반길 것입니다.

나는 3년 넘게 ribot팀에서 안드로이드 앱 개발을 해왔습니다. 이 기간 동안 사용된 아키텍처와 기술을 지속적으로 발전시켜 왔습니다. 이 글에서는 이러한 아키텍처를 적용하면서 얻은 노하우와 학습방법을 설명할 계획입니다.

이전

2012년 코드 베이스는 기본 구조를 따랐습니다. 네트워킹 라이브러리를 사용하지 않고 AsyncTask를 사용하여 직접 구현하였습니다. 아래 다이어그램은 아키텍처의 대략적인 모습을 보여줍니다.


이 코드는 두 개의 레이어로 구성되어 있습니다.

  • Data Layer: REST API 및 데이터 저장소에서 데이터를 검색/저장하는 역할을 담당.
  • View Layer: UI에서 데이터를 처리하고 표시.

API Provider는 Activity 및 Fragment를 REST API와 쉽게 상호 작용할 수 있게 하는 메서드를 제공합니다. 이러한 메서드는 URLConnection 및 AsyncTask를 사용하여 별도의 스레드에서 네트워크 호출을 수행하고 콜백을 통해 결과를 변환합니다.

비슷한 방식으로 CacheProvider는 SharedPreferences 또는 SQLite에서 데이터를 검색하고 저장하는 역할을 합니다. 또한 콜백을 사용하여 결과를 Activity로 다시 전달합니다.

문제점

이 접근 방식의 큰 문제점은 View Layer가 너무 많은 책임을 가지고 있는 것입니다. 앱이 블로그 게시물의 목록을 로드하고 SQLite 데이터베이스에 저장하고 불러오게끔 캐싱 한 다음 ListView에 표시하는 간단한 일반적인 시나리오를 생각해보십시오.

Activity는 다음과 같은 작업을 합니다.

  1. APIProvider에서 loadPosts(callback) 메서드를 호출합니다.
  2. APIProvider 성공 콜백을 기다린 다음 CacheProvider에서 savePosts(callback)를 호출합니다.
  3. CacheProvider 성공 콜백을 기다렸다가 ListView에 게시물을 표시합니다.
  4. APIProvider 및 CacheProvider에서 발생할 수 있는 두 가지 에러 콜백을 별도로 처리합니다.

 

이것은 아주 간단한 예입니다. 실제 시나리오에서는 REST API가 뷰에서 필요로 하는 데이터만 주지 않습니다. 따라서 Activity는 데이터를 표시하기 전에 필요로 하는 정보로 재 가공해야 합니다. 또 다른 일반적인 경우는 loadPosts() 메서드가 PlayServices SDK에서 제공하는 이메일 주소와 같이 다른 곳에서 가져와야 하는 매개 변수를 사용하는 경우입니다. SDK가 콜백을 사용하여 이메일을 비동기적으로 반환할 가능성이 있습니다. 이런 시나리오인 경우 3번의 중첩된 콜백이 됩니다. 복잡성을 계속 더해가면 이러한 접근방식은 콜백 지옥이 됩니다.

요약하면:

  • Activity와 Fragment의 코드가 많아질수록 유지보수가 어렵습니다.
  • 중첩된 콜백이 많으면 코드를 변경하거나 새로운 기능을 추가하기 어렵고 이해하기가 어렵습니다.
  • Activity와 Fragment에 많은 로직들이 구현되어 있어 유닛 테스팅은 가능하지만 어렵습니다.

 

RxJava가 주도하는 새로운 아키텍처

우리는 약 2년 동안 이전의 접근 방식을 사용해왔습니다. 그동안 위에서 설명한 문제를 약간 완화한 몇 가지 개선 사항을 만들었습니다. 예를 들어 Activity와 Fragment의 코드를 줄이기 위해 몇 가지 Helper 클래스를 추가했으며 APIProvider에서 Volley를 사용하기 시작했습니다. 이러한 변화에도 불구하고 코드는 아직 테스트 친화적이지 못했으며 콜백 지옥 문제는 여전히 자주 발생했습니다.

2014년 RxJava에 대한 기사를 읽기 시작했습니다. 몇 가지 샘플 앱을 만들어 보면서 이것이 중첩된 콜백 문제에 대한 해결책이 될 수 있다는 생각을 했습니다. 반응형 프로그래밍에 익숙하지 않다면 이 소개글을 읽어 보세요. 즉, RxJava를 사용하면 비동기 스트림을 통해 데이터를 관리할 수 있으며, 데이터를 변환 및 필터링 또는 결합을 위해 많은 Operations를 사용할 수 있습니다.

지난 몇 년간 우리가 겪었던 고통을 고려해 볼 때 새로운 앱의 아키텍처가 어떻게 보이는지 그려지기 시작했습니다. 그래서 우리는 이것을 도입하였습니다.

첫 번째 접근 방식과 마찬가지로 이 아키텍처는 Data와 View Layer로 분리될 수 있습니다. Data Layer는 DataManager와 Helper를 포함합니다. View Layer는 Activity, Fragment, ViewGroup 등과 같은 Android 프레임 워크의 구성 요소로 구성됩니다.

Helper 클래스(다이어그램의 3번째 열)는 매우 구체적인 책임을 지며 간결한 방식으로 구현합니다. 예를 들어 대부분의 프로젝트에는 REST API 액세스, 데이터베이스에서 데이터 읽기 또는 SDK와의 상호 작용 위한 Helper가 있습니다.

가장 일반적인 것은 다음과 같습니다.

  • PreferencesHelper: SharedPreferences에서 데이터를 읽고 저장합니다.
  • DatabaseHelper: SQLite 데이터베이스 액세스를 처리합니다.
  • Retrofit Service: REST API에 대한 호출을 수행합니다. RxJava를 지원하기 때문에 Volley 대신 Retrofit을 사용하기 시작했습니다. 또한 사용하는 것이 더 좋습니다.

Helper 클래스 안에 있는 public 메서드의 대부분은 Observable을 리턴합니다.
DataManager는 아키텍처의 뇌에 해당합니다. RxJava 연산자를 광범위하게 사용하여 Helper클래스에서 검색 한 데이터를 결합, 필터링 및 변환합니다. DataManager의 목적은 변환이 필요하지 않은 데이터를 제공하여 Activity나 Fragment에서 작업량을 줄이는 것입니다.

아래 코드는 DataManager 메서드가 어떻게 보이는지 보여줍니다. 아래 예제 메서드는 다음과 같이 작동합니다.

  1. Retrofit Service를 호출하여 REST API에서 블로그 게시물 목록을 로드합니다.
  2. DatabaseHelper를 사용하여 캐싱을 위해 로컬 데이터베이스에 게시물을 저장합니다.
  3. View Layer에서 표시하고자 하는 블로그 게시물만 필터링하므로 오늘 작성된 블로그 게시물만 표시됩니다.


Activity 또는 Fagrment과 같은 View Layer의 구성 요소는 이 메서드를 호출하고 리턴된 Observable를 통해 RecyclerView에 직접 표시할 수 있습니다.

이 아키텍처의 마지막 요소는 이벤트 버스입니다. 이벤트 버스를 사용하면 Data Layer에서 발생하는 이벤트를 브로드캐스트 할 수 있으므로 View Layer의 여러 구성 요소가 이러한 이벤트를 캐치(Subscriptions) 할 수 있습니다. 예를 들어, Observable이 완료되면 DataManager의 signOut() 메서드는 브로드캐스트 이벤트 보내고 이 브로드캐스트를 수신하고 있는 Activity는 UI를 변경하여 로그아웃 상태를 표시할 수 있습니다.

이 방법이 왜 더 좋습니까?
RxJava Observable과 Operators는 중첩된 콜백이 필요 없습니다.

  • DataManager는 이전에 View Layer의 일부였던 책임을 담당합니다. 따라서 Activity와 Fagrment를 더욱 가볍게 만듭니다.
  • Activity, Fagrment에서 DataManager, Helper로 코드를 이동하면 유닛 테스트 작성이 쉬워집니다.
  • DataManager는 Data Layer의 유일한 상호 작용점이기 때문에 테스트 친화적입니다. Helper 클래스 또는 DataManager는 쉽게 변경 가능합니다.

 

여전히 문제점이 있습니까?

  • 크고 복잡한 프로젝트의 경우 DataManager가 너무 커져 유지 관리가 어려울 수 있습니다.
  • Activity 또는 Fagrment와 같은 View Layer의 구성 요소가 더 가벼워졌지만 RxJava Subscriptions 관리, 에러 탐지 등과 관련하여 상당한 양의 작업을 처리해야 합니다.

 

Model View Presenter 통합

작년(2014년)에 MVP 또는 MVVM과 같은 몇 가지 아키텍처 패턴이 Android 커뮤니티에서 인기를 얻었습니다. 샘플 프로젝트기사에서 이러한 패턴을 조사한 결과, MVP가 기존 접근법에 매우 중요한 개선을 가져올 수 있다는 것을 발견했습니다. 현재 아키텍처가 두 개의 레이어(View Layer, Data Layer)로 나누어졌기 때문에 MVP를 추가하는 것이 자연스러웠습니다. 새로운 Presenter Layer를 추가하고 코드의 일부를 View에서 Presenter로 옮겨야 했습니다.

Data Layer는 그대로 유지되지만 패턴 이름과의 일관성을 유지하기 위해 이제는 Model이라고 부릅니다.

Presenter는 Model에서 데이터를 로드하고 결과가 준비되면 View의 설정된 메서드를 호출하는 역할을 담당합니다. 데이터 관리자가 리턴한 Observables에 subscribe 합니다. 따라서 schedulerssubscriptions과 같은 것들을 처리해야 합니다. 또한 필요에 따라 에러 코드 탐지를 하거나 데이터를 재가공하는 등 추가 작업을 할 수 있습니다. 예를 들어 일부 데이터를 필터링해야 하고 이 필터를 다른 곳에서 재사용할 가능성이 없는 경우 데이터 관리자가 아닌 Presenter에서 구현하는 것이 더 적합할 수 있습니다.

아래에서 Presenter의 public 메서드는 어떤 것이 있는지 확인할 수 있습니다. 이 코드는 이전 섹션에서 정의한 dataManager.loadTodayPosts() 메서드에서 반환 한 Observable을 subscribe 합니다.

mMvpView는 이 Presenter가 지원하는 View 구성 요소입니다. 일반적으로 MVP View는 Activity, Fragment 또는 ViewGroup의 인스턴스입니다.
이전 아키텍처와 마찬가지로 View Layer에는 ViewGroup, Fragment 또는 Activity와 같은 안드로이드 프레임 워크 구성 요소가 포함되어 있습니다. 주요 차이점은 이러한 구성 요소가 Observables에 직접 subscribe하지 않는다는 것입니다. 대신 MvpView 인터페이스를 구현하고 showError() 또는 showProgressIndicator()와 같은 간결한 메서드 목록을 제공합니다. 또한 View 구성 요소는 클릭 이벤트와 같은 사용자 상호 작용을 처리하고 그에 따라 설정된 메서드를 Presenter에서 호출하여 작동합니다. 예를 들어 게시물 목록을 로드하는 버튼이 있는 경우 Activity는 onClickListener에서 presenter.loadTodayPosts()를 호출합니다.

이 MVP기반 아키텍처의 전체 예제를 보려면 GitHub의 Android Boilerplate 프로젝트를 확인하거나 ribot의 아키텍처 가이드라인에서 자세한 내용을 읽을 수 있습니다.

이 접근법이 더 좋은 이유는 무엇인가요?
Activity와 Fragment는 매우 가볍습니다. 이것의 유일한 책임은 UI를 설정하거나 업데이트를 하고 사용자 이벤트를 처리합니다. 따라서 유지 관리가 더 쉬워집니다.
이제 View Layer를 변경해도 Presenter를 위한 단위 테스트를 쉽게 작성할 수 있습니다. 이전 코드는 View Layer의 일부가 포함되어 단위 테스트가 쉽지 않았습니다. 전체 아키텍처는 매우 테스트 친화적입니다.
DataManager가 점점 커질 경우 일부 코드를 Presenter로 이동하여 이 문제를 완화할 수 있습니다.

여전히 문제점이 있습니까?
코드가 매우 크고 복잡해지면 단일 데이터 관리자를 갖는 것이 여전히 문제가 될 수 있습니다. 이것이 실제로 문제가 되는 지점에 도달하지 못했지만, 일어날 수 있다는 것을 알고 있습니다.

이것은 완벽한 아키텍처가 아니라는 점은 명백한 사실입니다. 모든 문제를 영원히 해결할 수 있는 완벽한 것이 있다고 생각하지 않는 것이 속 편할 것입니다. Android 생태계는 빠른 속도로 발전해 나갈 것이며 우수한 Android 앱을 계속 개발할 수 있는 더 나은 방법을 찾을 수 있도록 계속 실험해야 합니다. 이 글을 즐겁게 읽었으면 좋겠습니다.

안드로이드 올바른 스플래시 화면을 구현하기 위한 방법

많은 실험 과정 속에서 안드로이드 스플래시 화면 구현에 대해 좋은 방법을 발견하였습니다. 보통 스플래시 화면 구현은 Activity에서 Runnable을 통해 시간을 지연하는 방식으로 구현합니다. 이는 약간의 문제점을 발생시킵니다.

실제로 스플래시 동안 아무런 작업을 하지 않지만 사용자는 무작정 기다려야 하는 문제가 있습니다. 또한 Application이나 Launch Activity에 많은 작업이 이루어진다면 Activity에서 레이아웃 인플레이트가 지연되어 스플래시 화면이 늦게 뜨는 문제점이 발생됩니다.

이해를 돕기 위해 문제가 되는 앱을 하나 보겠습니다. 메가박스 앱의 경우 실행할 때 검은 화면으로 덮인 후에 스플래시 화면이 뜨는 것을 볼 수 있습니다.

이런 문제점을 보완하여 앱을 로드하는 동안 스플래시 화면을 보여주기 위한 멋진방법을 소개해드리겠습니다.

코드

먼저 스플래시 화면을 사용한 레이아웃을 Drawable로 다시 디자인합니다. layer-list를 이용하여 배경 색상과 로고 이미지를 배치합니다.

이제 Activity에 사용할 테마의 windowBackground값에 이전에 만든 Drawable로 바꿔줍니다.

안드로이드 매니페스트에서 런치 될 Activity의 테마를 위에서 만든 스플래시 테마로 설정합니다.

Activity에서 onCreate전 setTheme를 이용하여 원래의 테마로 돌립니다.

Activity가 실행되기 전에 테마의 Background를 Drawable로 구성된 스플래시 화면을 보여주며 Activity가 생성되고 레이아웃 인 플레이트 되기 전 원래의 테마로 돌려줌으로써 스플래시 화면을 위한 Activity를 새롭게 생성할 필요가 없습니다.

이렇게 간단한 리소스를 이용한다면 앱의 초반 로딩 속도가 늦는 경우 로딩 프로그래스 대신 스플래시 화면을 사용하는 것도 나쁘지 않은 선택이 될 것입니다.

모든 코드는 Github에 공개되어있습니다.

RecyclerView DiffUtil로 성능 향상하기

Smart way to update RecyclerView using DiffUtil을 번역한 글입니다.

이제 notifyDataSetChanged()는 더 이상 쓰지마세요!

우리는 리스트를 매일 사용합니다. 사용자가 목록을 스크롤 할때 데이터를 업데이트 해야합니다. 이를 위해 서버에서 데이터를 가져와서 아이템을 업데이트 합니다.
이런 과정에서 지연이 길어지면 UX에 영향을 미치기 때문에 가능한 적은 리소스와 함께 빠른 작업이 이루어져야 합니다. 목록의 내용이 변경되면 notifyDataSetChanged()를 호출하여 아이템을 업데이트하지만 비용이 많이듭니다. RecyclerView에서 데이터를 업데이트 처리를 효율적으로 작업하기위해 DiffUtil 클래스가 개발되었습니다.

DiffUtil?

RecyclerView Support Library v7의 24.2.0버전에 DiffUtil이라는 매우 편리한 유틸리티 클래스가 포함되었습니다. 이 클래스는 두 목록간의 차이점을 찾고 업데이트 되어야 할 목록을 반환해줍니다. RecyclerView 어댑터에 대한 업데이트를 알리는데 사용됩니다.
Eugene W. Myers’s의 차이 알고리즘을 이용하여 최소한의 업데이트 수를 계산합니다.

어떻게 사용하나?

DiffUtil.Callback은 추상 클래스이며 두 목록 간의 차이를 계산하는 동안 DiffUtil에 의해 콜백 클래스로 사용됩니다. 4개의 추상 메소드와 1개의 비추상 메소드로 이루어져있습니다. 이를 확장하고 모든 메소드를 오버라이드해야 합니다.

  • getOldListSize(): 이전 목록의 개수를 반환합니다.
  • getNewListSize(): 새로운 목록의 개수를 반환합니다.
  • areItemsTheSame(int oldItemPosition, int newItemPosition): 두 객체가 같은 항목인지 여부를 결정합니다.
  • areContentsTheSame(int oldItemPosition, int newItemPosition): 두 항목의 데이터가 같은지 여부를 결정합니다. areItemsTheSame()이 true를 반환하는 경우에만 호출됩니다.
  • getChangePayload(int oldItemPosition, int newItemPosition): 만약 areItemTheSame()이 true를 반환하고 areContentsTheSame()이 false를 반환하면 이 메서드가 호출되어 변경 내용에 대한 페이로드를 가져옵니다.

다음은 EmployeeRecyclerViewAdapter 및 EmployeeDiffCallback에서 직원 목록을 정렬하는데 사용하는 간단한 Employee클래스입니다.

다음은 Diff.Callback 클래스의 구현입니다. getChangePayload()가 추상 메소드가 아님을 알 수 있습니다.

DiffUtil.Callback 구현이 완료되면 아래 설명 된대로 RecyclerViewAdapter의 목록 변경사항을 업데이트 해야합니다.

DiffUtil.dispactUpdatesTo(RecyclerView.Adapter adapter)를 호출 하여 업데이트할 Adapter를 전달하세요. diff계산에서 반환된 DiffResult 객체가 변경사항을 Adapter에 전달하고 어댑터가 변경 사항에 대해 알림을 받습니다.
getChangePayload()에서 반환 된 객체는 notifyItemRangeChanged(position, count, payload)를 DiffResult에서 호출하여 결론적으로 onBindViewHolder(… List<Object> payloads) 메서드가 호출되어 목록이 업데이트 됩니다. 이때 업데이트될 목록은 정말 업데이트가 필요한 아이템만 호출된다는 점입니다.

DiffUtil은 RecyclerView.Adapter의 다양한 데이터 업데이트 메서드를 사용하여 알립니다.

  • notifyItemMoved()
  • notifyItemRangeChanged()
  • notifyItemRangeInserted()
  • notifyItemRangeRemoved()

RecyclerView.Adapter 및 해당 메소드에 대한 자세한 내용은 여기에서 읽을 수 있습니다.

중요

목록이 많으면 작업에 상당한 시간이 걸릴 수 있으므로 백그라운드 스레드에서 실행하고 DiffUtil.DiffResult를 가져와서 메인스레드(UI스레드)의 RecyclerView에 적용세요. 또한 구현 제약으로 목록의 최대 크기는 2²⁶개로 제한되어 있습니다.

성능

DiffUtil은 두 목록 간의 추가 및 제거 작업의 최소 수를 찾기 위해 O(N) 공간이 필요합니다. 예상되는 성능은 O(N + D²)입니다.
N: 추가 및 제거 된 항목의 총 수
D: 스크립트 길이

더 많은 성능 수치를 보려면 Android의 공식 페이지를 살펴볼 수 있습니다. 위의 DiffUtil 예제의 참조 구현을 GitHub에서 찾을 수 있습니다.

안드로이드 개발 스킬을 향상시키는 오픈소스 앱 20가지

이 글은 20+ Awesome Open-Source Android Apps To Boost Your Development Skill의 핵심적인 부분을 이해하기 쉽도록 의역하였습니다.

읽는다는 것은 배움에 있어서 가장 좋은 방법입니다. 개발자에게도 마찬가지입니다. 더 발전하는 개발자가 되고 싶다면 많은 코드를 읽어야 합니다. 이것 만큼이나 간단한 방법은 없습니다.

책, 블로그, 커뮤니티에도 좋은 읽을거리가 많이 올라오지만 하나의 앱을 작동하는 전체 앱은 훨씬 더 완벽하게 배울 수 있습니다. 이 글을 통해 다양한 카테고리 및 장르 중 최고의 오픈소스 안드로이드 앱을 소개하고 학습에 필요한 부분에 대해 살펴보겠습니다. Play 스토어에서 직접 앱을 사용해 보고 구현된 코드를 직접 볼 수 있습니다. 각 앱에 첨부된 난이도를 통해 자신의 역량에 따라 선택하셔서 보시길 추천합니다.

LeafPic

(Github | Play 스토어 | 난이도: 초급)

안드로이드 기기에서 찾아볼 수 있는 가장 흔한 사진 및 동영상 갤러리 앱입니다. 오픈소스로 공개되어 있는 최고의 갤러리 앱 중 하나입니다. 아주 간단하며 이해하기 쉽기 때문에 초급 개발자에게 적합합니다. 또한 동적 테마를 구현 방식을 사용하기 때문에 이와 관련된 기능을 배울 수 있습니다.

Simple Calendar

(Github | Play 스토어 | 난이도: 초급)

Kotlin을 사용하여 작성된 간단하고 사용하기 쉬운 달력앱입니다. Kotlin을 배우고자 한다면 가장 좋은 방법 중 하나가 될것 입니다. 이 프로젝트에서 배울 수있는 점은 완전히 새로운 언어로 개발해 볼 수 있다는 것과 안드로이드 위젯을 만들수 있다는 것입니다.

Amaze File Manager

(Github | Play 스토어 | 난이도: 중급)

거의 모든 안드로이드 기기에서 볼 수 있는 파일 관리자 앱입니다. 파일 관리자 앱은 모든 안드로이드 플랫폼과 모든 기기에서 사용할 수 있는 멋진 호환성을 자랑합니다. 특히, 플랫폼 버전에 따라 SD카드 마운트를 처리하는 방법을 배울 수 있습니다. 그러나 이 프로젝트에서 사용된 코딩 표준을 따르지 않는 것이 좋습니다.

Easy Sound Recorder

(Github | Play 스토어 | 난이도: 초급)

간단하고 사용하기 쉬운 사운드 레코드 앱입니다. 안드로이드에서 오디오 녹음과 컨트롤에 대해 배우고 싶다면 처음으로 접하기 가장 좋은 프로젝트입니다.
매우 작은 규모의 프로젝트로 이해하기도 매우 쉽습니다. 덤으로 머티리얼 디자인의 기초를 배울 수도 있습니다.

MLManager

( Github | Play 스토어 | 난이도: 초급)

MLManager는 안드로이드 기기를 위한 앱 관리 기능을 가진 앱입니다. 기기에 설치 괸 앱에 대한 상세한 정보를 얻거나 APK를 추출하며, 제거 기능을 배울 수 있습니다. 코딩 표준을 잘따라 작성되었으며, 머티리얼 디자인 가이드라인을 적용하여 심플한 모습을 갖추었기 때문에 좀 더 좋은 아이디어를 얻을 수도 있습니다.

PhotoAffix

(Github | Play 스토어 | 난이도: 초급)

사진을 가로또는 세로로 회전하여 편집할 수 있는 매우 간단한 앱입니다. 안드로이드 개발의 기초를 배우기 위해 적합하며 코딩 표준을 잘 지키고 있습니다. 기초를 바탕으로 나중에 복잡한 사진 뷰어를 만들기 위해 유용한 방법은 많이 배울 수 있습니다.

MovieGuide

(Github | 난이도: 중급)

영화 예고편 및 리뷰로 인기 있는 영화를 보여주는 간한단 앱입니다. MVP모델을 도입하였으며 클린 아키텍처를 돕는 RxJava, Dagger2를 사용하였습니다. 매우 간단한 프로젝트이지만 최신 유행 중인 기술을 배울 수 있는 좋을 기회를 제공합니다.

AnExplorer

(Github | Play 스토어 | 난이도: Intermediate)

파일 관리자의 앱이며 태블릿 지원을 위해 필요한 사항들을 배울 수 있습니다. 파일 처리, Rooting 관리, Loader, 커스컴 View 등을 배울 수 있습니다.

Minimal ToDo

(Github | Play 스토어 | 난이도: 초급)

초보자에게 아주 적합한 프로젝트입니다. 안드로이드의 기본적인 부분을 대부분 배울수 있습니다. 단, 코딩 표준은 지켜지지 않았으며 패키지 구조 또한 배우지 말아야 합니다.

Timber

(Github | Play 스토어 | 난이도: Advanced)

아름답게 디자인된 안드로이드 음악 재생앱입니다. 로컬 음악을 보여주고 음악을 재생합니다. 이 프로젝트는 매우크며 초보자에게는 어려우며 중급자 이상이면 배울만할것 입니다.

AnotherMonitor

(Github | Play 스토어 | 난이도: 중급)

안드로이드 프로세스, 메모리 사용량, CPU사용량 및 이와 관련된 것들을 모아 모니터링 하는 프로젝트입니다. 이해하기 쉽고 작지만 코딩 표준, 아키텍처 및 전체 디자인은 배우지 말아야할 부분입니다.

InstaMaterial

(Github | 난이도: 초급)

머티리얼 디자인 가이드를 잘 적용하여 관련된 스킬을 배우기 좋은 프로젝트입니다. Instargram 앱의 일부를 동일하게 구현하려고 시도했으며, 머티리얼에 사용된 애니메이션, 트렌지션을 배울 수 있습니다. 이해하기 쉬우며 간단하며 설계 능력을 향상하기 위해 이상적입니다.

CoCoin

(Github | 난이도: 쉬움)

개인 금융및 회계솔루션 프로젝트입니다. 데이터 관리하는 방법과 그 데이터를 바탕으로 아름다운 차트를 커스컴 View를 통해 보여줍니다.

OmniNotes

(Github | Play 스토어 | 난이도: 중급)

Evernote와 같은 완벽한 기능을 가진 노트작성 앱입니다. 노트 공유및 검색, 이미지, 비디오, 오디오를 첨부 할 수 있으며, 스케치, 미리알림등 많은 기능이 구현되어 있는 상당히 큰 규모의 프로젝트입니다. 또한 Google Now와 완벽히 통합된다는 점에서 많은 것을 배울 수 있습니다.

Clip Stack

(Github | Play 스토어 | 난이도: 초급)

클립보드(복사) 관리자 앱입니다. 프로젝트는 아주 단순하며 작습니다. 프로젝트에 사용된 패키지 구조나 아키텍쳐 등은 표준이 아니기 때문에 배우지 않았으면 좋겠습니다. 이 프로젝트는 안드로이드의 기본 기능을 잘 활용한 좋은 아이디어를 바탕으로 매우 간단하게 만들어졌다는 점에서 배울만합니다.

Super Clean Master

(Github | 난이도: 고급)

클린 마스터는 안드로이드 유저라면 한 번씩 사용해본 경험이 있을 것입니다. 성능 향상을 위해 불필요한 데이터를 정리하는 앱입니다. 프로젝트 전반적으로 약간 복잡하고 코드를 이해하기 위해 약간의 시간이 필요합니다. 고급자에게 추천합니다.

Travel Mate

(Github | 난이도: 중급)

여행 기반의 앱으로 위치와 지도에 대해 많은 것을 배울 수 있는 프로젝트입니다. 디자인과 코드품질을 그렇게 좋지는 않지만 지도와 위치정보를 어떻게 다루는지에 대해 초보자와 중급자들에게는 많은 배움거리가 될 것 같습니다.

KISS

(Github | Play 스토어 | 난이도: 중급)

정말 가벼운 런처 응용 프로그램입니다. 런처를 만들고 싶다면 이것으로 시작하기좋습니다.

Turbo Editor

(Github | Play 스토어 | 난이도: 중급)

심플한 텍스트 편집 앱입니다. 프로그래밍 언어 구문에 대해 보기편한 뷰어를 지원합니다. 큰 텍스트 파일인경우 한번에 메모리에 올리지 않고 구간별로 잘라서 처리하는 섬세함까지 보여줍니다.

Wally

(Github | 난이도: 초급)

웰페이퍼(배경화면) 앱입니다. 매우 간단하지만 웰페이퍼앱을 만들고 싶은 초보 개발자들에게 매우 유용합니다.

Pedometer

(Github | 난이도: 초급)

하드웨어 센서를 이용하여 만보계의 기능을 구현한 앱입니다. 최적화를 통해 기기의 배터리 성능에 거의 영향을 주지 않습니다. 좋은 프로젝트이긴 하나 코딩 표준과 디자인면에서는 좋지 않습니다.

안드로이드의 기본적인 기능을 구현하기 위해 필요한 대부분의 장르의 오픈소스를 몇 가지 살펴보았습니다. 이 오픈소스를 통해 개발 스킬을 향상하는데 도움이 되었으면 합니다.