RecyclerView.ItemDecoration을 이용한 StickyHeader 구현

외부 라이브러리없이 전체 코드로 구현한다.

 

ItemDecoration

RecyclerView를 더 커스텀하여 사용할 수 있게 해준다.

아이템 간의 구분선을 만들거나 여백을 조정할 수 있으며 해당 포스팅처럼 RecyclerView 자체에 그릴 수도 있다.

 

ItemDecoration의 onDrawOver 메소드를 오버라이딩하여 구현한다.

RecyclerView가 draw할 때마다 콜백으로 호출되며 RecyclerView 위에 그려지게 된다.

 

 

RecyclerView의 Header 아이템과 일반 아이템으로 구분이 되어야 한다.

ViewType은 따로 나누지 않아도 상관없으며 구분할 함수만 구현하면 된다.

 

 

 

 

 

 

 

ItemDecoration 코드와 사용 코드

 

 

HeaderItemDecoration.kt

 

더보기
class HeaderItemDecoration(
    recyclerView: RecyclerView,
    private val isHeader: (itemPosition: Int) -> Boolean,
    onClickHeader: ((itemPosition: Int) -> Unit)? = null
) : RecyclerView.ItemDecoration() {
 
    private var currentHeaderToShow: Pair<Int, RecyclerView.ViewHolder>= null
 
    init {
        recyclerView.adapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
            override fun onChanged() {
                // clear saved header as it can be outdated now
                currentHeaderToShow = null
            }
        })
 
        recyclerView.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
            // clear saved layout as it may need layout update
            currentHeaderToShow = null
        }
 
        // handle click on sticky header
        onClickHeader?.let {
            initHeaderClickListener(recyclerView, it)
        }
    }
 
    override fun onDrawOver(c: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) {
        super.onDrawOver(c, recyclerView, state)
 
        val topChildView = getTopChildView(recyclerView) ?: return
        val topChildViewPosition = recyclerView.getChildAdapterPosition(topChildView)
        if (topChildViewPosition == RecyclerView.NO_POSITION) return
 
        val headerView = getHeaderViewToShow(topChildViewPosition, recyclerView) ?: return
 
        val contactedNewHeader = getContactedNewHeader(headerView, recyclerView)
        if (contactedNewHeader != null) {
            drawMovedHeader(c, headerView, contactedNewHeader, recyclerView.paddingTop)
        } else {
            drawHeader(c, headerView, recyclerView.paddingTop)
        }
    }
 
    private fun getTopChildView(recyclerView: RecyclerView)
            = recyclerView.findChildViewUnder(
                recyclerView.paddingStart.toFloat(),
                recyclerView.paddingTop.toFloat()
            )
 
    private fun getHeaderViewToShow(topChildItemPosition: Int, recyclerView: RecyclerView): View? {
        recyclerView.adapter ?: return null
 
        val headerPositionToShow = getHeaderPositionToShow(topChildItemPosition)
        if (headerPositionToShow == RecyclerView.NO_POSITION) return null
 
        return getHeaderView(headerPositionToShow, recyclerView)
    }
 
    private fun getHeaderView(headerPositionToShow: Int, recyclerView: RecyclerView): View? {
        val headerHolderType = recyclerView.adapter!!.getItemViewType(headerPositionToShow)
        if (currentHeaderToShow?.first == headerPositionToShow && currentHeaderToShow?.second?.itemViewType == headerHolderType) {
            return currentHeaderToShow?.second?.itemView
        }
 
        val headerHolder = recyclerView.adapter!!.createViewHolder(recyclerView, headerHolderType)
        recyclerView.adapter!!.onBindViewHolder(headerHolder, headerPositionToShow)
        fixLayoutSize(recyclerView, headerHolder.itemView)
 
        currentHeaderToShow = headerPositionToShow to headerHolder
 
        return headerHolder.itemView
    }
 
    private fun getHeaderPositionToShow(topChildItemPosition: Int): Int {
        var headerPosition = RecyclerView.NO_POSITION
        var currentPosition = topChildItemPosition
        do {
            if (isHeader(currentPosition)) {
                headerPosition = currentPosition
                break
            }
            currentPosition -= 1
        } while (currentPosition >= 0)
        return headerPosition
    }
 
    private fun fixLayoutSize(parent: ViewGroup, view: View) {
        // Specs for parent (RecyclerView)
        val widthSpec = View.MeasureSpec.makeMeasureSpec(parent.width, View.MeasureSpec.EXACTLY)
        val heightSpec = View.MeasureSpec.makeMeasureSpec(parent.height, View.MeasureSpec.UNSPECIFIED)
 
        // Specs for children (headers)
        val childWidthSpec = ViewGroup.getChildMeasureSpec(
            widthSpec,
            parent.paddingStart + parent.paddingEnd,
            view.layoutParams.width
        )
        val childHeightSpec = ViewGroup.getChildMeasureSpec(
            heightSpec,
            parent.paddingTop + parent.paddingBottom,
            view.layoutParams.height
        )
 
        view.measure(childWidthSpec, childHeightSpec)
        view.layout(00, view.measuredWidth, view.measuredHeight)
    }
 
    private fun drawHeader(c: Canvas, header: View, paddingTop: Int) {
        c.save()
        c.translate(0f, paddingTop.toFloat())
        header.draw(c)
        c.restore()
    }
 
    private fun getContactedNewHeader(headerView: View, recyclerView: RecyclerView): View? {
        val contactPoint = headerView.bottom + recyclerView.paddingTop
        val contactedChildView = getContactedChildView(recyclerView, contactPoint) ?: return null
        val contactedChildViewPosition = recyclerView.getChildAdapterPosition(contactedChildView)
 
        return if (isHeader(contactedChildViewPosition)) {
            contactedChildView
        } else {
            null
        }
    }
 
    private fun getContactedChildView(recyclerView: RecyclerView, contactPoint: Int): View? {
        var childInContact: View? = null
        for (i in 0 until recyclerView.childCount) {
            val child = recyclerView.getChildAt(i)
            val bounds = Rect()
            recyclerView.getDecoratedBoundsWithMargins(child, bounds)
            if (bounds.bottom > contactPoint) {
                if (bounds.top <= contactPoint) {
                    childInContact = child
                    break
                }
            }
        }
        return childInContact
    }
 
    private fun drawMovedHeader(c: Canvas, contactedTopHeader: View, contactedBottomHeader: View, paddingTop: Int) {
        c.save()
        c.translate(0f, (contactedBottomHeader.top - contactedTopHeader.height).toFloat())
        contactedTopHeader.draw(c)
        c.restore()
    }
 
    private fun initHeaderClickListener(recyclerView: RecyclerView, onClickHeader: (Int) -> Unit) {
        recyclerView.addOnItemTouchListener(object : RecyclerView.SimpleOnItemTouchListener() {
            override fun onInterceptTouchEvent(
                recyclerView: RecyclerView,
                motionEvent: MotionEvent
            ): Boolean {
                return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
                    val hasClickedOnHeaderArea = motionEvent.y <= currentHeaderToShow?.second?.itemView?.bottom ?: 0
                    if (hasClickedOnHeaderArea) {
                        currentHeaderToShow?.first?.let { position ->
                            onClickHeader.invoke(position)
                        }
                        true
                    } else {
                        false
                    }
                } else false
            }
        })
    }
}
cs

 

 

