devlog of ShinJe Kim

[Android] AndrodManifest에서 service 속성의 종류

|

<service>

syntax

<service android:description="string resource"
         android:directBootAware=["true" | "false"]
         android:enabled=["true" | "false"]
         android:exported=["true" | "false"]
         android:foregroundServiceType=["connectedDevice" | "dataSync" |
                                        "location" | "mediaPlayback" | "mediaProjection" |
                                        "phoneCall"]
         android:icon="drawable resource"
         android:isolatedProcess=["true" | "false"]
         android:label="string resource"
         android:name="string"
         android:permission="string"
         android:process="string" >
    . . .
</service>

contained in:

<application>

can contain:

  • <intent-filter>
  • <meta-data>

description:

서비스(Service)를 애플리케이션의 컴포넌트로 선언하는 부분입니다. 액티비티와는 달리 서비스는 보여지는 유저 인터페이스가 없습니다. 주로 오래 지속되는 백그라운드 오퍼레이션이나 다른 애플리케이션으로부터 호출되는 많은 통신 API(rich communications API)를 구현하는데에 사용됩니다.

모든 서비스는 매너페스트 파일의 <service>에서 선언되어야만 합니다. 이 곳에 선언되지 않은 서비스는 시스템에서 보여지지 않으며 절대 실행되지 않습니다.

