31
“MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” Charles

“MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

Page 1: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

“MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기”

Charles

Page 2: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

발표순서�

- 요구사항 분석 - 구현에 필요한 기술검토(MVVM, Data Binding, Paging) - 핵심 코드 설명 - 퍼포먼스 비교

Page 3: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

이미지�피커�요구사항

•사용자�기기에�저장된�이미지�목록을�불러온다.��

•불러온�이미지는�격자무늬(Grid)�형태로�표현�한다.��

•복수개의�이미지�선택할�수�있어야�한다.�

3

�->�ContentResolver를�통해�MediaStore에�질의(Cursor�반환)�

��->�RecyclerView�+�GridLayoutManager��

�->�선택된�아이템을�저장할�자료구조�고려�및�구현

Page 4: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

개발자�관점에서�추가적으로�고려해야�할�내용

•변화에�능동적인�구조�만들기�(확장성↑,�재사용성↑)��

•빠른�퍼포먼스�보장하기

4

�->�아키텍처�패턴�적용��

�->�Paging�라이브러리�적용

4

Page 5: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

MVVM�패턴이란?

Model,�View,�View�Model�약자를�딴�디자인�패턴의�한�종류

5

•Model�:�데이터�저장,수정,삭제�관리.�네트워크�통신�기타�등등��->�Entity,�Repository,�DB,�Util�클래스�등,�그�외�모든것�

•View�:�UI�조작을�담당��->�Activity,�Fragment,�View,�ViewDataBinding�등�

•ViewModel�:�View에�표현할�데이터를�Model로�부터�가져와�가공하고�관리한다.��->�AAC�ViewModel�클래스,�BaseObservable�등�

구성요소가 하는 일

Page 6: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

MVVM�패턴�특징1

6

• View로부터 완전히 독립된 ViewModel을 사용하는 패턴

View ViewModel유닛 테스트, 재사용, 확장 쉬워짐

난 너 알아난 너 몰라!

Page 7: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

MVVM�패턴�특징2

7

• View와 ViewModel은 1:N의 관계를 갖을 수 있다.

View ViewModel

View

View

Page 8: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

MVVM�패턴�특징3

8

• UI변경에 강건한 구조

디자인 좀 바꾸고 싶은데요..

일정에 차질이 생깁니다. 안됩니다.네 가능합니다.

Page 9: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

AAC�ViewModel�(Android�Architecture�Component�ViewModel)

9

val�imageViewModel�=�ViewModelProvider(LifecycleOwner,�ViewModelProvider.Factory).get(ImageViewModel::class.java)

AAC ViewModel 인스턴스 생성

LifecycleOwner�:�생명주기를�담당하는�주체,�일반적으로�액티비티�또는�프레그먼트

ViewModelProvider.Factory�:�ViewModel�인스턴스�생성을�담당

• AAC ViewModel은 Configuration Change가 발생해도 뷰모델의 상태를 유지해준다. • 범위내에서 데이터 공유가 쉽다 • LifecycleOwner의 생명주기를 알고있다.

특징

Page 10: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

AAC�ViewModel�제약사항

• 절대로 Context를 참조해서는 안된다. (Activity도 Context의 한 종류)

10

ViewModelActivity

Garbage Collector

Activity재생성

유지

화면회전

메모리 누수!파괴

Page 11: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

데이터�바인딩

데이터�바인딩�정의(in�Wiki)�

data�binding�is�a�general�technique�that�binds�data�sources�from�the�provider�and�consumer�together�and�synchronizes�them.

<layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <variable name="viewmodel" type="com.myapp.data.ViewModel" /> </data> <ConstraintLayout... /> <!-- UI layout's root element --> </layout>

activity_main.xml 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.javaactivity_main.xml ActivityMainBinding.java

11

->�데이터소스와�UI컴포넌트의�상태를�일치시키는�기술,�(findViewById�대용�X)��

Page 12: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

12

ViewModel ViewViewDataBinding

12

ViewModel에�View를�바인딩하자

UI컴포넌트(액티비티 or 프레그먼트)

class MainActivity : AppCompatActivity{ val viewModel : ViewModel val binding : ActivityMainBinding

@Override protected void onCreate(Bundle savedInstanceState) { … binding.viewModel = viewModel } }