사용 코드

 

        rv_main.addItemDecoration(
            HeaderItemDecoration(
                rv_main,
                { it%4 == 0 },
                { Toast.makeText(this, it.toString(), Toast.LENGTH_SHORT).show() }
            )
        )
cs

 

HeaderItemDecoration 클래스에

recyclerView, header 판단 함수, header 클릭시 동작 함수를 입력해 사용하면 된다.

 

 

 

 

onDrawOver

    override fun onDrawOver(c: Canvas, recyclerView: RecyclerView, state: RecyclerView.State) {
        super.onDrawOver(c, recyclerView, state)
 
        val topChildView = getTopChildView(recyclerView) ?: return
        val topChildViewPosition = recyclerView.getChildAdapterPosition(topChildView)
        if (topChildViewPosition == RecyclerView.NO_POSITION) return
 
        val headerView = getHeaderViewToShow(topChildViewPosition, recyclerView) ?: return
 
        val contactedNewHeader = getContactedNewHeader(headerView, recyclerView)
        if (contactedNewHeader != null) {
            drawMovedHeader(c, headerView, contactedNewHeader, recyclerView.paddingTop)
        } else {
            drawHeader(c, headerView, recyclerView.paddingTop)
        }
    }
cs

 

onDrawOver 함수에 모든 핵심 내용이 다 있다.

 

1. recyclerView에서 보이는 가장 최상단 아이템 포지션을 얻는다.

2. 최상단 아이템 index 이하의 가장 가까운 header 아이템의 뷰를 얻는다.

3. (기존의 그리고 있는 header) 아래로 (접촉이 있는 새로운 header)가 있는지 확인한다.

 3-1. 있다면 새로운 header가 간섭한 만큼 위로 올려 그린다.

 3-2. 없다면 그대로 그린다.

 

만약 헤더끼리 접촉했을 때 하단 헤더가 상단 헤더를 밀도록 구현하는게 아닌 아래로 들어가듯이 구현하고 싶다면 contactedNewHeaer를 구할 필요없이 drawHeader만 사용하면 된다.

 

 

 

 

 

 

원본 코드 및 참고한 링크

 

원본코드

원본코드에서 필요하다고 생각하는 코드로만 다시 정리했다.

https://gist.github.com/filipkowicz/1a769001fae407b8813ab4387c42fcbd

 

Item Decorator for sticky headers in Kotlin

Item Decorator for sticky headers in Kotlin. GitHub Gist: instantly share code, notes, and snippets.

gist.github.com

 

참고한 링크

StickyHeader를 구현하는 여러 코드들이 있다.

https://www.it-swarm.dev/ko/android/recyclerview%EC%97%90%EC%84%9C-%EB%81%88%EC%A0%81%ED%95%9C-%ED%97%A4%EB%8D%94%EB%A5%BC-%EB%A7%8C%EB%93%A4%EB%A0%A4%EB%A9%B4-%EC%96%B4%EB%96%BB%EA%B2%8C%ED%95%B4%EC%95%BC%ED%95%A9%EB%8B%88%EA%B9%8C-%EC%99%B8%EB%B6%80-lib%EC%97%86%EC%9D%B4/1055032283/

 

android — RecyclerView에서 끈적한 헤더를 만들려면 어떻게해야합니까? (외부 lib없이)

에야디야, 이것은 스크린에서 벗어나기 시작할 때 한 종류의 홀더 스틱을 원한다면 (우리는 어떤 섹션도 신경 쓰지 않습니다) 당신이 그것을하는 방법입니다. 재활용 아이템의 내부 RecyclerView 로

www.it-swarm.dev

 

 

'Android > UI' 카테고리의 다른 글

BottomSheet  (0) 2020.08.10

 

 

 

 

dependency

 

implementation 'com.google.android.material:material:1.3.0-alpha02'

 

 

 

BottomSheetDialog

 

 

기존의 다이얼로그 사용법과 동일하다.

 

        BottomSheetDialog(this).apply {
            val view = layoutInflater.inflate(R.layout.view_bottom_sheet, null)
            view.btn_view_bottom_sheet.setOnClickListener {
                Toast.makeText(this@MainActivity, "FIN", Toast.LENGTH_SHORT).show()
                dismiss()
            }
            setContentView(view)
        }.show()
cs

 

 

 

 

 

 

BottomSheet With Behavior

 

 

 

activity_main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <!--  Content  -->
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="50dp">
 
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello, World!"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent" />
 
    </androidx.constraintlayout.widget.ConstraintLayout>
 
    <!--  BottomSheet  -->
    <LinearLayout
        android:id="@+id/ll_main_bottom_sheet"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#66A9EC"
        app:behavior_hideable="true"
        app:behavior_peekHeight="50dp"
        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
 
        <TextView
            android:id="@+id/tv_main_bottom_sheet_title"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:text="TODO List"
            android:textSize="20dp"
            android:textColor="@android:color/black"
            android:textStyle="bold"/>
 
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_main_bottom_sheet"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:paddingBottom="20dp"
            android:clipToPadding="false"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
 
    </LinearLayout>
 
</androidx.coordinatorlayout.widget.CoordinatorLayout>
cs

 

 

behavior_hideable : 완전하게 숨길 수 있을지 없을지를 정한다.

behavior_peekHeight : 최소 높이를 정한다.

layout_behavior : 행동을 정한다. 물론 BottomSheet을 만들기 위해서이기 때문이니

  com.google.android.material.bottomsheet.BottomSheetBehavior로 값을 넣어준다.

 

 

 

 

 

 

MainActivity.kt

 