attributes:

  • android:description

    사용자에게 서비스를 설명하는 문자열입니다. 사용자 인터페이스의 다른 문자열처럼 현지화될 수 있도록 이 레이블 또한 스트링 리소스를 참조하도록 설정해야합니다.

  • android:directBootAware

    서비스가 direct-boot aware 한지를 설정합니다. direct-boot aware란, 사용자가 기기의 잠금을 해제하지 않고도 실행할 수 있는지의 여부를 의미합니다. 디폴트값은 "false"입니다.

    주의: Direct Boot를 하는 동안 당신의 애플리케이션의 서비스는 device protected 저장소에 저장된 데이터에만 접근할 수 있습니다.

  • android:enabled

    시스템에 의해 서비스가 인스턴스화 될 수 있는지를 의미합니다. 디폴트값은 "true"입니다. <application>의 엘리먼트에도 <enabled> 속성이 있습니다. 따라서 <application><service>속성 모두 "true"로 되어 있어야(두 속성 모두 디폴트값은 true입니다) 서비스가 활성화됩니다. 둘 중 하나라도 “false“로 설정되어 있으면 서비스가 비활성화되고 인스턴스화 될 수 없습니다.

  • andorid:exported

    다른 애플리케이션의 컴포넌트가 서비스를 호출하거나 서비스와 상호작용 할 수 있는지를 결정합니다. 값이 "false"이면 같은 user ID를 가진 동일한 애플리케이션 내에서만 서비스를 시작하거나 bind할 수 있습니다. 디폴트값은 해당 서비스가 intent filter를 포함하고 있는지의 여부에 따라 다릅니다. intent filter가 없다는 것은 서비스가 해당 애플리케이션 내에서만 사용하도록 만들어졌다는 의미입니다. 따라서 이 때의 디폴트값은 "false"입니다. 반면 intent filter가 하나라도 있다면 이는 external use를 염두에 두고 만들었다는 뜻이므로 "true"가 디폴트값으로 설정됩니다.

  • android:foregroundServiceType

    서비스가 특정 사용 사례를 만족시키는 foregound service인지를 지정합니다. 예를 들어 "location"의 foreground 서비스 타입은 일반적으로 사용자 시작 작업을 계속(continue a user-initiated action)하기 위해 앱이 디바이스의 현재 위치를 가져온다는 것을 의미합니다.

    특정 서비스에 여러개의 foregound 서비스 타입을 지정할 수 있습니다.

  • android:icon

    서비스를 나타내는 아이콘입니다. 이 속성은 이미지 정의를 포함하는 drawable 리소스에 대한 찹조로 설정해야만 합니다. 만약 설정하지 않는다면, 애플리케이션 전체에 지정된 아이콘이 사용됩니다(<application>요소의 icon속성 참조)

    매너페스트 파일에서 설정했든, <application>요소에 의해 설정되었든 서비스 아이콘은 모든 서비스의 intent filter의 디폴트 아이콘이기도 합니다(<intent-filter> 요소의 icon 속성 참조).

  • android:isolatedProcess

    true로 설정하면 이 서비스는 다른 시스템과 분리되어 자체 권한이 없는 특수한 프로세스에서 실행됩니다. 이 서비스와의 유일한 통신 방법은 Service API(binding과 starting)를 통한 것입니다.

  • android:label

    사용자에게 보여지는 서비스의 이름입니다. 만약 이 속성을 설정하지 않으면 전체 애플리케이션에 설정된 레이블이 대신 사용됩니다(<application>요소의 label속성을 참고하십시오).

    이곳에서 설정되었든 <application> 요소에서 설정되었든, 서비스의 레이블은 모든 서비스 intent filter의 디폴트 레이블입니다(<intent-filter> 요소의 label 속성 참조).

    레이블은 유저 인터페이스의 다른 문자열처럼 현지화될 수 있도록 스트링 리소스를 참조하도록 설정해야합니다. 하지만 개발상의 편의를 위해 raw 스트링으로도 설정할 수는 있습니다.

  • android:name

    서비스를 구현하는 Service 서브클래스의 이름을 의미하며, 이는 fully qualified 클래스 이름이어야 합니다(예: com.example.project.RoomService). 하지만 간단히 표현하여 이름의 첫 문자가 마침표이면(예: .RoomService) <manifest> 요소에 지정된 패키지 이름에 추가됩니다.

    애플리케이션을 발행한 이후에는, 이 이름을 변경하지 않도록 해야합니다(android:exported="false"로 설정하지 않은 경우에).

    디폴트값은 없으며, 이름을 꼭 설정해야합니다.

  • android:permission

    서비스를 시작하거나 바인딩하기 위해 엔티티가 가져야 하는 권한의 이름을 의미합니다. 만약 startService(), bindService(), 혹은 stopService()의 호출자에게 이 권한이 부여되지 않으면 메서드가 작동하지 않으며 Intent object가 서비스에 전달되지 않습니다.

    이 속성을 설정하지 않으면 <application> 요소의 permission 속성으로 설정된 권한이 해당 서비스에 적용됩니다. 만약 두 가지 속성 모두 설정되지 않으면, 서비스는 권한으로 보호되지 않습니다.

    권한에 대한 자세한 내용은 소개의 Permissions 섹션과 별도의 문서인 Security and Permissions를 참조하십시오.

  • android:process

    서비스가 실행될 프로세스의 이름을 의미합니다. 일반적으로 애플리케이션의 모든 구성요소는 애플리케이션에 대해 생성된 기본 프로세스에서 실행됩니다. 이는 애플리케이션 패키지의 이름과 동일한 이름을 가집니다. <application> 요소의 process 속성은 모든 컴포넌트에 대해 다른 기본값을 설정할 수 있습니다. 하지만 컴포넌트는 자체적인 process 속성을 재정의하여 당신의 애플리케이션을 여러 개의 프로세스로 분산시킬 수 있습니다.

    만약 이 속성에 지정된 이름이 콜론(‘:’)으로 시작한다면, 필요에 의해 애플리케이션 전용의 새 프로세스가 생성되고, 서비스가 해당 프로세스에서 실행될 수 있습니다. 만약 프로세스 이름이 소문자로 시작한다면, 서비스는 권한이 있는 경우 해당 이름의 글로벌(global) 프로세스에서 실행됩니다. 이를 통해 서로 다른 애플리케이션에 속한 구성요소가 프로세스를 공유하여 리소스 사용량을 줄일 수 있습니다.

