Re-stitching Plaid with Kotlin (Android Dev Summit ’18)

[음악 재생] FLORINA MUNTENESCU : 안녕하세요 내 이름은 Florina Muntenescu이고 나는 Google의 개발자 옹호자 그래서 지난 몇 달 동안 3 명과 함께 우리 동료들과 함께, 우리는 격자 무늬를 돌려 놓기 위해 노력해 왔습니다

유행에 그래서 더 정확하게, 격자 무늬는 응용 프로그램입니다 그것은 처음에 동료 인 Nick Butcher에 의해 개발되었습니다 재료 디자인을 보여주는 방법 그리고 여기서 보시는 것은 실제로 2016 년에 앱 상태가 꽤 좋았을 때, 격자 무늬의 영광의 날을 보자

그래서 Nick은 여기에 많은 API를 사용했습니다 애니메이션, 전환, 모든 메소드 벡터에서 drawables 그래서 이들 모두가 앱을 실제로 만들었습니다 그들은 실제로 사용자 경험을 향상 시켰습니다 그래서 Plaid [INAUDIBLE] 데이터는 세 가지 다른 출처에서 왔는데, 그리고 2016 년부터 지금까지의 이러한 자료들은, 음, 그 중 일부는 더 이상 사용되지 않습니다

그래서 그것은 세 가지 근원 중에서, 우리는 하나와 조금 남았습니다 그래서 우리가 가진 멋진 멋진 UI 기능들 모두에서, 우리는 거의 아무것도 남기지 않았습니다 우리는 꽤 지루한 응용 프로그램을 남겼습니다 그래서 우리는 우리가 이것을 좋아하지 않기를 결심했다 우리는이 깨진 기능을 수정하기로 결정했습니다

그러나 이것과 별개로 우리는 우리가 뭔가를 향해 가고 싶었던 아키텍처에서 모듈화되고 확장 가능합니다 관점 하지만 그 점은 Plaid가 UI 샘플로 개발되었지만 아키텍처 샘플로 그래서 당신은 모든 동점을보기 위해 놀랄 것입니다 코드의 종속성, 그리고 음, 실제로 조금 뒤떨어 진 코드입니다

왜냐하면 Nick 2014 년에이를 구축하기 시작했습니다 그 당시에는 앱 아키텍처에 대한 가이드가 없었습니다 우리가 작년에 발표 한 그리고 우리가 코 틀린을 가지고 있었지만, 우리는 실제로 그것을 사용하지 않았습니다 그래서 우리는 Plaid를 다시 만들고 싶다는 것을 알고있었습니다 그러나 우리는 그것을 올바른 방법으로 다시 만들고 싶었습니다

미래의 변화에 ​​대비하여 좋은 상태로 유지해야한다 우리가 만들고 싶었던 그래서이 이야기에서 우리가 배운 것을 말해주고 싶습니다 또한 우리 앱에서 Kotlin을 어떻게 활용할 수 있었는지를 보여줍니다 그래서 우리는이 독창적 인 가이드를 앱 아키텍처에 발표했습니다 그러나 당신이 그것을 읽으면, 당신은 그것이 또한다는 것을 볼 것입니다

여전히 매우 모호합니다 그래서 우리는이 주요 클래스들을 가지고 있습니다 우리는 우리의 응용 프로그램을 설계해야합니다 그러나 우리는 몇 가지 분명한 지침을 만들고 싶다고 결정했습니다 그래서 우리는 앱에서 몇 가지 주요 유형의 클래스를 정의했습니다

우리는 또한 일련의 규칙을 정의했다 각 클래스에 대해 그럼 이들이 어느 것이 었는지 봅시다 그래서 우리는 RemoteDataSource를 정의했습니다 실제로 요청 데이터를 구성하기 만하면됩니다 API 서비스에서 데이터를 가져와야합니다

정보 요청 만하면됩니다 응답이 수신되었습니다 다음으로 우리는 LocalDataSource를 가지며, LocalDataSource의 역할 디스크에 데이터를 저장하는 것입니다 따라서 공유 환경 설정에서이 작업을 수행 할 수도 있습니다 또는 데이터베이스에서

다음으로 저장소를 가지게 될 것이다 데이터를 가져오고 저장하는 것입니다 또한 선택적으로 인 메모리 캐시를 수행 할 수도 있습니다 따라서 저장소가 클래스가됩니다 이는 로컬 데이터 소스와 원격 데이터 소스를 중재합니다

비즈니스 로직이 매우 복잡했기 때문에, 우리는 다른 레이어를 추가하기로 결정했습니다 유스 케이스를 추가하기로 결정했습니다 따라서 유스 케이스의 역할은 데이터를 처리하는 것입니다 비즈니스 로직을 기반으로합니다 작고 가벼운 수업이 될 것입니다