Page 13: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

구글에서�권장하는�아키텍처

Model

ViewModel

View

13

Page 14: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

아키텍처

14

Page 15: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

페이징�라이브러리�개요

•사용자에게�보여지는�일부�데이터만�로드��

•네트워크�대역폭,�시스템�리소스�사용량을�절감

15

Page 16: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

페이징�라이브러리�핵심�컴포넌트

16

DataSource-> 요청된 데이터를 PagedList에 전달한다.

PagedList -> Immutable한 데이터를 Lazy로딩함

PagedListAdapter

-> 백그라운드에서 PagedList의 로딩을 관찰하고 비교한 뒤 RecyclerView에 데이터를 표현

Page 17: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

DataSource의�종류(https://www.charlezz.com/?p=599)

PositionalDataSource��

포지션�기반으로�데이터들을�불러온다.�고정된�사이즈를�갖는�데이터셋을�페이징�하는데�적합

17

PageKeyedDataSource��

페이지�기반으로�데이터를�불러온다.�페이지�단위의�키를�통해�이전�페이지와�다음페이지를�가져온다.�

ItemKeyedDataSource��

아이템의�아이디를�기반으로�데이터들을�불러온다.�로드된�아이템의�키가�다음�및�이전�데이터를�페이징�하기�위해�참조된다.�

Page 18: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

핵심�코드�설명�

https://github.com/sabujak-sabujak/Pickle

Page 19: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

MediaStore에�질의하기(이미지�목록�가져오기)

19

val projection = arrayOf(

MediaStore.Images.ImageColumns._ID,

MediaStore.Images.ImageColumns.BUCKET_ID,

MediaStore.Images.ImageColumns.DATE_ADDED,

MediaStore.Images.ImageColumns.SIZE,

MediaStore.Images.ImageColumns.MIME_TYPE,

MediaStore.Images.ImageColumns.ORIENTATION)

class Image(

val id: Long,

val bucketId: String,

val dateAdded: Long,

val fileSize: Long,

val mediaType: Int,

val orientation: Int,

)val cursor: Cursor? = context.contentResolver.query(

MediaStore.Images.Media.EXTERNAL_CONTENT_URI,

projection,

selection,

selectionArgs,

sortOrder)데이터 베이스 쿼리 결과에 접근할 수 있는 포인터

Page 20: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

MediaStore에�질의하기(이미지�목록�가져오기)

val�id�=�cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID))�

val�bucketId�=�cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.BUCKET_ID))�

val�dateAdded�=�cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_ADDED))�

val�fileSize�=�cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns.SIZE))�

val�mimeType�=�cursor.getString(cursor.getColumnIndex(MediaStore.Images.ImageColumns.MIME_TYPE))�

val�orientation�=�cursor.getInt(cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION))�

val�uri�=�ContentUris.withAppendedId(getContentUri(),�id)�

val�image�=�Image(�

������������id,�bucketId,�

������������dateAdded,�fileSize,�

������������MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE,�

������������mimeType,�orientation,�

������������uri�

)

20

레코드를 하나씩 읽으며 Image 인스턴스로 치환

Page 21: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

DataSource�만들기

class�ImageDataSource(...)�:�PositionalDataSource<Image>()�{��

����private�var�cursor�=�...�

����override�fun�loadInitial(params:�LoadInitialParams,�callback:�LoadInitialCallback<Image>)�{�

��������val�list:List<Image>�=�getMediaList(cursor,�params.requestedLoadSize)�

��������callback.onResult(list,�0,�cursor.count)�����������

����}�

����override�fun�loadRange(params:�LoadRangeParams,�callback:�LoadRangeCallback<Image>)�{�

��������val�list:List<Image>�=�getMediaList(cursor,�params.loadSize)�

��������callback.onResult(list)�

����}�

����private�fun�getMediaList(cursor:�Cursor,�loadSize:�Int):�ArrayList<Image>�{�

��������val�imageList�=�ArrayList<Image>()�

��������…�//�loadSize�만큼�cursor�순회하며�mediaList에�Image�추가�

��������return�imageList�

����}�

}� 21

Position을 Key로 사용