참고문헌

구글 공식문서 - <service>

[TIL] 2019-10-07

|

Today I Learned

  • GoF(Gang of Four)의 디자인 패턴의 패턴은 총 23가지이다. (안드로이드의 holder 패턴과 viewholder 패턴은 디자인 패턴이 아님) 1995년에 나온 고전이지만 아직까지 쓰는 것은 외워두도록.
  • 하지만 패턴에 종속되면 안된다. 지금은 프로그래밍 환경과 그에 따른 방법론이 많이 변했기 때문에 안쓰는 패턴이 많다. 다만 proxy, iterator, observer, strategy는 정말 많이 쓴다고 한다.
  1. 싱글턴
  2. 빌더
  3. 추상팩토리
    • 생성(factory)에 대한 방법을 가변적으로 구현할 수 있다.
  4. 팩토리메서드
    • factory란 소프트웨어에서 무엇인가 생성하는 역할이라는 뜻으로 쓰임.
  5. 프로토타입
    • 견본에 의한 생성. 어떠한 객체를 새롭게 생성하는 것이 아니라 견본을 복제하여 생성하는 것. clone의 문제점은? 3가지가 있음. 이펙티브 자바에 나옴.
  6. 템플릿 메서드
    • 엄청 많이 씀. 안드로이드에서 onCreate를 호출할 때 super.onCreate를 하는 부분이 바로 템플릿 메서드임. super.onBackPressed()도 마찬가지.
  7. 전략(strategy)
    • 변해야 하는 정책은 interface를 통해서 전달, 혹은 함수를 통해서 인자로 전달. setOnClickListener가 대표적인 전략임. 이것은 오버로딩/오버라이딩과는 다름. 오버로딩은 메서드의 이름이 같고 입력만 다름(컴파일 타임 다형성). 오버라이딩은 그것을 호출하는 객체가 다름(런타임 다형성). 오버라이딩은 어떤 객체가 그것을 호출하는지를 런타임에 검사함. 상속에서 부모와 자식은 반드시 is-a 관계가 성립해야 함. 컴파일 타임 다형성은 성능적인 오버헤드가 없다. 런타임 다형성은 그 자체가 오버헤드이다. kotlin에서 기본적으로 final 메소드를 만들도록 하는 것은 불필요한 상속을 방지하는 것도 있지만 성능적인 부분을 위한 것이기도 함.
  8. state 상태패턴
    • 나의 상태에 따라서 동작이 바뀌는 패턴. 자바의 enum. enum의 각 항목이 state.
  9. 관찰자
    • 변경을 알려주는 것. push 메시지 또한 관찰 패턴임.
  10. 반복자
    • 컬렉션의 내부 구조에 상관없이 요소를 열거하는 패턴.
  11. composite
    • 복합 객체는 단일 객체도 복합 객체도 포함할 수 있다. 폴더와 파일. 폴더는 복합 객체이고 파일은 단일 객체임. 폴더에는 폴더도 들어갈 수 있다.
    • 메뉴. 서브메뉴 안에 또 서브메뉴를 포함할 수 있음.
  12. decorator
    • 실행중인 객체의 기능을 포함하고 싶을 때. inputStream, outputStream. 버퍼를 추가하고 싶으면 bufferedInputStream.. 등. SRT(단일 설계)를 잘 구현한 것.
    • 문제점: call이 중첩됨. 림이 생긴다? 자바에서는 io성능상의 이슈가 있음.
  13. 브릿지
  14. adapter
    • 각기 다른 인터페이스의 접합부 역할을 해줌. 예를 들면 리사이클러뷰에 리스트를 뿌리고 싶으면 이 어답터가 필요함.
    • 자바의 stack은 adapter로 구현되어 있음. 리스트를 한 번 감싸서 선입후출의 구조로 만든 것이 스택임. 이것은 잘못된 설계임. is-a 관계가 설립하지 않는데 상속으로 만들었기 때문.
  15. mediator(중간자)
    • 라디오버튼. 여러가지 상태를 동시에 가질 수 없음. 이 상태를 유지하기 위해서는 서로의 상태를 모두 알아야 함. 1:1 대응 관계로 만들기 위해서는 복잡도가 높아짐. 그래서 중간자를 두면 복잡도를 낮추면서 상태를 감지할 수 있음
  16. facade(퍼사드)
    • 레이어를 만드는 것. 인터넷에서 무언가를 다운로드받는 코드를 짠다고 해보자. C언어로 하려면 소켓연결부터 다 짜야하지만, 자바는 해당 메서드를 가져다 쓰기만 하면 됨.
    • 코드의 가독성을 높이기 위해서 중요함.
    • private 메서드의 역할: 우리는 정보 은닉이라고 배웠지만 아니다. public 메서드의 가독성을 높이기 위해서 private을 써야 한다.
  17. flyweight(경량)
    • 상태를 복사하지 않고 공유하여 쓰는 것.
    • 장점: 메모리 점유를 낮춤.
    • 전제조건: 불변 객체이어야 한다.
    • COP(copy on write): 쓸 때 복제하겠다는 것. late copy.
  18. proxy
    • 많이 씀.
    • 디자인 패턴의 꽃.
    • RPT, RMI
  19. 책임 연쇄(chain of responsibility)
    • 예: 자식 이벤트를 클릭했을 때, 해당 이벤트가 자식이 아니라 부모에게서 발생해야 할 때.
    • 이것을 책임의 전가라고 부름.
  20. 커맨드(command)
    • undo, redo를 만든다고 생각해보자. 이 명령 자체를 만들 수 없음.
    • 명령을 저장하기 위해서 명령이라는 행위 자체를 객체로 만드는 것.
  21. interpreter(인터프리터)
  22. 메멘토(memento)
    • 저장해야 하는 부분을 별도로 분리해서 관리해야 할 때 씀. 상태를 저장하고 상태를 복원함.
  23. 비지터(visitor). 방문자패턴.
    • 컬렉션의 내부 구조에 상관없이 요소를 연산(operate)하는 패턴.
    • 많이 안쓴다.
    • 문제점: 캡슐화가 깨져 있는 디자인 패턴임. 그래서 요즘은 연산 또한 iterator로 구현함.

