2019년 11월 25일 월요일

The Mystery of Mutable Kotlin Collections 를 읽고

https://proandroiddev.com/the-mystery-of-mutable-kotlin-collections-e82cbf5d781

Kotlin에서 MutableList는 List는 아래와 같다.

public interface Mutable:ist<E> : List<E>, MutableCollection<E> {}
public interface List<out E> : Collection<E> {}

List: read-only access
MutableList: read/write access

- MutableListOf(...)나 listOf(...)를 통해서 만들어진 리스트는 MutableList로 인식된다.
- List 인터페이스를 Kotlin으로 직접 구현하면 MutableList로 인식하지 않는다.

위처럼 되는 이유는?
Kotlin에서의 List는 mock interface이기 때문이다.
Kotlin에서의 List는 컴파일시 사라지고 Java에서의 List로 변환된다.

2019년 11월 22일 금요일

Public API challenges in Kotlin 요약

https://jakewharton.com/public-api-challenges-in-kotlin/

코틀린의 data class 만이들 사용하실 겁니다. 자바에서 일일이 코딩해야 되는 내용을 매우 간단하게 만들어 주니까요. 그런데
이 data class를 쓰는 경우 바이너리 호환에 주의해야 합니다. 클래스의 내부 필드에 변경이 있는 경우 바이너리 호환이 안될
수가 있기 때문입니다.

1. 새로운 필드를 중간에 추가하는 경우 코틀린에서 제공하는 ComponentN()
함수가 맞지 않게 됩니다. 따라서, 새로운 필드를 추가하는 경우에는 항상 마지막에 추가해야 합니다.
2. data class의 경우 copy 함수를 자동으로 생성해 줍니다. 그런데 필드가 새로 추가되는 경우 copy()의 signature가 바뀌어
버리는 문제가 발생할 수 있습니다. 결국 바이너리 호환이 필요한 경우라면 data class를 쓰기보다는 직접 관련 함수 구현을 다
해주는 것이 좋겠습니다.
3. class 생성시 자유도를 주기위해 builder 패턴을 많이 사용합니다. 이 경우 필드에 @set:JvmSynthetic 를 사용해서 get은
노출하지만 set은 노출하지 않을 수 있습니다.
코틀린의 경우에는 top-level function이나 DSL을 통해 인스턴스를 생성하는 방법을 많이 사용합니다. 이 경우 자바에서는
바이너리 호환 문제가 발생할 수 있으므로 @JvmSynthetic을 사용해서 자바쪽에 노출이 되지 않도록 해줍니다.

2019년 11월 21일 목요일

Composition over Inheritance: Adding a Material Speed Dial to a Floating Action Button 요약

https://proandroiddev.com/composition-over-inheritance-adding-a-material-speed-dial-to-a-floating-action-button-1e646995ab49

FAB 버튼을 누르면 추가 버튼이 나오는 UI를 생각해보자. 어떻게 구현하는 것이 좋을까? Custom View를 만드는 방법을 생각해 볼
수 있다. 이렇게 구현된 많은 오픈소르 라이브러리들이 있다. 그런데 이 라이브러리들의 동작이 내가 원하는 것과 다르면 어떻게
해야 할까? 클래스를 상속받아서 내가 원하는 형태로 변경하면 될까? 이렇게 하려면 상속이 가능해야 하고 또한 내가 원하는
함수를 override 해서 처리할 수 있게 되어 있어야 한다. 그렇지 않다면 힘들것이다.

View와 Behavior를 분리해서 작성하는 건 어떨까?(Composition over inheritance)

1. FAB를 클릭했을 때 보여주는 PopupWindow 작성

1.1. anchor view의 위치에 따라 PopupWindow의 위치 지정: doOnLayout
1.2. PopupWindow는 FragmeLayout으로 구성하고 addView를 통해 내부에 필요한 view를 넣는다 -> LinearLayout으로 구성
1.3. LinearLayout의 아래에서 부터 item을 넣어야 하므로 MIRROR를 사용
1.4. LayoutAnimationController를 사용하여 item을 보여줄 때 애니메이션을 보여준다.
1.5. ripple 효과를 위해 RippleDrawable을 만들고 button의 background에 설정

2. FAB를 클릭시 동작 지정

2.1 버튼을 회전시키는 spring animation
2.2 1에서 만든 PopupWindow 보이기
2.3 PopupWindow의 dismiss시 동작 설정

위처럼 1과 2의 형태로 분리하면 어떤 View를 사용해서라도 추가 버튼이 나오는 형태를 구현할 수 있다.


2019년 11월 14일 목요일

Injection into Android Component’s Constructors is real 요약

https://proandroiddev.com/inject-into-android-component-constructor-4f5ddd27d06

안드로이드는 Activity와 Fragment를 직접 생성하지 않고 OS가 생성을 해줍니다. 그러다보니 생성시 필요한 값이 있을 때
생성자에 넣어줄 수는 없고 Activity.onCreate()나 Fragment.onAttach()에 넣어주게 되죠. 그러다보니 lateinit을 사용하게
됩니다.
그렇게 큰 불편함은 없지만 이와이면 생성자에서 값을 생성할 수 있으면 좋을텐데요. Activity의 경우에는
AppComponentFactory를 사용해서, Fragment의 경우에는 FragmentFactory를 사용해서 생성자에 값을 넣어줄 수 있습니다.
다만, AppComponentFactory는 안드로이드 9 부터 사용이 가능하다는 단점이 있네요. 하지만 요즘 추세는 Single activity이니까
큰 문제 없겠죠?

