반응형

1. 구성요소

1) 기본 구성요소

1–1)LayoutManager

*리스트, 그리드, 스태그드 그리드 등의 배치를 담당하고
*동시에 스크롤도 담당한다.
*아이템의 focus담당 (리스트를 내릴때 새로운 아이템을 추가해야하는지도 알려준다)

1–2) Adapter

*view, viewHoler생성
*item과 viewHolder를 bind
*RecyclerView에게 changes를 notify
*아이템 클릭등의 interaction 핸들링
*viewTypes 분기
*onFailedToRecyclerView를 통해 Recycler복원

2) 내부 구성요소

2 . Recycle 방법

1) 레이아웃 매니저가 getViewForPosition으로 view를 요청

2) RecyclcerView는 캐시에 getViewForPosition으로 확인. 있으면 LayoutManager에게 반환

3) 캐시에 없으면 adapter에게 type이 뭔지 물어보고 Recycled Pool에 getViewHolderByType으로 요청

4) Pool에 있으면 반환, 없으면 adapter에게 createViewHolder로 아이템 생성.

5) 뷰를 찾으면 adapter에서 bindview를 하고 LayoutManager에게 리턴.

6) LayoutManager는 RecyclerView에게 addView를 수행하고 adapter의 onViewAttachedToWindow가 호출됨.

  • 캐시와 Recycled Pool은 RecyclerView안에 이미 선언되어있음.
  • setItemViewCacheSize함수로 캐시 사이즈 변경 가능.

3. childeren 제어 순서

1) recyclerView가 LayoutManager에게 onLayoutChildren을 하면 배치를 시켜줌.
2) recyclerView에서 지워지거나 추가된 childeren들을 조사해서 알맞는 작업을 진행.
3) recyclerView는 ItemAnimator에게 animateChildren을 호출하여 애니메이션 수행
4) ItemAnimator가 동작을 하고 onAnimationFinished를 호출함.
5) adapter에게 onViewDetachedFromWindow를 호출
5) 상황에 따라서 view를 캐시하거나 재활용을 하게 된다.

4. ItemDecoration

1) getItemOffsets함수를 통해 Item의 영역을 늘릴 수 있다.
2) onDraw함수를 통해서 아이템이 그려지기 전에 먼저 그릴 수 있고
3) onDrawOver함수를 통해서 아이템 위에 덮어서 그릴 수 있다. 
4) 주의사항:

  • adapter에 접근하려 하지 말것
  • 그릴때 필요한 정보는 ViewHolder에 넣어둘것
  • viewHolder가 필요할때 recyclerView.getChildViewHolder(view)함수를 통해서 가져올것.

5. 꼭 알아야 할 것

1) 모든 아이템을 notify하는것은 되도록 피해라( notifyItemRangeChanged(0, getItemCount());
 ->해결 방법. notifyItemChanged(3)

2) onBindViewHolder 메소드 안에 final int position을 사용하지 말라. (final인 position은 보장하지 않는다. 위치가 바뀌거나 지워질때 등)
-> 해결방법. holder.getAdapterPosition을 이용해라.

3) holder.getAdapterPosition()해서 가져온 position값이 RecyclerView.NO_POSITION인지 꼭 확인하도록 해라.
-> 방법. if(holder.getAdapterPosition()!=RecyclerView.NO_POSITION)인지 확인하고 필요한 로직 추가.

4) Item Payload를 활용하라.
-> 방법. adapter.notifyItemChanged(position, LIKE_UPDATE); (좋아요 눌렀을때 처리, 타입을 여러개로 나눠서 필요한 호출을 한다.) 
 onBindViewHolder(holder, position, List payloads)를 override해서 사용하고
 payloads.contains(LIKE_UPDATE) 함수로 좋아요를 눌렀는지 판단해서 로직 처리가 가능하다.

5) onCreateViewHolder에서는 새로운 인스턴스만 반환해라.
-> 전역변수로 holder를 keep했다가 캐시처럼 넘기는 오류는 범하지 말라. 항상 new Holder()를 반환하도록 해라.

6) pool을 메모리 캐시에 놓고 모든 recyclerview에 세팅하지 말라.
:context leak를 유발하게 될것.

7) recyclerView adpater는 single쓰레드 기반이다.
->영향을 주는 list들은 main Thread에서만 변경하도록 해야 정상 동작한다.

반응형
,
반응형

일반적으로 하나의 인스턴스만 존재해야 할 경우 Singleton 패턴을 사용하게 된다. 물론 Single Thread에서 사용되는 경우에는 문제가 되지 않지만 Multi Thread 환경에서 Singleton 객체에 접근 시 초기화 관련하여 문제가 있다.

보통 Singleton 객체를 얻는 static 메서드는 getInstance()로 작명하는 게 일반적이다.

그렇다면 어떻게 코드를 작성해야 Singleton 객체를 생성하는 로직을 thread-safe 하게 적용할 수 있을까? 정말 단순하게 별로 신경 쓰고 싶지 않다면 메서드에 synchronized 키워드만 추가해도 무방할 것이다.