class MainActivity : AppCompatActivity() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
 
        initViews()
    }
 
    private fun initViews() {
        rv_main_bottom_sheet.adapter = TodoRecyclerAdapter {
            Toast.makeText(this"$it clicked", Toast.LENGTH_SHORT).show()
            hideBottomSheet()
        }.apply {
            updateDataSet(sample)
        }
 
        tv_main_bottom_sheet_title.setOnClickListener {
            showBottomSheet()
        }
    }
 
    private fun showBottomSheet() {
        BottomSheetBehavior.from(ll_main_bottom_sheet).let {
            if (it.state != BottomSheetBehavior.STATE_EXPANDED) {
                it.state = BottomSheetBehavior.STATE_EXPANDED
            }
        }
    }
 
    private fun hideBottomSheet() {
        BottomSheetBehavior.from(ll_main_bottom_sheet).let {
            if (it.state != BottomSheetBehavior.STATE_COLLAPSED) {
                it.state = BottomSheetBehavior.STATE_COLLAPSED
            }
        }
    }
 
    private val sample = IntRange(020).map { "Job $it" }.toList()
 
}
cs

 

 

기본적으로 드래그 액션을 통해서 BottomSheet을 제어할 수 있지만

코드를 통해서 특정상황에 확장하거나 줄일 수 있다.

 

 

 

'Android > UI' 카테고리의 다른 글

RecyclerView - StickyHeader  (0) 2020.08.17

 

 

 

 

프리뷰 홈

https://developer.android.com/preview

 

Android 개발자  |  Android Developers

테스트, 개발 및 의견 제공을 위해 Android 차기 버전을 미리 살펴봅니다.

developer.android.com

 

 

 


 

 

 

Component Changing

 

Window Insets

IME Animation

IME를 에니메이션을 커스텀해 좀 더 뷰와 interaction한 UI를 만들 수 있다.

Insets에서 IME에 대한 정보를 통해 동기화된 UI를 만들 수 있다.

 

Conversations

기본적인 Notification이 아닌 Conversation을 통해 커뮤니케이션이 필요한 내용을 따로 관리하고 더 많은 정보를 보여줄 수 있다.

 

Bubbles

커뮤니케이션을 하기위한 depth를 줄일 수 있는 컴포넌트

기존에는 개발자 모드에서 사용할 수 있는 기능이었지만 11부터는 모든 사용자가 사용할 수 있다.

 

 

 

Privacy

 

Data Access Auditing

 

One-Time Permission

기존의 지속적인 권한허용이 아닌 1회용 권한허용 추가

 

Background Location

11에서는 더욱 제한적

우선 포그라운드에서 권한을 허용해야하고 백그라운드에서 사용하기 위해서는 한번 더 설정창(앱 내부가 아닌)에서 허용해야 한다.

 

Package visibility restrictions

패키지에 접근하기 위해서는 메니페스트 설정이 필요한듯 보인다.

 

Scoped Storage

MedialStore API로 편집하는 기능이 추가

 

Auto-reset permissions

앱을 오랜기간 사용하지 않는다면 권한 설정이 초기화될 수 있다.

 

 

 

Utility for Developer

 

Wi-Fi Debugging

현재는 수동으로 설정해야 하지만 Canry에서는 도구를 이용해 접근이 가능하다.

 

Nullability Annotations

@RecentlyNullable, @RecentlyNonNull makes Warnings

@Nullable, @NonNull makes Errors

 

Crash Reasons Reporting

앱에 문제가 생겼을 때 더 자세한 정보를 얻을 수 있다.

 

GWP-ASan

HWASan in Andorid10

 

ADB Incremental

용량이 큰 앱을 위해 10배 더 빠른 설치가 가능하게 해준다.

 

 

 

Graphics & Media

 

NDK Image Decoder 해독기를 직접 사용할 수 있다.

Animated HEIF GIF보다 가벼운 HEIF를 디코딩하여 에니메이션이 가능하다.

OpenSL ES deprecated -> Oboe

 

 

 

기타

 

Neural Networks API 머신러닝을 위한 API

5G 코드를 통해 네트워크가 5G를 지원하는 상황인지에 대한 결정할 수 있다.

Autofill/Keyboard Integration 

 

 

 

Jetpack

 

Hilt : Dagger에 설치된 의존성 라이브러리, DI 작업에 권장하는 새로운 방식

Paging

CameraX beta

Compose pre-alpha version

 

 

 

Android Studio

 

4.0 : Stable

Motion Editor

LayoutInspector

 

4.1 : Beta

DatabaseInspector(Room, SQLite) 기존의 다운로드 받아서 확인해야했던 방식에서 도구로 내장되었다.

 

4.2 : Canary

Wireless debugging with Android11

Jetpack Compose development

 

 

 

Google Play

 

New Play console beta

https://play.google.com/apps/

 

로그인 - Google 계정

하나의 계정으로 모든 Google 서비스를 Google 계정으로 로그인

accounts.google.com

 


 

AndroidDevelopers Youtube

위의 기본 소개영상 외에 분야별 영상들도 있다.

https://www.youtube.com/c/AndroidDevelopers/videos

 

Android Developers

Welcome to the official Android Developers YouTube channel. Get the latest Android news, best practices, live videos, demonstrations, tutorials, and more. Su...

www.youtube.com

 

'Android > Version' 카테고리의 다른 글

Q -> Android 10  (0) 2019.06.01
Pie_파이  (0) 2018.06.07
Oreo_오레오  (0) 2018.06.06
Nougat_누가  (0) 2018.06.06
Marshmallow_마시멜로  (0) 2018.06.06

 

 

 

 

이름 그대로 인스턴스가 만들어진 순간부터 소멸하는 순간까지의 루틴을 의미한다.

중요하고 반복적으로 발생하는 상황에 대한 처리를 위해 만들어졌다고 생각한다.

 

수명주기는 보통 많은 리소스를 가지고 있는 컴포넌트들이 가지고 있다.

그렇기 때문에 컴포넌트의 상태에 따라 필요없어지는 리소스 해제를 통해 가용공간을 늘리거나 컴포넌트의 소멸시 리소스 해제를 통해 메모리 누수를 막을 수 있다.

 

 

 


 

액티비티

 

 

 

 

onCreate / onDestroy : 액티비티가 생성되고 종료되는 순간에서의 처리

onStart / onStop : 액티비티가 조금이라도 화면에 보이는 순간과 보이지 않는 순간에서의 처리

onResume / onPause : 액티비티가 사용자와 상호작용을 할 수 있는 순간과 없는 순간에서의 처리

 

보통 위와 같이 짝으로 묶는다.

할당과 해제, 시작과 종료가 필요한 조합이기 때문이다.

 

 

 

https://developer.android.com/guide/components/activities/activity-lifecycle?hl=ko

 