[Kotlin] 코틀린의 널 안전성(Null Safety)

|

코틀린의 널 안전성(Null Safety)

이 글은 코틀린 공식문서를 공부하며 번역한 글입니다. 틀린 부분이나 어색한 부분을 댓글로 알려주시면 감사하겠습니다.

널이 될 수 있는 타입과 널이 될 수 없는 타입(Nullable types and Non-Null Types)

코틀린의 타입(type) 시스템은 Billion Dollar Mistake라고도 알려진 null 참조 코드의 위험성을 없애기 위한 것입니다.

Java를 포함한 많은 프로그래밍 언어에서 가장 일반적인 함정 중 하나는, null 참조의 멤버에 접근하면 null 참조 예외(null reference exception)가 발생한다는 것입니다. Java에서는 이것을 NullPointerException 또는 NPE라고 합니다.

코틀린의 타입(type) 시스템은 코드에서 NullPointerExeption을 제거하기 위한 것입니다. NPE의 유일한 원인은 아래와 같습니다:

  • throw NullPointerException을 명시적으로 호출하는 것;
  • 아래에서 설명하는 것과 같이 !! 연산자를 사용하는 것;
  • 초기화와 관련하여 아래와 같은 특성으로 데이터의 불일치가 발생할 때:
  • Java interoperation:
    • 플랫폼 유형null 참조에서 멤어에 접근하려고 시도함
    • Generic types used for Java interoperation with incorrect nullability, e.g. a piece of Java code might add null into a Kotlin MutableList, meaning that MutableList<String?> should be used for working with it;
    • 외부의 자바 코드로 인한 기타 문제들