1. FragmentFactory를 상속한 InjectFragmentFactory를 만든다.
2. Application의 registerActivityLifecycleCallbacks에 SetFragmentFactoryActivityCallbac를 등록한다.
3. SetFragmentFactoryActivityCallback는 EmptyActivityLifecycleCallbacks를 상속해서 만드는데 onActivityCreated에서
fragmentManager에 SetFragmentFactoryFragmentCallback를 등록하고 FragmentFactory를 설정한다.
4. SetFragmentFactoryFragmentCallback는 FragmentManager.FragmentLifecycleCallbacks를 상속해서 만드는데 child
fragment에도 FragmentFactory를 설정해준다.

여기까지가 FragmentFactory를 적용하기 위한 설정이고 다음에는 Dagger를 통해 FragmentFactory에 대한 DI 적용을 한다.

5. Component와 Module 및 Multi bind를 생성한다: AppComponent, AppModule, ComponentProvidersModule, FragmentBindsModule, FragmentKey

Dagger를 통한 생성은 multi bind를 공부해 봐야 할듯 하군요.

https://dagger.dev/multibindings

2019년 11월 4일 월요일

libp2p - Publish/Subscribe 를 읽고

https://docs.libp2p.io/concepts/publish-subscribe/

- Discovery

Distributed hash tables
Local network broadcasts
Exchanging peer lists with existing peers
Centralized trackers or rendezvous points
Lists of bootstrap peers

peer와의 연결에 full-message와 metadata-only 방식이 있다. full-message peering은 메시지를 주고 받을 때 사용하는 연결로서, 최대 연결 가능한 peer의 수를 제한(6정도)하여 연결을 맺는다. metadata-only peering은 full-message로 연결되어 있지 않은 peer들을 더 많이 연결을 맺어 관련 여러 정보들을 주고 받는다.(예를 들면 '난 이런 토픽들에 subscribe하고 있다', '난 이런 토픽에 unscribe 한다.' 등등)

peer는 메시지를 받으면 full-message로 연결된 peer에 메시지를 포워딩한다. 또한, 매 1초마다 metadata-only로 연결된 peer들 중 6개를 랜덤으로 골라서 최근에 받은 메시지를 보낸다. 이렇게 서로서로 받은 메시지가 무었인지를 주고 받음으로서 내가 어떤 메시지를 받았는지 못 받았는지를 확인하고 이에 따라 full-message로 연결될 peer를 조정한다.(기존에 full-message로 연결된 peer를 metadata-only로 조정하고, metadata-only로 연결된 peer를 full-message로 바꾼다던지)

내가 subscribe 하고 있지 않은 topic에도 메시지를 보낼 수 있다. 이를 Fan-out이라 한다. 

libp2p - Stream Multiplexing 을 읽고

https://docs.libp2p.io/concepts/stream-multiplexing/

Stream multiplexing: Transport 단에서 하나의 connection을 맺고 그 위로 application 단에서 필요에 따라 stream을 여러개 열어서 사용

libp2p는 peer와의 연결에 stream multiplexing을 사용한다.

Stream multiplexing을 하는 다양한 프로토콜이 있다.

: mplex, yamux, quic, spdy

libp2p - Addressing 를 읽고

https://docs.libp2p.io/concepts/addressing/

libp2p는 다른 peer와의 연결시 사용하는 프로토콜이 매우 자유롭게 설계되어 있다. 따라서, 어떤 프로토콜을 사용할지를 연결하려는 peer에게 알려주어야 한다.

/ip4/7.7.7.7/udp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

=> ip 주소는 7.7.7.7이고 udp를 사용하고 udp 포트는 4242이고 내 peer id 는 QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N 임을 알리는 multiaddr 표현

NAT 안에 있어서 릴레이가 필요한 경우는 다음과 같이 표시할 수 있다.

/ip4/7.7.7.7/tcp/4242/p2p/QmRelay/p2p-circuit/p2p/QmRelayedPeer

=> 릴레이를 해주는 peer의 id 는 QmRelay이고 이 peer의 ip 주소는 7.7.7.7이고 tcp 포트는 4242이다. 내 peer id는 QmRelayedPeer이다.

libp2p - Peer Identity 를 읽고

https://docs.libp2p.io/concepts/peer-id/

- PeerId

a cryptographic hash of a peer's public key
multihash 포맷을 사용해서 인코딩
보통 base58로 인코딩해서 표시

=> QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

multiaddrs에 표시할 때는 /p2p로 표시한다.

=> /p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

transport 주소와 같이 사용되는 경우에는 다음과 같이 된다.

/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N

- PeerInfo

개별 peer에 대한 정보를 담고 있다. 나중에 특정 peer에 접속하려 할 때 사용할 수 있다.


Building asynchronous views in SwiftUI 정리

Handling loading states within SwiftUI views self loading views View model 사용하기 Combine을 사용한 AnyPublisher Making SwiftUI views refreshable r...