Activity 수명 주기에 대한 이해  |  Android 개발자  |  Android Developers

An Activity is an application component that provides a screen with which users can interact in order to do something, such as dial the phone, take a photo, send an email, or view a map. Each activity is given a window in which to draw its user interface��

developer.android.com

 

 

 


 

프래그먼트

 

 

https://developer.android.com/guide/components/fragments?hl=ko

 

프래그먼트  |  Android 개발자  |  Android Developers

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section��

developer.android.com

 

 

 


 

 

 

 

https://www.charlezz.com/?p=29013

 

Android에서 View의 생명주기 | 찰스의 안드로이드

https://proandroiddev.com/the-life-cycle-of-a-view-in-android-6a2c4665b95e을 번역한 내용입니다. 안드로이드 앱을 실행할 때 우리가 가장 먼저 스크린에서 볼 수 있는 것이 View라고 말할 수 있다. View 클래스는 사�

www.charlezz.com

 

https://hyeonu1258.github.io/2018/03/26/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C%20%EB%A9%B4%EC%A0%91/

 

안드로이드 면접 | 현우의 개발노트

View가 그려지는 과정 뷰는 포커스를 얻으면 레이아웃을 그리도록 요청한다. 이때 레이아웃의 계층구조 중 루트 뷰를 제공해야한다. 따라서 그리기는 루트노드에서 시작되어 트리를 따라 전위 ��

hyeonu1258.github.io

 

'Android > Common' 카테고리의 다른 글

비동기  (0) 2020.05.14
안드로이드 개발자 로드맵  (2) 2020.05.07
안드로이드 프로젝트 루틴  (0) 2020.02.20

 

관련 키워드

Thread, UiThread, MainThread, Jank, ANR, Asynchronous, Synchronous, Blocking, Non-Blocking, Concurrency, Parallelism, Handler, Looper, MessageQueue, AsyncTask

 

 


왜 비동기가 필요한가?

 

안드로이드에서는 UI를 그리는 메인스레드가 매우 중요하다.

 

애플리케이션이 실행되고 메인액티비티가 메모리에 로드되면

메인스레드를 포함한 프로세스가 생성된다.

 

안드로이드는 초당 60프레임을 지원하고 한 프레임은 16ms안에 그려져야 한다.

16ms 안에 화면을 그리는 작업이 완료되지 않으면 쟁크가 발생한다.

(쟁크; jank : 프레임 누락으로 인해 끊겨보이는 현상)

5초 이상의 긴 작업을 메인스레드에서 실행한다면 ANR이 발생한다.

(ANR; Application Not Responding)

 

결국 안드로이드에서 UI스레드가 원활하게 돌아가기 위해서는 비동기가 필수로 사용된다.

 

 

 

 

 


그럼 비동기는 무엇인가?

 

동기(Synchronous)

작업을 수행하고 그 작업이 완료될 때까지 다른 작업을 하지못하고 기다리는 방식

 

예를 들어 편의점에서 혼자 일을 한다면 카운터 일 물건 정리나 청소 등의 일을 하는 동안 전혀할 수 없다.

즉, 메인스레드가 UI를 그려야만 하는데 무거운 작업(시간이 오래걸리는)을 한다면 그 동안 UI를 그릴 수 없어 화면이 멈춰있을 것이다.

 

 

비동기(Asynchronous)

어떤 작업을 수행하지만 완료와 상관없이 계속해서 작업을 할 수 있는것

 

편의점이 바빠 한 명의 직원을 더 고용했다. 한명은 계속해서 카운터 일을 보고 물건 정리나 청소 등의 일은 다른 직원에게 시키면 카운터가 비는 일이 없다.

메인 스레드는 계속해서 UI를 그리고, 무거운 작업은 추가적인 스레드에게 시키면 된다.

 

 

 

 

사실 위에서 설명한 동기는 Sync-Bloking이고, 비동기는 Async-NonBlocking이다.

Sync-NonBlocking, Async-Blocking은 거의 사용되지 않기 때문에 우선은 위에 설명만 알아도 된다.

하지만 더 자세히 알고 싶다면 아래 포스팅 참조

 

Sync, Async, Blocking, NonBlocking 추천 포스팅

https://homoefficio.github.io/2017/02/19/Blocking-NonBlocking-Synchronous-Asynchronous/

 

Blocking-NonBlocking-Synchronous-Asynchronous

꽤 자주 접하는 용어다. 특히나 요즘들어 더 자주 접하게 되는데, 얼추 알고는 있고 알고 있는게 틀린 것도 아니지만, 막상 명확하게 구분해서 설명하라면 또 만만치가 않은.. 그래서 찾아보면 ��

homoefficio.github.io

 

 

 

 

 


그럼 어떻게 구현하지?

 

무거운 작업을 새로운 스레드에서 한다고 치자.

작업이 끝나고 그 결과를 가지고 UI에 보여주고 싶다.

UI관련 작업은 메인스레드에서만 해야하는데 어떻게 보여줄까?

 

이때 Handler가 필요로 하다.

 

Handler는 스레드의 작업을 관리하고 스레드간에 작업을 전달하기 위한 용도로 존재한다.

 

굉장히 오랜시간동안 Handler가 UI를 변경하기 위한 용도라고만 생각해왔다.

작업스레드에서 UI를 처리하기 위해 주로사용하다 보니 그런 고정관념이 생겼던 것 같다.

 

 

새로운 스레드에서 작업을 마치면 결과를 가지고 변경할 UI 작업을

Handler를 통해 메인스레드에 전달하면 된다.

 

 

 

 

 


Handler 예시

 

    private val handler = MainHandler(this)

    companion object {
        const val HANDLE_WORK_UPDATE_PROGRESS = 1
        const val HANDLE_WORK_DONE = 2

        class MainHandler(
            activity : MainActivity
        ) : Handler() {
            private val activity = WeakReference<MainActivity>(activity)
            override fun handleMessage(msg: Message) {
                when(msg.what) {
                    HANDLE_WORK_UPDATE_PROGRESS -> activity.get()?.updateProgress(msg.obj as Int)
                    HANDLE_WORK_DONE -> activity.get()?.showDoneMessage()
                }

            }
        }
    }

    private fun processAndUpdateUI() {
        Thread {
            for(i in 0..10) {
                Thread.sleep(1000L)

                handler.obtainMessage().apply {
                    what = HANDLE_WORK_UPDATE_PROGRESS
                    val progress = i*10
                    obj = progress
                }.let {
                    handler.sendMessage(it)
                }
            }
            handler.sendEmptyMessage(HANDLE_WORK_DONE)
        }.start()
    }

    fun showDoneMessage() {
        tv_main_state.text = "completed"
    }

    fun updateProgress(progress: Int) {
        tv_main_state.text = "progress $progress"
    }

 