코틀린에서는 타입 시스템이 null이 가능한 참조와 그렇지 않은 참조를 구분합니다. 예를 들어, Stringnull을 참조할 수 없습니다.

var a: String = "abc"
a = null // 컴파일 에러 발생

null을 참조하기 위해서는 String?와 같이 선언해야 합니다.

var b: String? = "abc"
b = null // ok
print(b)

만약 NPE를 발생시키지 않도록 보장된 a라는 메소드를 호출하거나 프로퍼티에 접근한다면, 안전하게 아래와 같이 선언할 수 있습니다:

val l = a.length

하지만 만약 NPE에 안전하지 안흔 b와 같은 프로퍼티에 접근한다면, 컴파일 에러가 발생할 것입니다.

val l = b.length // error: variable 'b' can be null

하지만 우리는 여전히 b 프로퍼티에 접근해야합니다. 어떻게 해야 할까요? 이 문제를 해결하기 위한 몇 가지 방법이 아래에 있습니다.

조건에서 null을 확인하기(Checking for null in conditions)

우선, 명시적으로 b가 null인지를 체크하여 두 가지 옵션을 구분하여 다루어야 합니다:

val l = if (b != null) b.length else -1

컴파일러는 작성된 코드가 수행하는 검사를 추적하여 if 내부의 length를 호출하는 것을 허용합니다. 이보다 더 복잡한 조건도 지원됩니다:

val b: String? = "Kotlin"
if (b != null && b.length > 0) {
    print("String of length ${b.length}")
} else {
    print("Empty string")
}

이는 b가 변경 불가능한 경우일때에만(즉, 검증(check)과 사용(usage) 사이에서 수정되지 않은 지역 변수 혹은, backing field가 있고 재정의할 수 없는 멤버 변수) 동작합니다. 그렇지 않으면 b가 검증 이후에 null이 될 수 있기 때문입니다.

안전한 호출(Safe Calls)

두 번째 방법은 안전한 호출 연산자인 ?.을 사용하는 것입니다:

val a = "Kotlin"
val b: String? = null
println(b?.length)
println(a?.length) // Unnecessary safe call

위의 코드는 b가 null이 아니면 b.length를 리턴하고, 그렇지 않으면 null을 반환합니다. 이 표현식의 타입은 Int?입니다.

안전한 호출(safe calls)은 체인(chain)에서 유용합니다. 예를 들어, 직원인 Bob이 부서에 배정된 경우(혹은 그렇지 않은 경우), 다른 직원을 부서장으로 둔 다음, Bob의 부서 장(만약 있다면)의 이름을 얻기 위해 아래와 같은 코드를 작성합니다:

bob?.department?.head?.name

이 체인에서 프로퍼티가 null인 것이 하나다로 있다면, 이 체인은 null을 리턴합니다.

null이 아닌 값에 대해서만 특정 연산을 수행하려면 아래와 같이 안전한 호출 연산자인 let을 사용하면 됩니다:

val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
    item?.let { println(it) } // prints Kotlin and ignores null
}

A safe call can also be placed on the left side of an assignment. 그 다음, 만약 안전한 호출 체인의 리시버중 하나가 null이면 할당을 건너뛰고, 오른쪽의 표현식은 고려되지 않습니다.

// If either `person` or `person.department` is null, the function is not called:
person?.department?.head = managersPool.getManager()

엘비스 연산자(Elvis Operator)

null을 참조할 수 있는 r이 있을 때, “만약 r이 null이 아니면 해당 값을 사용하고, r이 null이면 x라는 null이 아닌 값을 사용한다”라고 표현할 수 있습니다:

val l: Int = if (b != null) b.length else -1

위의 if 표현식을 엘비스 연산자인 ?:를 사용하여 아래와 같이 표현할 수도 있습니다.

val l = b?.length ?: -1