아이템 리스트 전달, 내부에서 PagedList로 변경

Page 22: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

DataSource.Factory�만들기

class�ImageDataSourceFactory(...)�:�DataSource.Factory<Int,�Image>()�{�

����override�fun�create():�DataSource<Int,�Image>�{�

��������val�dataSource�=�ImageDataSource(...)�

��������return�dataSource�

����}�

}

22

초기화 또는 무효화(invalited) 될 때마다 호출

Page 23: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

Repository�만들기�(�LiveData<PagedList<Image>>�반환하기)

class�ImageRepository(…)�{�

����fun�queryImageList():�LiveData<PagedList<Image>>�{�

��������val�factory�=�ImageDataSourceFactory(…)�

��������val�pageSize�=�30�

��������return�LivePagedListBuilder(factory,�pageSize).build()�

����}�

}

23

LiveData<PagedList<T>>를 반환할 수 있도록 도와주는 유틸 클래스

Page 24: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

ViewModel�만들기

class�ImageViewModel(application:�Application,�...)�:�AndroidViewModel(application)�{�

����private�val�imageRepository:�ImageRepository�=�...�

����val�items:�LiveData<PagedList<Image>>�by�lazy�{�

��������imageRepository.queryImageList()�

����}�

����...�

}

24

Repository로부터 데이터를 가져온다

Page 25: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

RecyclerView에�데이터�표현하기

class�ImageFragment�:�Fragment()�{�

����...�

����lateinit�var�viewModel:ImageViewModel�

����lateinit�var�adapter:�ImageAdapter�

����lateinit�var�layoutManager:�GridLayoutManager�

����...�

����override�fun�onViewCreated(view:�View,�savedInstanceState:�Bundle?)�{�

��������super.onViewCreated(view,�savedInstanceState)�

��������binding.lifecycleOwner�=�viewLifecycleOwner�

��������binding.viewModel�=�viewModel�

��������binding.recyclerView.adapter�=�adapter�

��������binding.recyclerView.layoutManager�=�layoutManager�

��������viewModel.items.observe(viewLifecycleOwner,�Observer�{�adapter.submitList(it)�})�

��������...�

����}�

} 25

LiveData를 관찰하여 PagedList에 변경이 있을시 Adapter에 즉각 반영

Page 26: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

선택한�아이템�관리하기

class�SelectionManager(…)�:�BaseObservable()�{�

����val�selectedMap�=�LinkedHashMap<Long,�Image>()�

����fun�setChecked(image:�Image,�checked:�Boolean)�{…}�

����fun�isChecked(id:�Long):�Boolean�{…}�

����fun�toggle(image:�Image)�{…}�

����fun�getCount():�LiveData<Int>�=�…�

}�

26

순서가 보장되는 HashMap

Page 27: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

SelectionManager와�데이터�바인딩�표현식

<—RecyclerView에서�표현되는�ViewHolder용�레이아웃—>�

<layout>�

����<data>�

��������<variable�name=“item”�type=“Image”/>�

��������<variable�name=“selectionManager"�type="SelectionManager"�/>�

����</data>�

����<ConstraintLayout>�

��������<ImageView�…�

������������android:onClick="@{v->selectionManager.toggle(item.media)}"/>�

��������<ImageView�…�

������������android:src="@{selectionManager.isChecked(item.id)?�@drawable/checked_on:@drawable/checked_off}”/>�

����</ConstraintLayout>�

</layout>

27

View를 클릭하면 선택한 아이템으로 지정

선택된 아이템인지 확인하여 체크 유무를 결정

Page 28: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

시연영상�및�퍼포먼스�비교�

Page 29: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml
Page 30: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

Pickle Band Facebook Instagram

2.2초 3.0초 5초 7초

초기화에 걸리는 시간 비교

페이징 컴포넌트를 사용하면 사진 수에 크게 영향 받지 않고 일정한 퍼포먼스를 발휘한다.

Page 31: “MVVM 패턴과 Paging으로 수퍼 빠른 이미지 피커 만들기” · 2020. 5. 17. · 애노테이션 프로세싱에 의한 코드 생성 ActivityMainBinding.java activity_main.xml

Thank You

Questions?

31