processAndUpdateUI()에서 10초동안 작업이 진행하며

매 1초마다 진행정도를 전달하는 UI작업 (HANDLE_WORK_UPDATE_PROGRESS)

10초가 모두 지나고 완료를 표시하는 UI작업 (HANDLE_WORK_DONE)

두가지를 Handler를 통해 처리하는 간단한 예시이다.

 

Handler에 메세지 외에도 Runnable을 던질 수도 있다.

 

AsyncTask, runOnUiThread 역시 내부적으로는 Handler를 이용하고 있다.

 

 

 

 

 


Handler의 동작 원리

 

 

위에 그림에서 Handler, MessageQueue, Looper역할을 알아야 한다.

 

 

1. Handler를 통해 스레드의 외부 혹은 내부에서 작업을 받는다.

2. 처리해야할 작업을 MessageQueue에 enqueue한다.

3. Looper는 처리해야할 작업이 있는지 계속해서 확인하고 Handler에게 처리를 맡긴다.

4. Handler는 해당 작업에 맞는 처리를 한다.

 

 

메인스레드에서 위와 같이 동작한다고 보면된다.

 

우리가 새로 만드는 작업 스레드는 여기서 Looper가 없기 때문에 직접 만들고 loop해줘야 한다.

그렇기 때문에 Looper를 포함한 HandlerThread를 지원한다. 

 

 

추천 포스팅

https://academy.realm.io/kr/posts/android-thread-looper-handler/

 

안드로이드 백그라운드 잘 다루기 Thread, Looper, Handler

안드로이드 백그라운드 잘 다루기 Thread, Looper, Handler

academy.realm.io

 

 

 

 


실제 비동기 사용

 

위에 Handler 예시를 LiveData를 사용해서 바꿔보았다.

 

    private val stateText = MutableLiveData<String>()

    private fun observe() {
        stateText.observe(this, Observer {
            tv_main_state.text = it
        })
    }

    private fun initViews() {
        btn_main_update_ui.setOnClickListener { processAndUpdateUI() }
        stateText.value = "start progress"
    }

    private fun processAndUpdateUI() {
        Thread {
            for(i in 0..10) {
                Thread.sleep(1000L)
                stateText.postValue("progress ${i*10}%")
            }
            stateText.postValue("done")
        }.start()
    }

 

LiveData를 공부없이 써서 맞게 쓴것인지 모르겠지만..

대략 반으로 코드 라인이 줄었다.

 

실제 회사에서 Handler를 UI 변경을 위해 사용하는 일은 드물 것이라 생각한다.

비동기를 더 쉽게하기 위한 정말 많은 기술과 조합이 있기 때문이다.

 

대표적으로 사용하는 비동기 기술로 Coroutine과 Rx가 있다.

 

 

 

 

 

'Android > Common' 카테고리의 다른 글

수명주기; Lifecycle  (0) 2020.06.22
안드로이드 개발자 로드맵  (2) 2020.05.07
안드로이드 프로젝트 루틴  (0) 2020.02.20

 

 

안드로이드 개발자로서 잘하는 개발자가 되기 위해선 무엇이 필요할까?

체워도 체워도 언제나 고민이다.

 

우연히 찾게된 안드로이드 개발자 로드맵 PPT이다.

네이버 테크 콘서트에서 발표한 내용을 담은 PPT였다.

더 검색해보니 영상도 찾을 수 있었다.

 

영상을 보면서 나오는 내용들 대부분은 접하거나 공부한 내용이다.

하지만 부족한 부분도 있고 공부했다 해도 완벽하게 설명할 수 있는가 하면 아니다.

 

안드로이드 개발자로서 튼튼한 기본기를 가지고

이를 바탕으로 더 많은 것을 할 수 있는 개발자가 되기를 바란다.

 

발표한 내용을 바탕으로 포스팅을 정리해보려 한다.

 

 

PPT

https://www.slideshare.net/NaverEngineering/techcon-2019-mobile-android3

 

[TECHCON 2019: MOBILE - Android]3.안드로이드 개발자 로드맵

NAVER WEBTOON | Son App Tech | 안중환 안드로이드개발자로드맵

www.slideshare.net

 

영상

https://tv.naver.com/v/9329737/list/486582

 

TECH CONCERT: MOBILE 2019 - 안드로이드 개발자 로드맵

NAVER Engineering | 내용 네이버 안드로이드 개발자 3년차가 되는 과정에서 졸업생은 무엇을 준비하고, 어떤 걸 공부해야 하는지 키워드와 노하우를 공유합니다.

tv.naver.com

 

 


기본기

 

CS(Computer Science)

DB + OS + Network + Algorithm + Data Structures

 

개발자라면 너무 당연히 접해본 것들이다.

대학교에서 공부했고 취업을 하기 위해 공부했고

실무를 하면서도 계속 부족하다고 느끼는 기본 중의 기본이다.

 

 

T자형 인재

DB + OS + Network + Algorithm + Data Structures + Git

Framework + Programming Language

 

Git을 사용하지 않는 회사는 거의 없다라고 생각한다.

Git까지 공부하고 나면 어떤 언어로 어떤 것을 개발할지 확장해 공부한다.

 

 

 

 


키워드

 

안드로이드 개발자로 정했다.

그렇다면 관련 키워드는 어디에서 나올까?

 

GoogleIO : 매년 구글의 기술을 설명하는 컨퍼런스

Android Dev Summit : 개발에 관해 집중적으로 발표하는 컨퍼런스

 

위의 컨퍼런스들을 통해 새로운 키워드들이 등장한다.

뒤늦게 대세가 된 후 접하기 보단 더 빠르게 기술에 대해 인지할 수 있다.

 

 

Android Developers Youtube : 컨퍼런스 발표 동영상이 올라와 있다.

https://www.youtube.com/user/androiddevelopers/videos

 

Android Developers

Welcome to the official Android Developers YouTube channel. Get the latest Android news, best practices, live videos, demonstrations, tutorials, and more. Su...

www.youtube.com

 

위의 컨퍼런스 외에 한국에서 진행하는 컨퍼런스들도 있다.

드로이드 나이츠, 코틀린 나이트, GoogleIO in Seoul, if kakao, Naver Deview

 

항상 관심을 가지고 참석해보도록 하자.

 

 

 

 

그 외에 사이트로

 

Android Weekly : 메일을 통해 안드로이드 뉴스를 전달해준다.