?: 연산자는 결과값이 null이 아니면 연산자 왼쪽의 값을 반환하고, 값이 null이면 오른쪽의 값을 반환합니다. 오른쪽 표현식은 왼쪽의 값이 null인 경우에만 계산된다는 것을 꼭 기억하세요.

코틀린에서는 throwreturn이 표현식이므로 엘비스 연산자의 오른쪽에서도 사용할 수 있습니다. 이는 함수의 인수를 확인할때와 같은 경우에 매우 유용합니다.

fun foo(node: Node): String? {
    val parent = node.getParent() ?: return null
    val name = node.getName() ?: throw IllegalArgumentException("name expected")
    // ...
}

!! 연산자(The !! Operator)

세번쨰 방법은 NPE 애호가를 위한 것입니다: not-null을 선언하는 연산지인 !!는 모든 값을 null이 아닌 타입으로 변환하고, 값이 null인 경우에는 예외를 throw합니다. 예를 들면, b!!와 같이 쓸 수 있으며 이것은 null이 아닌 b값(예, String)을 리턴하거나 b가 null인 경우 NPE를 throw합니다:

val l = b!!.length

따라서, NPE를 원한 경우 NPE를 발생시킬 수 있지만 명시적으로 요청해야하며 파란색으로 표시되지 않습니다.

안전한 캐스팅(Safe Casts)

객체가 target 타입이 아닌 경우 규칙적인 타입 캐스잉으로 인해 ClassCastException 발생할 수 있습니다. 이 떄의 방법은, null을 리턴하는 안전한 캐스팅을 사용하는 것입니다.

val aInt: Int? = a as? Int

널이 가능한 타입 컬렉션(Collections of Nullable Type)

null을 입력할 수 있는 타입의 요소 컬렉션이 있을 때, null이 아닌 요소를 필터링하고 싶다면 filterNotNull을 사용할 수 있습니다:

val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()

참고 문헌

코틀린 공식문서

첫 코드리뷰 회고

|

[부제] 똑같은 실수를 반복하지 말자.

  1. 네이밍
    • 기본, 또 기본을 지켜라.
      • 프로젝트명에는 언더스코어가 안들어간다. -로 연결하자.
      • 코드를 프로젝트에 옮겨심을 때 패키지명을 변경하지 않았다. 꼭 확인하자.
      • 클래스는 항상 대문자로 시작한다. 알고 있었는데 실수했다. 절대 실수하지 말자.
      • 클래스는 명사로 시작하고, 메소드는 동사로 시작한다.
      • 네이밍을 복잡하고 어렵게 하지 말자. 지역변수를 사용할 때에는 풀네임을 쓰기보다는 일반 명사를 사용하자. 만약 전역이면 길어도 명확하게, 지역이면 축약해서(만약 중복이 있다면 구분해줘야 함)(예. passwordResetFragment -> fragment)
  2. 코틀린 문법
    • 코드 중복을 최대한 줄이고 블록을 줄여라.(블록이 중첩될수로 코드가 읽기 어렵다)
      • 뷰단의 id를 불러올때에는 findViewById 할 필요 없이 바로 id를 호출하면 된다.
      • let을 난무하지 마라. let이 중첩되면 코드가 읽기 어려워진다.
      • if 다음에 else가 꼭 필요한 것이 아니라면 쓰지마라. 블록을 최대한 줄여라.
    • 명확한 코드를 작성하라.
      • it 금지. it이 무엇인지 찾아서 대체하라.
      • 사전조건을 먼저 체크하고 return해버리자. 블록 중간에 조건 체크를 하면 블록이 길어지고 중첩된다.
  3. 안드로이드
    • 프로젝트 구조와 패턴을 이해하고 써라.
      • viewModel의 역할이 무엇인지 잘 모르고 썼다. MVVM 패턴 공부해서 정리하자.
    • 기본적으로 제공되는 api를 잘 활용하라. 바퀴를 새로 발명하지 마라.
      • 안드로이드가 기본으로 제공해주는 AlertDialog가 있는데 굳이 fragment를 만들었다. 기존에 있는 것을 활용해야 한다. 커스텀뷰나 복잡한 것이 필요한 dialog는 fragment를 따로 만든다.
    • 안드로이드 라이프 사이클을 완벽히 이해하고 제어해야 한다.
      • onViewCreate에서는 뷰만 불러와라. onViewCreate에서 로직을 넣으면 미처 뷰가 생성되지 않아서 null이 들어올 수 있다. 뷰가 호출 된 뒤에 onViewCreated에서 로직을 작성하라.
  4. 기타
    • 동기/비동기의 실행 흐름을 머릿속으로 그리며 코딩해라.
      • 비동기와 콜백을 제대로 이해하지 못한채로 틀린 코드를 작성하였다. 가장 중요한 부분이므로 반복 또 반복해서 이해하고 숙지하자.
    • 요구사항을 빼먹지 마라.
      • 로딩 애니메이션을 만들라고 했는데 만들지 않는 큰 실수를 범했다. 요구사항을 항상 잘 정리해서 절대 빠지는 것이 없도록 해야한다.
    • 기존의 코드를 그냥 따라하지 마라.
      • 기존에 틀린 코드가 있던 것을 그대로 따라하여 중복된 코드를 작성하였다.(disposeBag부분) 이 부분은 한 번 생각하며 봤으면 의문을 가질 수 있었는데 그렇게 하지 않아서 생긴 잘못이다. 다음번엔 절대 실수하지 말자.