그것도 재사용 될 수 있습니다 따라서 사용 사례는 리포지토리 및 / 또는 기타 용도에 따라 다릅니다 사례 다음에는 뷰 모델이 있습니다 따라서 ViewModel의 역할은 노출하는 것입니다

UI에 의해 표시 될 데이터 및 동작을 트리거하는 데이터 사용자의 작업에 따라 그리고 ViewModel은 유스 케이스에 의존한다 입력으로 ViewModel은 아마도 ID를 얻습니다 따라서 ID가 필요한 경우 예를 들어, 상세 화면 용 ViewModel입니다 그리고 물론, 그것은 사용자의 행동을 입력으로 받아 들일 것입니다 그리고 결과물로서 라이브 데이터를 반환합니다

다음으로, UI에서 우리는 활동과 XML로 작업 할 것입니다 따라서 이들의 역할은 데이터를 표시하는 것입니다 액션을 ViewModel에 전달할 수 있습니다 입력으로 그들은 선택적인 ID를 얻을 것입니다 및 사용자의 행동

그래서 우리는 우리의 애플리케이션이나 아키텍처를 살펴 보았습니다 우리는 이것을 세 개의 층으로 나누었습니다 데이터, 도메인 및 UI 레이어 우리는 한걸음 더 나아가서 좀 더 나아지기로 결정했습니다 우리가 사용하고있는 방식으로 비난 받았다 [? 데이터?] 또는 우리가 사용하고있는 도서관에서 그래서 우리는 LiveData가 실제로 그것이 활동이나 단편과 함께 사용될 때 빛난다

그래서 우리는 실제로 LiveData만을 유지하기로 결정했습니다 ViewModel과 UI 사이에 차이점이 있습니다 그리고 더 좋은 점은 좋은 통합 때문입니다 LiveData와 DataBinding 사이, 우리는 XML에서 DataBinding을 사용하기로 결정했습니다 그러나 다시, 이러한 모든 제약 조건들과 함께 우리가 설정 한 이러한 모든 지침은 우리가 실제로 구현할 수있는 많은 방법들이 있습니다

이런 종류의 건축 그리고 우리는 Kotlin과 Kotlin 언어 기능을 알고있었습니다 이 점을 개선하는 데 도움이 될 것입니다 그리고 더 정확하게, 우리가 특히 좋아할 것 Kotlin이 지원하는 기능적 구성 요소입니다 사실, 우리가 만들어야 할 첫 번째 결정 중 하나 비동기 작업을 처리하는 방법은 무엇입니까? 그리고 우리는 코 루틴으로 작업하기로 결정했습니다

우리 앱의 거의 백본으로 왜냐하면 코 루틴으로 시작하는 것이 쉽기 때문입니다 coroutines 및 응답을 처리합니다 그리고 더 정확하게, 우리가 좋아하는 것 코 루틴에 범위가 있다는 사실입니다 예를 들어, 활동을 시작한다고 가정 해 봅시다 네트워크 요청을 실행 중입니다

당신은 언제 당신이 그 활동을 뒤로 누르고 그 활동을 끝내고, 또한 해당 네트워크 요청을 취소합니다 그래서이 coroutines의 범위 지정 우리가 좋아했던 것이 었습니다 그래서 이것은 우리가 ViewModel에서 결정한 것을 의미합니다 우리가 시작하는 곳이 될거야 우리는 코 루틴을 취소하고 있습니다

coroutine과 LiveData 사이를 전환합니다 그러나 ViewModel 위의 다른 모든 레이어의 경우, 우리는 단지 서스펜션 기능을 사용합니다 그러나 이러한 중지 함수는 결과 클래스를 반환합니다 더 정확하게 말하자면이 결과에는 두 가지 유형이 있습니다 성공 또는 오류

우리가 확신하고 싶었 기 때문입니다 여기저기서 예외를 던지지 않을거야 오히려 이러한 예외, 오류, 국가를 대표한다 그래서 코 틀린에서 흥미로운 것은 클래스를 확장 할 수 있기를 원한다면, 그것을 오픈으로 표시해야합니다 따라서 이것은 기본적으로 클래스가 최종적이라는 것을 의미합니다

상속을 사용할 때는 고의적이어야합니다 그래서 이것은 Kotlin이 정말로이 아이디어를 지원한다는 것을 의미합니다 상속을 선호하는 구성 그러나 오픈 클래스를 사용하는 것보다 더 잘할 수 있습니다 봉인 된 클래스를 사용할 수 있습니다