https://androidweekly.net/

 

Android Weekly - Free weekly Android & Kotlin development newsletter

Android Weekly is a free newsletter that helps you to stay cutting-edge with your Android Development.

androidweekly.net

 

 

 

 


안드로이드

 

개발을 하다보면 구글링이 필수가 된다.

 

하지만 구글링으로 나오지 않을 수도 있고 잘못된 정보를 다루고 있을 수 도 있다.

그렇기 때문에 공식 문서부터 찾아보는 방법을 권한다.

 

 

새로운 API를 접할 때

1. Android Developer 검색

2. Android Developer Medium, Blog 검색

3. CodePath Android Cliffnotes 검색

4. 구글링

 

 

Android Developer : 최신 정보는 영어로 먼저 나오기 때문에 영어로 보길 권장한다.

 - Android Studio / User Guide

 - Jetpack

 - Kotlin

 - Docs / Guides, References, Samples

https://developer.android.com/

 

Android 개발자  |  Android Developers

Android 앱 개발자를 위한 공식 사이트입니다. Android SDK 도구 및 API 문서를 제공합니다.

developer.android.com

 

Android Developer Medium

https://medium.com/androiddevelopers

 

Android Developers – Medium

The official Android Developers publication on Medium.

medium.com

 

Android Developer Blog

https://android-developers.googleblog.com/

 

Android Developers Blog

 

android-developers.googleblog.com

 

Google Codelabs : 코드베이스로 익혀나가고 싶다면...

https://codelabs.developers.google.com/

 

Google Codelabs

Google Developers Codelabs provide a guided, tutorial, hands-on coding experience. Most codelabs will step you through the process of building a small application, or adding a new feature to an existing application. They cover a wide range of topics such a

codelabs.developers.google.com

 

CodePath Android Cliffnotes : 사용법, 꿀팁, 관련 오픈소스

https://guides.codepath.com/android

 

Home | CodePath Android Cliffnotes

CodePath Android Cliffnotes Welcome to the open-source CodePath Android Cliffnotes! Our goal is to become the central crowdsourced resource for complete and up-to-date practical Android developer guides for any topic. Just take me to the notes! We have And

guides.codepath.com

 

 

기본적인 내용

4대 컴포넌트 : Activities, Services, BroadcaseReceivers, ContentProviders

Jetpack : Fragment, ConstraintLayout, ViewPager, RecyclerView...

Inflater

View

 

Inflate, View

 - XML -> Inflate -> 화면을 객체 형태로 만듬

 - 비용이 있는 동작

 - Layout Inspector로 만든 화면 보기 

 - 화면은 모두 View로 되어있다.

 - 화면은 View의 트리구조형태이며 전위 순회하며 깊이우선탐색하여 처리해나아간다.

 - 렌더링 : measure > layout > draw

 - Fragment 역시 View : 즉 Fragment보다 View를 재활용 단위로 가져가는 것이 좋다.

   View를 사용하면 xml에 넣거나 동적생성하여 재활성이 좋아진다.

 

 

 

 


비동기

  안드로이드의 꽃!   

 

 

비동기관련 기술들

 - Thread

 - Executor

 - CompletableFuture(java8, sdk>=24)

 - AsyncTask

 - Loader

 - WorkManager

 - Rx

 - Coroutine

 

AndroidDeveloper의 비동기 관련 내용

https://developer.android.com/

 

Docs - Core topics - Activities - Processes and app lifecycle

 - foreground process

 - visible process

 - service process

 - cached process

Docs - Core topics - Background tasks - Sending operations to multiple threads - Communicate with the UI thread

 - UI Thread 의 정의와 통신하는 방법

 - Handler, Looper, MessageQueue

Docs - Besr Practice - Processes and Threads Overview

 - Thread 기본 사용법

 

 

Callback에 대한 이해

 - Runnable 왜 있을까?

 - subroutines, lamda, expressions, blocks, function pointers...

 

concurrency vs parallelism

 

synchronous vs asynchronous

 

blocking vs non-blocking

 

Java Concurrent

 

#synchronized #volatile #atomic #semaphore #ReentrantLock #LockFree #FalseSharing #ForkJoinPoll #Executor #BlockingQueue #CountDownLatch #Future ...

 

 

Thread, MessageQueue, Handler, Looper에 대한 이해

#Consumer-Producer Patter #MainThread #UiThread #MessageQueue #Handler #MainLooper #Looper #HandlerThread

https://academy.realm.io/kr/posts/android-thread-looper-handler/

 

안드로이드 백그라운드 잘 다루기 Thread, Looper, Handler

안드로이드 백그라운드 잘 다루기 Thread, Looper, Handler

academy.realm.io

 

 

위에 내용을 이해하고 Rx, Coroutine을 사용하자

 

 

 

정확한 타이밍에 실행이 보장되길 원한다면

  : Foreground Service

특정 조건을 걸고 실행이 보장되길 원한다면

  : JobScheduler, JobDispatcher, AlarmManager, BroadcastReceivers

 

 

WorkManager in Jetpack

 - 기존의 파편화된 백그라운드 처리를 하나로 모아서 실행 

 

 

 

 


성능 최적화

 

초당 60프레임 : 프레임당 16ms

한 프레임에 16ms보다 더 오래 걸리는 경우 프레임 드랍이 발생

 

프레임 드랍이 발생한다면

 - MainThread -> WorkerThread

 - UI 업데이트가 순차적으로 진행될 수 있도록 조절

 - CPU -> GPU

 - GPU에 안그려도 되는 부분을 알려준다

 

#ViewHierarchy #DoubleLayoutTaxation #Async #Overdraw #AnimationProperty #Cache #Clipping #ObjectPool #HardwareAcceleration #VSYNC

 

 

발표에서 보여주고자 하는 동영상이 어디있는지 모르겠지만

같은 내용을 다루는 듯 하다.

https://www.youtube.com/watch?time_continue=8&v=qk5F6Bxqhr4&feature=emb_title

 

 

 


안드로이드 프레임워크

 

우리가 사용하는 부분은 Application Framework까지이다.

 

NDK 개발시에는 필수로 더 로우레벨까지 공부해야 한다.

 

 

Android Open Source Project

 - Cofigure / RUNTIME : 컴파일러 관련 내용

#.class #.dex #Dalvik #ART #D8 #R8 #AOT #JIT

 - Develop / GRAPHICS : 화면 렌더링 관련 내용

#SurfaceView #TextureView #BufferQueue, #SurfaceFlinger, #Surface, #Canvas, #SurfaceHolder 

 - Develop / ARCHITECTURE : IPC 관련 내용