[TWIL] 2019-09-27

|

This Week I Learned

  1. Android
    • 중첩된 fragment에서 addToBackStack으로 stack에 넣은 뒤, 한 단계 이전의 fragment로 돌아가고 싶을 때에는 popBackStack을 하면 된다.
    • 이전의 TIL에서 해결하지 못했던 것을 해결했다. 여러개의 fragment를 중첩했을 때, 2단계 이상 차이 나는 fragment로 이동시켜야 할 때가 있다. 이 때 popBackStack(id, flags)을 사용하여 구현할 수 있다. 각 인자가 의미하는 바는 아래와 같다.
      • id: FragmentTransaction.commit()을 할 때마다 각 fragment에 id가 부여되는데 이 떄의 id를 의미한다. 공식 문서 설명으로는 어떻게 id가 부여되는지 잘 이해가 안되는데, 구현해본 결과 아마 쌓이는 순서대로 0, 1, 2 … 이렇게 부여되는 것 같다. 정확하게 알게 되면 다시 추가하기.
      • flags: 0 혹은 POP_BACK_STACK_INCLUSIVE의 값이다. 만약 POP_BACK_STACK_INCLUSIVE를 설정하고 id를 지정하면 해당 id와 일치하는 fragment를 찾을때까지 중첩된 fragment를 pop한다. id에 null을 넣으면 쌓여있던 모든 fragment가 pop된다.
      • 예를 들어 5개의 fragment가 중첩되어있다고 치자. 만약 5번째로 중첩된(stack의 가장 상위에 있는) fragment에서 popBackStack(1, POP_BACK_STACK_INCLUSIVE)를 실행하면 1의 id를 가진 fragment를 찾을때까지 총 3개의 fragment가 pop되고, 두 번째로 중첩된 fragment가 보여진다.
    • Button.isEnabled = true || false를 사용하면 버튼 클릭을 막거나 활성화 할 수 있다. 이를 사용하여 버튼의 중복 클릭을 방지할 수 있다.
    • dialog의 백그라운드 터치를 비활성화 하고 싶으면 fragment.isCancleable = true || false를 false로 설정하면 된다.
    • 네트웍 요청은 절대 메인 스레드에서 하면 절대 안된다.
    • 백그라운드 스레드에서 ui작업을 하면 절대 안된다. 만약 백그라운드 스레드에서 toast를 하려고 하면 Can't toast on a thread that has not called Looper.prepare()라는 에러가 날 것이다. 이 때에는 runOnUIThread() 라는 것으로 스레드를 묶어주면 해결된다.
    • call에서의 this는 해당 익명객체를 의미함. 그래서 명시적으로 this@MainActivity 라고 액티비티를 지정해주어야 함.
  2. retrofit
    • HTTP Request에서 발생하는 아래와 같은 보일러플레이트를 효과적으로 제거할 수 있게 해준다.
      • OkHttpClient로 설정해주어야 하는 수많은 작업
      • 동기(Synchronous) 작업을 별도의 스레드에서 작업할 수 있게 해줌(?) 동기/비동기, 스레드 공부 뒤에 다시 이해해보자
      • 비동기(Asynchronous)로 할 떄에는 UI를 벡그라운드스레드에서 작업하지 않도록 주의해야 한다. 이 때 별도의 코드를 넣지 않아도 retrofit 내부에서 처리를 해주어 오류가 발생하지 않도록 해준다. 예를 들어 call 요청시 onfailure, onresponse에서 ui를 다룰 때 메인 스레드에서 할 수 있도록 내부적으로 처리해줌. 그래서 여기서 toast 등을 해도 에러가 안난다.
      • 응답을 Gson을 통하여 객체로 변환해준다.
  3. rxjava2
    • rx의 adapter는 call을 rx의 observable로 변환해주는 역할을 함.
    • Jake Wharton님의 라이브러리에 감사하기.
    • disposebag 쓰고 난 다음 onDestroy에서 dispose() 해줘야 함.
    • 스레드의 동작은 결정되어 있지 않고 프로그래머가 세팅할 수 있게 하는게 중요하다고 함.
    • throttleFirst로 연속클릭 방지 처리. 사실은 clickenable = false로 처리하는게 맞음.
    • throttleFirst, throttleLast는 1-3초정도로 간격이 짧을 때에는 별 의미가 없다. 하지만 5초 이상의 간격으로 시간이 길 때에는 제대로 써줘야 알맞는 데이터가 들어간다.
  4. 기타
    • 왜 개발자가 동기/비동기를 선택할 수 있게 만들어놓았을까? -> 별도의 스레드가 있으면 동기로 하는 것이 편하기 때문에
    • 만약 400~499 에러가 났다면 요청이 실패한 것일까? 아니다. MDN에서는 400 Bad Request를 다음과 같이 정의하고 있다.

      HyperText Transfer Protocol (HTTP) 400 Bad Request 응답 상태 코드는 서버가 클라이언트 오류(예: 잘못된 요청 구문, 유효하지 않은 요청 메시지 프레이밍, 또는 변조된 요청 라우팅)를 감지해 요청을 처리할 수 없거나, 하지 않는다는 것을 의미합니다.

    • http 요청할 때 항상 url 인코딩을 확인하자.
    • 확장함수는 함부로 만드는 것이 아니다. 왜..였지? 코틀린 책 다시 보고 숙지하기.

TODO

  • 이번 비밀번호 변경때 사용한 라이브러리 다시 하나씩 찾아보고 어떤 역할을 하는지 명확하게 숙지하자.
  • 왜 메소드? 인터페이스? 에서 항상 자기 자신을 인자로 받도록 해놓았을까? call.enqueue를 할 때 call을 인자로 받는 이유?
  • 비동기의 핵심은 별도의 스레드라고 한다. 무슨말인지 공부해서 다시 정리해보자.
  • 안드로이드에서의 메인 스레드/백그라운드 스레드 공부 및 정리
  • Observer 패턴 정리
  • 안드로이드에서 보일러플레이트를 줄이기 위해 AOP(관점 지향 프로그래밍)를 한다. AOP 찾아보고 정리하자.
  • 레트로핏 protocol buffer converter? protocol buffer가 뭔지 찾아보자.

읽을거리