그렇지만 좋은 개발자가 되기 위해선 효율적인 코드에 대해서 고민을 해 봐야 한다. 메서드에 Singleton 클래스의 getInstance() 메서드에 synchronized키워드를 추가하는 건 역할에 비해서 동기화 오버헤드가 심하다고 생각한다.

그래서 개발자들 사이에서 Singleton 초기화 관련하여 많은 이디엄들이 연구되었고 몇몇 이디엄들을 소개하려고 한다.


Double Checked Locking

일명 DCL이라고 불리는 이 기법은 현재 broken 이디엄이고 사용을 권고하지 않지만 이러한 기법이 있었다는 걸 설명하고 싶다. 코드는 다음과 같다.


메서드에 synchronized를 빼면서 동기화 오버헤드를 줄여보고자 하는 의도로 설계된 이디엄이다. instance null 인지 체크하고 null 일 경우 동기화 블럭에 진입하게 된다. 그래서 최초 객체가 생성된 이후로는 동기화 블럭에 진입하지 않기 때문에 효율적인 이디엄이라고 생각할 수 있다. 하지만 아주 안 좋은 케이스로 정상 동작하지 않을 수 있다. 그래서 broken 이디엄이라고 하는 것이다.

Thread A와 Thread B가 있다고 하자. Thread A가 instance의 생성을 완료하기 전에 메모리 공간에 할당이 가능하기 때문에 Thread B가 할당된 것을 보고 instance를 사용하려고 하나 생성과정이 모두 끝난 상태가 아니기 때문에 오동작할 수 있다는 것이다. 물론 이러할 확률은 적겠지만 혹시 모를 문제를 생각하여 쓰지 않는 것이 좋다.


Enum

Java에 지대한 공헌을 한 Joshua Bloch가 언급한 이디엄이다. 그냥 간단하게 class가 아닌 enum으로 정의하는 것이다.


Enum은 인스턴스가 여러 개 생기지 않도록 확실하게 보장해주고 복잡한 직렬화나 리플렉션 상황에서도 직렬화가 자동으로 지원된다는 이점이 있다. 하지만 Enum에도 한계는 있다. 보통 Android 개발을 하게 될 경우 보통 Singleton의 초기화 과정에 Context라는 의존성이 끼어들 가능성이 높다. Enum의 초기화는 컴파일 타임에 결정이 되므로 매번 메서드 등을 호출할 때 Context 정보를 넘겨야 하는 비효율적인 상황이 발생할 수 있다. 결론은 Enum은 효율적인 이디엄이지만 상황에 따라 사용이 어려울 수도 있다는 점이다.


LazyHolder

현재까지 가장 완벽하다고 평가받고 있는 이디엄이다. 이 이디엄은 synchronized도 필요 없고 Java 버전도 상관없고 성능도 뛰어나다. 일단 코드를 보면 아래와 같다.


이 이디엄에 대해서 설명을 하자면 객체가 필요할 때로 초기화를 미루는 것이다. Lazy Initialization이라고도 한다. Singleton 클래스에는 LazyHolder클래스의 변수가 없기 때문에 Singleton 클래스 로딩 시 LazyHolder 클래스를 초기화하지 않는다. LazyHolder 클래스는 Singleton 클래스의 getInstance() 메서드에서 LazyHolder.INSTANCE를 참조하는 순간Class가 로딩되며 초기화가 진행된다. Class를 로딩하고 초기화하는 시점은 thread-safe를 보장하기 때문에 volatile이나 synchronized 같은 키워드가 없어도 thread-safe 하면서 성능도 보장하는 아주 훌륭한 이디엄이라고 할 수 있다.

현재까지 LazyHolder에 대해서 문제점은 나타나지 않고 있다. 혹시나 Multi Thread 환경에서 Singleton을 고려하고 있다면 LazyHolder 기법을 이용하자.


반응형
,
반응형

Context의 정의

Interface to global information about an application environment. 
This is an abstract class whose implementation is provided by the Android system. 
It allows access to application-specific resources and classes, as well as up-calls for application-level operations 
such as launching activities, broadcasting and receiving intents, etc.
  • Application 환경에 대한 전역 정보를 접근하기 위한 인터페이스.
  • 추상 클래스이며 실제 구현은 Android 시스템에 의해 제공된다.
  • Context를 통해 어플리케이션에 특화된 리소스나 클래스에 접근할 수 있다.
  • Activity 실행, Intent 브로드캐스팅 그리고 Intent 수신 등과 같은 응용 프로그램 수준의 작업을 수행하기 위한 API를 호출 할 수 있다.

어플리케이션에 관하여 시스템이 관리하고 있는 정보에 접근

  • Context 인터페이스가 제공하는 API 중 getPackageName(), getResource() 등의 메서드