#HAL #Context #Parcel # BinderIPC #SystemServices #MediaServer #SystemServer

https://source.android.com/ 

 

Android 오픈소스 프로젝트  |  Android Open Source Project

Android는 세계를 하나로 묶어줍니다. 오픈소스 Android 운영체제를 사용하여 강력한 기기를 만드세요.

source.android.com

 

 


언어

 

Kotlin이 인기가 많다.

Kotlin을 하려면 Java도 알아야 한다.

함수형 프로그래밍을 사용하기에는 Java보다는 Kotlin을 사용하는 것이 났다.

 

 

프로그래밍 패러다임

 - 기능에 따라 프로그래밍 언어를 분류하는 방법. 언어는 여러 패러다임으로 분류 할 수 있다.

 - 프로그래밍 관점을 갖게 해 주고, 결정하는 역할

 - 서로 다른 프로그래밍 언어는 서로 다른 프로그래밍 패러다임을 지원

 - 새로운 것을 가능하게도 하지만 어떤 기법을 금지 함

 

#StructuredProgramming #Object-OrientedProgramming #FunctionalProgramming #ReactiveProgramming
#FunctionalReactiveProgramming #Modular Programming

 

 

 

Kotlin과 Java는 객체지향프로그래밍, 함수형 프로그래밍패러다임을 가지고 있다. 

 

 

객체지향프로그래밍

 

#OOP #클래스 #객체 #메시지 #추상화 #캡슐화 #상속 #구성 #다형성 #S.O.L.I.D

 

 

디자인 패턴

 

SourceMaking

https://sourcemaking.com/

 

Design Patterns and Refactoring

Design Patterns and Refactoring articles and guides. Design Patterns video tutorials for newbies. Simple descriptions and full source code examples in Java, C++, C#, PHP and Delphi.

sourcemaking.com

 

 

 

함수형프로그래밍

 - 클래스가 아닌 함수가 재활용 단위

 - 함수는 1급 객체

 

#람다 #클로저 #순수함수 #고차함수 #lazy #커링 #재귀 #메모이제이션 #모나드

 

 

 


Best Practice

 

클린 아키텍처

클린 코드

리팩토링

 

굉장히 유명한 책들이다.

하지만 많은 학습이 전제되지 않은 상태에서는 의미가 없을 수 있다.

 

그러니 우선 이것만 해보자!

 - 함수는 한가지 일만 한다.

 - SRP : 클래스는 하나의 책임만 가진다.

 - 상속 대신 구성

 

 

드로이드 나이츠에서 발표한 Clean Arhcitecture

(몇년전 발표. 첫 드로이드 나이츠로 기억하고 있다.)

https://academy.realm.io/kr/posts/clean-architecture-in-android/

 

안드로이드에 Clean Architecture 적용하기

안드로이드 앱을 만들면서 두 번의 대규모 업데이트를 배경으로 코드가 계속 바뀌었고, 이런 변화하는 코드에 잘 대응할 수 있는 Clean Architecture에 대해 고민한 경험에 대해 공유하고자 합니다.

academy.realm.io

 


TODO

키워드 수집

기록해라 : 블로그, Git과 같은 것들로 기록해라

 

테스트

 - 유닛테스트를 작성해보자

 - 좋은 구조로 가는 지름길이 되기도 한다.

 - CI

 

#TDD #BDD #Junit #Dummy #Stub #Spy #Fake #Mock #Mockito #PowerMock #Robolectric #CI #Jenkins #TestCoverage #Jacoco

 

 

 

위에 내용들을  완벽 하게 익혀다고 생각한다면

다양한 기술들을 공부해 스펙트럼을 넓혀보자!

 

 

 

 

 

'Android > Common' 카테고리의 다른 글

수명주기; Lifecycle  (0) 2020.06.22
비동기  (0) 2020.05.14
안드로이드 프로젝트 루틴  (0) 2020.02.20

 

WARNING

 

 

App is not indexable by Google Search; consider adding at least one Activity with an ACTION-VIEW intent filter. See issue explanation for more details.

inspection info:Adds URLs to get your app into the Google index; to get installs and traffic to your app from Google Search

 

issue id GoogleAppIndexingWarning

 

More info:

https://g.co/Appindexing/AndroidStudio

 

 

 


WHY

 

링크를 들어가 보면 앱 링크 추가에 관한 내용이 나온다.

 

앱 링크란 앱의 특정 컨텐츠로 사용자를 바로 안내하는 HTTP URL이다.

해당 기능을 통해 앱에 더 접근하기 쉽고 더 많은 유입을 이끌어 낼 수 있다.

 

다만 웹과 앱을 동시에 보유한 프로젝트에 한해 가능해 보인다.

 

여기서 구글 검색에서 해당 컨텐츠에 도달하기 위해 ACTION_VIEW 인텐트가 필요하다.

기본 설정으로 최소 하나 이상의 Activity에 ACTION_VIEW 인텐트 필터가 필요하다.

 

 

 


SOLUTION

 

 

<action android: name="android.intent.action.VIEW" />

위 코드를 한 개 이상의 Activity에 추가하면 warning을 해결할 수 있다.

 

 

 


ADVANCE

 

앱 링크에 대해 더 자세히 알고 싶다면...

 

https://developer.android.com/training/app-links

 

Android 앱 링크 처리하기  |  Android 개발자  |  Android Developers

기기에서 웹 링크를 따라가는 사용자는 종종 혼란스러운 선택에 직면합니다. 링크를 탭하면 시스템에서 사용자에게 링크를 처리할 앱을 지정해 줄 것을 요청하는 경우가 자주 있습니다. 예를 들어 은행에서 온 이메일에 포함된 URI를 클릭할 때 다음과 같은 대화상자가 열릴 수 있습니다.

developer.android.com

 

https://developer.android.com/training/app-links/deep-linking

 

앱 콘텐츠 딥 링크 만들기  |  Android 개발자  |  Android Developers

사용자가 링크에서 앱에 진입할 수 있도록 하려면 관련 활동의 인텐트 필터를 앱 manifest에 추가해야 합니다. 이러한 인텐트 필터는 모든 활동의 콘텐츠로 연결되는 딥 링크를 허용…

developer.android.com

 

https://developer.android.com/studio/write/app-link-indexing

 

Android 앱 링크 추가  |  Android 개발자  |  Android Developers

Android 앱 링크는 Android 앱의 특정 콘텐츠로 사용자를 바로 안내하는 HTTP URL입니다.

developer.android.com

 

 

 

 

 