봉인 된 클래스를 사용하면, 우리는 [INAUDIBLE] 클래스의 클래스를 제한 할 수 있습니다 이는이 파일 밖에서 클래스를 확장 할 수 없다는 것을 의미합니다 우리가이 결과 클래스를 사용할 때가 많았습니다 우리는 일반적으로 그것을 내부에서 사용할 것입니다 그래서, 우선 스마트 캐스트를 지원합니다

그래서 이것은 결과가 성공할 때 쉽게 할 수 있다는 것을 의미했습니다 뭔가해라 결과가 오류이면 다른 작업을 수행하십시오 그러나 우리가 그것을 사용할 때마다 우리가 항상 모든 사건을 다루고 있는지 확인하기를 원했습니다 우리는 실수로 알고 있다면, 우리는 무언가를 처리하지 않고, 컴파일러를 원했습니다

이봐, 너 뭔가 잊어 버렸어 오류 케이스를 처리하는 것을 잊었습니다 그래서 이것은 때가 철저해야한다는 것을 의미했습니다 그러나 식으로 사용할 때만 철저합니다 그래서 우리는 철저한 속성을 만들었습니다

그래서, 더 정확하게, 우리는 T에 확장 속성을 만들었습니다 여기서 우리는 단지 객체를 반환 할뿐입니다 우리가 가진 또 다른 문제가 있습니다 그래서 우리는 코멘트 클래스와 답글에 대한 코멘트를 가지고있었습니다 그래서이 둘의 차이점은 사실입니다

그 주석은 또한 사용자에 관한 정보를 보유하고있다 의견을 게시했습니다 그래서 그것은 표시 이름과 초상화 URL을 가지게 될 것이고, 답글을 사용하는 주석은 거의 트리 구조입니다 주석의 응답을 보유하고있는 그리고 답장의 응답 등등 하지만 우리가해야 할 일은 코멘트를 작성하는 것입니다 답글과 사용자 객체가있는 주석의 그래서 당신은 이렇게 말합니다

좋습니다 우리는 새로운 생성자를 만들 것입니다 우리에게 매개 변수, 사용자 및 답글에 대한 설명을 얻습니다 그리고 그게 다야 하지만 그 일은 우리가 실제로하지 않았기 때문입니다

클래스가 두 개의 서로 다른 레이어에 있기 때문에 이렇게합니다 그리고 코멘트가 답글에 대한 코멘트에 대해 알아야하는 이유는 무엇입니까? 왜 사용자에 대해 필연적으로 알아야합니까? 데이터가 어딘가에서 온 것일 수도 있습니다 이 API를 변경해야하는 이유는 무엇입니까? 그래서 우리가 사용하게 된 것은 확장 기능입니다 그래서, 더 정확하게, 우리는 확장 함수를 만들었습니다 사용자 개체를 기반으로 한 댓글에 대한 댓글에 그것은 주석을 작성합니다

따라서 확장 기능을 만들 때, 당신은 공개 필드에 액세스 할 수 있습니다 그래서 이것은 우리가 실수로, 개인 구현 데이터 액세스 또는 변경 그리고 그것은 우리가 수업에 집중할 수있게 해줍니다 그들이 그것을 연장하지 않고 무엇을하는지 집중했다 따라서 공용 API를 변경할 필요가 없다는 의미였습니다 개인 구현 세부 사항에 액세스하지 않아야합니다

그래서 데이터 클래스에 대해 좋아하는 것은 사실입니다 값 객체가 있다는 것을 테스트에서 사용하면 빛난다 그래서 예를 들어, 우리는 코멘트에 upvote 플래그를 가지고 있습니다 그래서 우리가 코멘트가 upvoted 여부를 확인하기 위해 테스트를 구축, upvoted 플래그를 false로 설정하여 주석을 작성합니다 어떤 의견이든간에, 우리는 예상 된 결과가 코멘트, 초기 코멘트, 하지만 그 upvoted 플래그와 함께 사실

그러나 그 일은 특히 우리의 경우에는 주석에 너무 많은 필드가 있기 때문에, 실수하기 쉽습니다 그리고 실제로 무엇이 빠졌는지 쉽게 알 수있었습니다 여기서 중요한 것은 upvoted 플래그가 변경되었다는 사실입니다 Kotlin을 사용하면 복사 방법을 사용할 수 있습니다 그리고 거기에서 우리는 객체의 복사본을 만들 것입니다