안드로이드 시스템에서 제공하는 API 를 호출할 수 있는 기능

  • startActivity(), bindService() 와 같음 메서드

Context 역할

Context는 어플리케이션과 관련된 정보에 접근하고자 하거나 어플리케이션과 연관된 시스템 레벨의 함수를 호출하고자 할 때 사용된다. 
그런데 안드로이드 시스템에서 어플리케이션 정보를 관리하고 있는 것은 시스템이 아닌, AcitivityManagerService 라는 일종의 또 다른 어플리케이션이다. 
따라서 다른 일반적인 플랫폼과는 달리, 안드로이드에서는 어플리케이션과 관련된 정보에 접근하고자 할 때는 AcitivityManagerService를 통해야만 한다. 
당연히 정보를 얻고자 하는 어플리케이션이 어떤 어플리케이션인지에 관한 키 값도 필요하다.

즉, 안드로이드 플랫폼 관점에서 Context는 다음과 같은 두 가지 역할을 수행하기 때문에 꼭 필요하다.

  • 자신이 어떤 어플리케이션을 나타내고 있는지 알려주는 ID 역할
  • ActivityManagerService에 접근할 수 있도록 하는 통로 역할

Context 생성

Acitivy와 Service가 생성될 때 만들어지는 Context와 BroadcastReceiver가 호출될 때 (onReceive()) 전해지는 Context는 모두 서로다른 인스턴스이다.

즉, Context는 어플리케이션이 시작될 때는 물론, 어플리케이션 컴포넌트들이 생성될 때 마다 생성된다.

새롭게 생성되는 Context들은 부모와 완전히 독립되어 있는 존재가 아니고 ‘거의’ 비슷한 내용을 담고 있다.

Context 종류

안드로이드 프레임워크에서 Context는 2가지 종류로 나뉜다

  • Application Context
  • Activity Context

안드로이드 프레임워크에서 Context는 Application Context와 Activity Context로 구분지을 수 있는데, Application Context는 application life-cycle에 접목되는 개념이고 Activity Context는 activity life-cycle에 접목되는 개념이다. 즉 전자는 하나의 애플리케이션이 실행되어 종료될 때까지 동일한 객체인 반면, 후자는 액티비티가 onDestroy() 된 경우 사라질 수 있는 객체이다. 이를 참고하여 목적에 맞도록 알맞은 종류의 context를 참조해야 한다.

Application Context

Application Context는 애플리케이션 자체의 생명 주기(라이프사이클)에 영향을 받는다. 
따라서 항상 애플리케이션의 생명 주기와 함께한다.

Activity Context

Activity Context는 Activity의 라이프사이클과 함께 작동해 onDestroy()와 함께 사라진다. 
즉, Activity에 대한 환경 정보들이 Context에 있고, 이 Context에 Intent를 통해 다른 액티비티를 띄우면, 액티비티 스택이 쌓이게 된다.

Context Type

Application

실행중인 어플리케이션 프로세스는 Singletone instances 이다. 
Activity이나 Service에서는 getApplication() 메소드를 통해 getApplicationContext()로 Context를 얻어 올 수 있다. 
프로세스내 동일한 instanees를 받게 된다.

Activity/Service

ContextWrapper를 상속받아 구현한 Context이며 메소드로 Context를 얻어 오면 기본 Context로 구성되어 있다. 
프레임워크는 Activity또는 Service가 실행 될때 기본 Context에다 필요한 정보를 warp한다. 
실행시 고유한 Context를 가지게 된다.

BroadcaseReceiver

위의 2가지와 다르게 자기자신이 Context자체는 아니다. 
하지만 onRecevie()시 Context를 가져올 수 있는데, 이때의 Context는 ReveiverRestrictedContext이며 두가지 기능 registerReceiver()와 bindService()를 사용 할 수 없다. 
리시버가 브로드캐스트를 처리 할때마다 새로운 Context가 생성 된다.

ContextProvider

브로드캐스트와 마찬가지로 자기자신이 Context를 상속 받은것은 아니다. 
하지만 액세스후 getContext()를 통해 Context를 가져 올 수 있다. 
ContextProvider가 동일한 응용프로그램에 대해 호출시, 동일한 응용프로그램의 Singletone instances를 반환하게 된다. 
하지만 별도의 프로세스에 있는 경우(서로다른 다른 앱), 프로바이더가 실행되는 응용프로그램의 instances가 반환된다.




Context 참조

LoginActivity.this

LoginActivity.this 는 Acitivity를 상속받은 자신의 클래스를 참조하지만 Activity 또한 Context class를 상속받으므로 activity context를 제공하는데 사용될 수 있다.

getApplication()

getApplication()은 Application 객체를 참조하지만 Application 클래스는 Context 클래스를 상속받으므로 application context를 제공하는데 사용될 수 있다.

getApplicationContext()

getApplicationContext()는 application context를 제공한다.

getBaseContext()

getBaseContext()는 activity context를 제공한다.



반응형
,