프로젝트를 만드게 되면 진행하는 일련의 과정을

루틴으로 만들고 계속 개선하려고 한다.

 

1. 프로젝트 생성, GIT 연결

2. 개발환경 설정

3. 개발 구조

4. 테스트

 


프로젝트 시작

 

프로젝트 생성

 

 - 패키지 이름, 프로젝트 이름

 

 

Git

 

Git Issue 생성

 - 개발 환경 설정 생성

 

.gitignore 설정 https://www.gitignore.io/

 

gitignore.io

Create useful .gitignore files for your project

www.gitignore.io

 

gitflow(개념만 사용)

 - 생성 프로젝트 mater branch 업로드

 - develop branch 생성, 업로드

 

 

 

 

 


개발 환경 설정

 

feature/environment 생성

 

 

gradle 설정

 

gradle 버전 관리 파일(dependencies.gradle) 생성

 - 모든 버전 reference로 사용

 - 앱 버전 0.1 시작

 

문서화 : gradlw dokka

 - apply plugin: 'org.jetbrains.dokka'

 - classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"

 

코드 검사 : gradlew lint

 

사용할 라이브러리 추가

 - anko, koin, rxandroid, rxjava, rxbinding...

 

 

 

기본 파일 구성

 

resource 설정

 - strings(ko) 추가

   : 아주 조금이라도 해외에서 앱이 쓰일 수 있다면 default를 영어로 한글은 strings(ko)를 사용하자.

 - dimens 추가

 

BaseColor 설정

https://material.io/resources/color/#!/?primary.color=252F4A&secondary.color=22B94E&view.left=0&view.right=1

 

Color Tool - Material Design

Create and share color palettes for your UI, and measure the accessibility of any color combination.

material.io

 

패키지 구성 및 필요한 기본 클래스들 추가 (Rx 기반)

component

 - BaseActivity

 - BasePresenter

 - BaseView

 - BaseViewModel

ext

 - LifecycleExtensions

 - RxExtensions

 - ViewExtensions

util

 - AutoClearedDisposable

ui

 - custom

 - main

Constants(optional)

App

 

 

Manifest Warning 제거

 - fullBackupContent 설정

 - MainActivity View action 추가

 

 

 

 

 


개발 구조

 

 

MVP, MVVM, Clean Architecure, Rx, LiveData, AAC, DataBinding, Coroutine, DI

위의 기술들을 어떻게 접목하느냐에 따라 천차만별의 구조가 나오게 된다.

 

심지어 MVP만 보아도 사용하는 방법다 가지각색이다.

Contract interface 안에 View, Presenter interface를 사용할 수도 있고

View만 추상화해서 사용할 수도 있다.

 

하지만 결국 몇가지 공통적인 문제들을 해결하기 위한 표현이 다를뿐이라고 생각한다.

 

1. 뷰와 모델, 그리고 이 둘을 어떻게 연결할까?

2. 비동기 처리

3. 응집도는 강하게 결합도는 약하게

 

3번이 너무 추상적이긴 하지만 이 정도가 안드로이드의 가장 근본적인 문제인 것 같다.

 

하지만 천차만별인 구조들 중에

하나의 완성된 프로젝트를 만들고 나면 다른 기술을 적용하는 것이 그리 어렵지 않다고 생각한다.

 

그러니 나만의 완성된 구조를 만들고 계속 날카롭게 다듬자.

 

 

 

 

 


테스트 코드

 

좋은 구조를 가지지 않는 이상 테스트 코드 많은 일을 만들어 낸다.

좋은 구조부터 갖자

 

 

 

 

 

 

'Android > Common' 카테고리의 다른 글

수명주기; Lifecycle  (0) 2020.06.22
비동기  (0) 2020.05.14
안드로이드 개발자 로드맵  (2) 2020.05.07

Android 10

https://developer.android.com/about/versions/10

 

Android 개발자  |  Android Developers

Android 10용으로 앱을 준비하세요!

developer.android.com

 

 


개인정보 보호

https://developer.android.com/about/versions/10/privacy

 

Android 개발자  |  Android Developers

개인정보 및 민감한 정보에 신중하게 액세스하고 사용자가 앱에 저장된 데이터를 직접 관리하고 투명하게 확인할 수 있도록 하여 사용자를 안전하게 보호합니다.

developer.android.com

 

 

 

 


동작 변경사항

https://developer.android.com/about/versions/10/behavior-changes-all

 

동작 변경사항: 모든 앱  |  Android 개발자  |  Android Developers

Android 10에는 앱에 영향을 줄 수 있는 동작 변경사항이 포함되어 있습니다. 이 문서에 나열된 변경사항은 앱의 targetSdkVersion에 관계없이 Android 10에서 실행될 때 앱에 적용됩니다. 이러한 변경사항을 적절히 지원하려면 앱을 테스트하고 필요에 따라 수정해야 합니다. 앱의 targetSdkVersion이 29 이상인 경우 추가 변경사항을 지원해야 합니다. 자세한 내용은 29를 타겟팅하는 앱의 동작 변경사항을 참조하세요. 참고: 이

developer.android.com

 

 

 

 


새로운 기능 및 API

https://developer.android.com/about/versions/10/highlights

 

개발자용 Android 10  |  Android 개발자  |  Android Developers

Android 10은 세 가지 중요한 테마를 중심으로 구축되었습니다. 첫째, Android 10은 고급 머신러닝과 폴더블 및 5G 스마트폰 등 새로운 기기 지원으로 모바일 혁신의 첨단을 구축하고 있습니다. 둘째, Android 10은 사용자에게 더욱 강화된 보호, 투명성 및 제어를 가능하게 하는 50가지에 달하는 기능으로 개인정보 보호 및 보안에 집중합니다. 마지막으로, Android 10은 개인과 가족이 기술과 더 나은 균형을 찾을 수 있도록 사용자의

developer.android.com

 

 

 

 

'Android > Version' 카테고리의 다른 글

Android 11 beta  (0) 2020.06.22
Pie_파이  (0) 2018.06.07
Oreo_오레오  (0) 2018.06.06
Nougat_누가  (0) 2018.06.06
Marshmallow_마시멜로  (0) 2018.06.06




ANDROID 9.0(API 28)


https://developer.android.com/about/versions/pie





'Android > Version' 카테고리의 다른 글

Android 11 beta  (0) 2020.06.22
Q -> Android 10  (0) 2019.06.01
Oreo_오레오  (0) 2018.06.06
Nougat_누가  (0) 2018.06.06
Marshmallow_마시멜로  (0) 2018.06.06

+ Recent posts