에 호출됩니다 그리고 우리는 깃발을 세우고 있습니다, 우리가 실제로 가지고있는 깃발 변화, 그리고 그게 전부 야 코드가 더 간결하고 읽기 쉽도록 끝나고, 더 이해하기 쉽다 그럼 다른 예를 들어 봅시다 우리의 응용 프로그램에서 우리는 원격 데이터 소스 의견을 게시 할 수 있습니다

그리고 여기서, 우리는 결과를 반환합니다 그리고이 방법 안에서, 우리는 새로운 코멘트 요청을 만들 것입니다 우리는 백 엔드에서 그 요청을 트리거 할 것입니다 우리는 그 반응을 기다릴 것이다 그리고 우리는 응답을 처리하고 결과 성공 또는 오류 결과, 필요한 것에 따라 그러나이 코드를 살펴보면 실제로 충분하지 않습니다

왜냐하면 당신의 장치가 오프라인 일 때, 이 코드는 충돌합니다 그래서 우리가 실제로해야 할 일은 모든 요청을 감싸는 것입니다 try-catch 내부 그리고 우리에게는 많은 요청이 있습니다 그래서 우리는 계속이 try-catch를 추가하고 추가하는 것을 보았습니다

사방에, 그리고 우리의 방법이로드되었습니다 그래서 우리는 무엇에 집중할 수 없었습니다 응답을 만드는 데 정말로 중요했습니다 요청을 트리거합니다 그래서 우리가 한 것은 최상위 함수를 만드는 것입니다

그래서 이것은 서스펜션 기능이 될 것입니다 그것은 우리에게 람다를 정지시키는 매개 변수와 오류를 줄 것입니다 메시지 그래서 여기에 전화가 걸립니다 try-catch 내부에 포장 한 다음 빌드합니다

예외의 경우, 결과 [INAUDIBLE]이 (가)있는 오류 메시지를 기반으로 오류가 발생했습니다 그래서 이것은 우리의 모드 데이터 소스에서, 안전한 API 호출을 만들 수 있습니다 실제로 우리에게 중요한 전화를하십시오 다른 함수 내부 이처럼 코드가 더 읽기 쉽고 더 쉬워졌습니다

이해하다 그래서이 안전한 API 호출은 말하고있었습니다 첫 번째 매개 변수로 호출이 있음 오류 메시지를 두 번째 오류 메시지로 보냅니다 그러나 Kotlin에서 메서드의 마지막 매개 변수 람다, 당신이 할 수 있다는 것을 의미합니다 이것을 후행 람다 (lambda)로 사용하십시오

그래서 이것은 당신이 이것을 부를 때, 이 두 매개 변수를 전달하는 대신, 우리는 단지 오류 메시지를 전달할 수 있습니다 함수의 매개 변수로 메서드를 호출하려면 후행 람다 구문을 사용하십시오 이와 같이 코드가보다 간결 해지고, 그러나 그것은 정말로 더 가독합니까? 그래서 우리가 이것을 보았을 때, 그것은 느꼈습니다 여기에서 가장 중요한 것은 오류 메시지입니다 정말로 그렇지 않습니다

우리에게 중요한 점은 이 게시물의 댓글입니다 그래서 우리는 코드가 더 간결하지만, 그것은 더 가독성을 의미하지는 않습니다 그래서 간결함이 반드시 좋은 것은 아닙니다 따라서 Kotlin이 이러한 모든 종류의 옵션을 제공하더라도 및 기능, 염두에두고 실제로 생각하는지 이 모든 것이 필요하거나 올바른 장소에서 사용하십시오 다른 예가 있습니다

그래서 우리가 코 틀린으로 전환하자마자, 특히 우리의 활동에서 우리가 한 첫 번째 일 우리의 모든 견해를 만들 [부적절] 왜냐하면 우리는이 null 가능성을 모두 처리하기를 원치 않았기 때문입니다 하지만 우리는 코드를 다시 살펴 봤습니다 우리는 이것을해서는 안된다는 것을 알았습니다 일부 견해, 예를 들면, 우리의 결과가없는 견해, 특정 조건이 충족 될 때만 팽창되었습니다 사실 null 허용은 좋았습니다

Nullability는 의미가 있습니다 무효 성은 우리에게 무언가가 빠져 있다는 것을 말해주었습니다 우리는 그것을 실제로 처리해야합니다 그래서 전반적으로, 우리는 코 틀린 (Kotlin) 코 루틴과 같은 불변성, 기능 시민, 우리의 응용 프로그램을 도울 수 있습니다 그리고 앱 아키텍처에 대한 가이드와 함께, 우리가 이것을 유지 보수 할 수 있도록 도와 줬다

신속한 개발 아키텍처 우리가 갖고 싶었던 것 고맙습니다 [박수 갈채] [음악 재생]