프로그램(Program)

특정 작업을 하기 위해 일련의 명령어 모임

응용 소프트웨어(application software) & 어플리케이션(Application)

OS 위에서 동작하는 모든 소프트웨어


프로세스(Process)

프로세스란, 컴퓨터에서 연속적으로 실행되고 있는 컴퓨터 프로그램을 의미합니다.

또한, 메모리에 올라와서 실행되고 있는 프로그램의 인스턴스 라고 할 수 있습니다.

프로세스 메모리 영역 ( 코드, 데이터, 스택, 힙 )

프로세스 구조 ( 레지스터 + 메모리영역 )

프로세스 특징

각 프로세스는 독립적이며, 서로 다른 프로세스의 자원에 사용이 불가능하다.

만약, 서로 다른 프로세스의 자원을 사용하기 위해서는 프로세스간 통신을 구현해야한다. ( IPC 통신 )

만약 프로세스간 통신을 구현했을 때, 공유자원에 대한 동시성 이슈에 대해서도 고민해야 한다.

 

 

쓰레드(Thread)

쓰레드란, 프로세스 내에서 실행되는 흐름의 단위 를 의미합니다.

일반적으로 1개의 프로그램은 1개의 쓰레드를 가지고 있지만, 1개의 프로그램환경에 따라 여러개의 쓰레드를 가질 수 있습니다.

 

쓰레드 메모리 영역 ( 스택 )

쓰레드 구조 ( 레지스터 + 메모리영역)

쓰레드 특징

각 쓰레드는 속해있는 프로세스의 Code, Data, Heap 영역에 접근할 수 있다.

따라서, 하나의 쓰레드에서 프로세스 힙영역의 자원을 수정하면, 다른 쓰레드 변경된 자원의 결과를 알 수 있다.

이는 서로 다른 쓰레드가 같은 공유자원을 서로 다른 기준으로 변경했을 때, 동시성 이슈를 발생할 수 있다.

'BackUp (관리중지) > CS 학습' 카테고리의 다른 글

GC ( Garbage Collection )  (0) 2021.04.28
동시성 이슈  (0) 2021.04.27
동기(Synchronous) / 비동기(Asynchronous)  (2) 2021.04.27
HTTP (HyperText Transfer Protocol)  (0) 2021.04.26
자료구조(Data Structure)  (0) 2021.04.26

먼저,  동기와 비동기에 들어가기에 앞서 대체 헷갈릴 수 있는 단어들의 종류를 적고 시작하겠습니다.

동기 (Synchronity) : "같은시간"에는 1개의 작업만 처리할 수 있다.

비동기 (Asynchronous) : "같은시간"에 여러개의 작업을 처리할 수 있다.

동시성 (Concurrency) : 병렬과 병행을 모두 포함하여 사용됨. (모호함)

병렬성 (Concurrency) : A작업과 B작업이 "다른시간"에 번갈아가며 동작하며, "같은시간"에 실행되는 것처럼 보이게 할 수 있음.

병행성 (Parallelism) : A작업과 B작업이 "같은시간"에 같이 실행될 수 있음.

순차적 (Sequential) : 작업이 들어오면 해당 작업이 끝날 때 까지 다른 작업을 진행할 수 없음.

 

제가 "동기와 비동기" 로 블로그를 찾아보았을 때, 이런 정의를 본적이 있습니다.

- 동기는 말 그대로 동시에 일어난다는 뜻입니다.

- 비동기는 말 그대로 동시에 일어나지 않는다를 의미합니다.

해당 글의 내용을 전부 읽었을 때, 그 설명이 틀리지는 않았다는 것을 알 수 있었습니다.

근데 뭔가 느낌이 좋지 않았습니다. 왜???? 내용을 전부 읽지 않는다면, 저 표현은 엄청난 오해(의도와 정말 극단적으로 반대되는 해석) 를 살 수 있을 것 같기 때문입니다.

 

왜 오해를 사는지 살펴보겠습니다.

 

병렬(Concurrency) 은 A 작업과 B 작업이 실행되는 것이 동시(Concurrency) 에 일어나는 것처럼 보이지만, 실제로는 순차적으로 진행됩니다. (ex.멀티쓰레드)

 

병행(Parallelism) 은 A 작업과 B작업이 실행되는 것이 동시(Concurrency) 에 일어납니다. (ex. 멀티프로세스)

 

동시성(Concurrency)은 이러한 두 상황을 통틀어서 말하는 경우가 많습니다. 

 

자, 그럼 위에 정의로 가볼까요?

 

"동기(Synchronity)는 말 그대로 동시(Concurrency)에 일어난다는 뜻입니다."

- 동기는 마치 병행을 말하는 것 같네요. 포괄적으로 본다해도, 병렬을 말하는 것 같아요.

 

"비동기(Asynchronous)는 말 그대로 동시(Concurrency)에 일어나지 않는다를 의미합니다"

- 위에서 말한 동시가 병행을 말한것이라면, 이건 병렬을 말한거겠네요.  아니면, 위에서 말한 동시가 병행,병렬을 말한거면 이 친구는 그 어디에도 속할 수 없네요. 음.. 순차적 (Sequential) 이 된다는 것일까요?

 

어떤 해석이든 둘다 문제가 있습니다.

좌측은 동기(Synchronity) 의 진행과정을 표현했으며, 우측은 비동기(Asynchronous) 의 진행과정을 표현했습니다.

(빨,노,초 작업은 각각 필요한 데이터 수신을 의미한다고 하겠습니다.)

 

아~ 그럼 동기는 순차(Sequential) 이고, 비동기는 동시(Concurrency) 인가요?

아닙니다. 다른 그림을 또 볼까요?

 

 

 

좌측은 동기(Synchronity) 의 진행과정을 표현했으며, 우측은 비동기(Asynchronous) 의 진행과정을 표현했습니다.

(빨강은 UI 그리기, 노랑과 초록은 필요한 데이터 수신을 의미한다고 하겠습니다.)

 

정리하자면,

 

1. 동기(Synchronity) 는 작업중에 다른 작업이 들어오면 해당 작업을 멈추고, 다른 작업을 진행합니다. 다른 작업을 끝낸 후에 하지못한 작업을 마무리하죠. 즉, 동시간대에는 1개의 Task 만을 수행합니다.

ex. UI 를 그리는 작업을 하는 중에 데이터를 받아오는 작업을 진행하면, UI 그리는 작업을 중지하고, 데이터 받아오는 작업을 진행한 후에 UI 그리는 작업을 다시 진행합니다.

로딩화면(빙글빙글) -> 로딩화면정지(stop) -> 데이터수신

 

2. 비동기(Asynchronous) 는 작업중에 다른 작업이 들어오면 해당 작업을 멈추는 것이아니라, 동시(Concurrency)적으로 진행합니다.

멀티프로세스에서 비동기를 진행하면 병행이 될것이고, 단일프로세스에서 비동기를 진행하면 병렬로 처리할 것입니다. 즉, 동시간대에 여러개의 Task 를 수행합니다.

ex. UI를 그리는 작업을 하는 중에 데이터를 받아오는 작업을 시작하면, 동시(Concurrency)적으로 진행합니다.

로딩화면(빙글빙글)0.1초 -> 데이터수신0.1초 -> 로딩화면(빙글빙글)0.1초 -> 데이터수신0.1초 ->..데이터수신완료

 

- 동기는 말 그대로 동시에 일어난다는 뜻입니다.

- 비동기는 말 그대로 동시에 일어나지 않는다를 의미합니다.

이 표현이 왜 오해를 살 수 있는지 아실 것 같나요?

해당 표현은 마치, 동기의 작업방식을 비동기처럼 작성했고, 비동기의 작업방식을 순차적인것으로 오해할 수 있습니다.

실제 동작과 정말 극단적으로 반대되는 결과가 나올 수 있다는 거겠죠?

 

 

동기로 프로그래밍 했을 때 장점과 단점

<장점>

단일 Task 를 빠르게 처리할 수 있다.

<단점>

1. UI 를 그리는 작업중에 데이터수신이 발생하면 UI 가 멈춘것처럼 보이는 결과가 나오는데, GUI 프로그램에서는 치명적인 단점으로 다가온다.

2. 공유자원에 대한 문제가 발생한다.

 

비동기로 프로그래밍 했을 때 장점과 단점

<장점>

여러개의 Task 를 동시에 처리할 수 있다.

GUI 프로그래밍에서 UI 가 멈춘것처럼 보이는 결과를 방지한다.

<단점>

병행이 아니라, 병렬인 경우 각 Task 처리에 대한 속도가 느려진다.

역시 공유자원에 대한 문제가 발생한다.

 

+ 동기화(synchronization)

서로 다른 Task 가 공유자원을 사용하는 경우 하나의 자원의 일관성이 깨질 수 있습니다.

그에 따라 공유자원을 사용할 때는 일관성이 깨지지 않도록 해야합니다.

이러한 작업을 동기화 작업이라고 하며, 이 과정은 결코 쉽지 않습니다.

 

오늘의 결론

자신이 만들고자 하는 프로그램이 동기or비동기 중 어느 것으로 가야하는지 잘 생각하여 결정하고,

동기화처리를 해주어야 하는 공유자원이 있는지에 대해서도 신경을 써주어야합니다.

static 변수는 많은 단점이 있지만, 그 중 공유자원으로 사용되면서 이같은 문제를 발생시킬 수 있습니다.

static 변수가 아니더라도, 공유자원으로 사용되면서 이같은 문제를 발생시킬 수 있습니다.

'BackUp (관리중지) > CS 학습' 카테고리의 다른 글

GC ( Garbage Collection )  (0) 2021.04.28
동시성 이슈  (0) 2021.04.27
쓰레드(Thread) / 프로세스(Process)  (0) 2021.04.27
HTTP (HyperText Transfer Protocol)  (0) 2021.04.26
자료구조(Data Structure)  (0) 2021.04.26

1. HTTP란,

HTTP 는 W3상에서 정보를 주고받는 프로토콜이며, HyperText Transfer Protocol 의 약자입니다. 

*W3 : www(Word Wide Web) 로 인터넷에 연결된 컴퓨터를 통해 정보를 공유할 수 있는 전 세계적인 정보 공간

 

2. HTTP 특징

1. 일반적으로 TCP (Transmission Control Protocol) 를 사용한다.

2. 클라이언트와 서버 사이에 요청(Request) 과 응답(Response) 이 이루어진다.

3. 상태가없는(stateless) 프로토콜이다. (각 요청들은 서로 연관이 없다.)

4. 요청와 응답에서 주고받는 메시지는 평문으로 이루어져 있다.

5. 기본으로 80포트를 사용한다.

 

3. HTTP 상태코드

  • 1xx (정보): 요청을 받았으며 프로세스를 계속한다
  • 2xx (성공): 요청을 성공적으로 받았으며 인식했고 수용하였다
  • 3xx (리다이렉션): 요청 완료를 위해 추가 작업 조치가 필요하다
  • 4xx (클라이언트 오류): 요청의 문법이 잘못되었거나 요청을 처리할 수 없다
  • 5xx (서버 오류): 서버가 명백히 유효한 요청에 대해 충족을 실패했다
상태코드 상세보기 ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C

 

4. HTTP 통신 과정

1. Server IP 가져오기 

2. Request & Response

5. HTTP 패킷의 구조 (Header 와 Body)

HTTP 패킷은 Header 와 Body 부분으로 나뉩니다.

Header : 보내고자하는 패킷에 대한 정보와 패킷이 도착해야 하는 곳에 대한 정보를 담아두고 있습니다.

  • General header: 요청과 응답 모두에 적용되지만 바디에서 최종적으로 전송되는 데이터와는 관련이 없는 헤더.
  • Request header: 페치될 리소스나 클라이언트 자체에 대한 자세한 정보를 포함하는 헤더.
  • Response header: 위치 또는 서버 자체에 대한 정보(이름, 버전 등)와 같이 응답에 대한 부가적인 정보를 갖는 헤더.
  • Entity header: 컨텐츠 길이나 MIME 타입과 같이 엔티티 바디에 대한 자세한 정보를 포함하는 헤더.

Body : 보내고자하는 내용이 담겨져있습니다.

 

'BackUp (관리중지) > CS 학습' 카테고리의 다른 글

GC ( Garbage Collection )  (0) 2021.04.28
동시성 이슈  (0) 2021.04.27
쓰레드(Thread) / 프로세스(Process)  (0) 2021.04.27
동기(Synchronous) / 비동기(Asynchronous)  (2) 2021.04.27
자료구조(Data Structure)  (0) 2021.04.26

자료구조 (Data Structure)

많은 사람들이 코딩테스트를 통과하기 위해 자료구조와 알고리즘을 공부하곤 합니다.

그렇게 접근하는 경우가 많다보니, 때로는 자료구조 = 알고리즘 이라는 잘못된 지식을 갖고 계신 분들고 적지않게 보았습니다.

 

그렇다면, 자료구조는 대체 무엇일까?

저는 자료구조는 데이터를 "효율적"으로 관리할 수 있는 구조 라는 것이 제일 적합하다고 판단됩니다. ( 더 좋은 정의표현이 있으면 알려주세요 )

데이터의 집합, 명령, 관계 등등이 설명에 포함되지만, 결국 "효율적"으로 관리하는 구조를 만들기 위함이겠지요.

 

 

자료구조(Data Structure) 종류

배열(Array)

배열리스트(ArrayList)

연결리스트(LinkedList)

스택(Stack)

큐(Queue)

힙(Heap)

트리(Tree)

그래프(Graph)

등등...

정말 많은 자료구조 들이 있습니다. 각 자료구조는 특징을 갖고 있고, 그 특징은 따로 각 자료구조에 따른 설명글로 대체하겠습니다.

 

선형 구조와 비선형 구조

자료구조는 크게 선형구조와 비선형구조로 나뉩니다.

어떤 기준에서 나뉘는가를 생각해보면, 간단하게

선형구조 : 배열, 스택, 큐 등

A - B - C - D - E

처럼 순서대로 구조를 이룹니다.

이처럼 앞의 데이터와 뒤의 데이터가 1:1 로 나열된 구조입니다.

 

비선형구조 : 트리, 그래프 등

A - B - C

A - C - D

처럼 A 다음에 B,C 로 갈수있고, B 는 C 로 갈수있고, C는 D로 갈수 있습니다.

이처럼 앞의 데이터와 뒤의 데이터가 1:1 or 1:n or n:1 or n:m 등으로 나열된 구조입니다.

 

어떤 자료구조(Data Structure) 선택의 기준

자료구조를 선택한 이후 효율적인 알고리즘(문제해결과정) 이 정해지는 경우가 있고, 또는 1차적으로 알고리즘 이후에 자료구조가 정해지는 경우도 있습니다.

그러나 중요한 것은 "자료구조" 는 반드시 결정되어야 한다는 것입니다.

이런 측면에서 우리는 어떤 "자료구조" 를 선택해야 할까? 를 반드시 고민하게 됩니다.

 

1. 시간복잡도

2. 공간복잡도

 

가 적을수록 효율적이다. 라고 할 수 있겠습니다만, 이는 정말 쉽지 않습니다.

공간복잡도는 높지만, 시간복잡도가 낮은경우

시간복잡도는 높지만, 공간복잡도가 낮은경우

처럼 완전 비례하지 않는 경우가 생깁니다.

 

때에 따라서는 자신의 상황에 맞게 타협가능한 수준에서 효율성을 결정하고, 그에 따라 많은 자료구조의 특징을 학습하고, 그 자료구조중 무엇을 택해야 하는지 결정해야 하는 능력이 필요하다고 판단됩니다.

 

 

'BackUp (관리중지) > CS 학습' 카테고리의 다른 글

GC ( Garbage Collection )  (0) 2021.04.28
동시성 이슈  (0) 2021.04.27
쓰레드(Thread) / 프로세스(Process)  (0) 2021.04.27
동기(Synchronous) / 비동기(Asynchronous)  (2) 2021.04.27
HTTP (HyperText Transfer Protocol)  (0) 2021.04.26

원하는 UI 예시

 

 

0. 프로젝트에 라이브러리 포함시키기

프로젝트의 Build.gadle 에 하단에 아래와 같이 추가해주세요.

maven { url 'https://jitpack.io' }

allprojects {
    repositories {
        google()
        jcenter()
        ...
        maven { url 'https://jitpack.io' }
    }
}

dependencies 를 추가해주세요.

implementation 'com.github.kr-OL:BottomDrawer:1.1.0'

(가능한 항상 최신버전을 사용해주세요. github.com/DevHyeon0312/BottomDrawerDemo에서 최신버전 확인 가능)

 

1. 플로팅 버튼을 클릭하면 하단에서 UI 등장

 

1. Activity.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"
    android:background="#d8d8d8">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">


        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:id="@+id/floatingActionButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="20dp"
            android:layout_marginBottom="20dp"
            android:clickable="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:srcCompat="@drawable/ic_baseline_add_circle_outline" />
    </androidx.constraintlayout.widget.ConstraintLayout>

    <include layout="@layout/layout_basic_bottom_sheet" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

2. Fragment.xml 생성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@drawable/bg_googledrive">

    <TextView
        android:id="@+id/textView3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:gravity="center"
        android:text="새로만들기"
        android:textSize="24sp"/>

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="20dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="horizontal">

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />
            </LinearLayout>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="20dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="horizontal">

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />
            </LinearLayout>

        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="20dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:orientation="horizontal">

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />

                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    app:srcCompat="@drawable/ic_baseline_folder_open" />
            </LinearLayout>
        </TableRow>
    </TableLayout>

</LinearLayout>

3. Fragment 코드 작성

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.bottomupdrawer.demo.databinding.FragmentGoogledriveBinding

class GoogleDriveFragment : Fragment() {
    private var _binding: FragmentGoogledriveBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentGoogledriveBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

4. Activity 코드 작성

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.bottomupdrawer.demo.databinding.ActivityGoogledriveBinding
import lib.bottomupdrawer.BasicBottomSheet

class GoogleDriveActivity : AppCompatActivity() {
    lateinit var binding : ActivityGoogledriveBinding
    lateinit var bottomSheet: BasicBottomSheet

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityGoogledriveBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.floatingActionButton.setOnClickListener {
            bottomSheet.hidden(false)
        }

        val googleDriveFragment = GoogleDriveFragment()
        bottomSheet = BasicBottomSheet
            .Begin(this@GoogleDriveActivity)
            .hide(true)
            .addContents(googleDriveFragment)
            .hidden(true)
            .commit()
    }
}

 

2. 하단 UI에 있는 버튼 클릭시 UI 가 위로 up up up 

1. Activity.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"
    android:background="#d8d8d8">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </androidx.constraintlayout.widget.ConstraintLayout>

    <include layout="@layout/layout_basic_bottom_sheet" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

2. Fragment.xml 작성

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/white">

    <TextView
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        style="?android:attr/borderlessButtonStyle"
        android:textSize="24sp"
        android:textColor="@color/black"
        android:text="Button" />


    <TextView
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#c8c8c8"
        android:layout_marginTop="5dp"
        android:layout_marginBottom="5dp"/>

    <androidx.recyclerview.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="200dp" />

</LinearLayout>

3. Fragment 코드 작성

import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.bottomupdrawer.demo.databinding.FragmentGooglemapBinding

class GoogleMapFragment : Fragment() {
    private var _binding: FragmentGooglemapBinding? = null
    private val binding get() = _binding!!


    interface OnClickListener {
        fun onClicked()
    }

    var listener: OnClickListener? = null


    override fun onAttach(context: Context) {
        super.onAttach(context)
        listener = context as? OnClickListener
        if (listener == null) {
            throw ClassCastException("$context must implement OnArticleSelectedListener")
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentGooglemapBinding.inflate(inflater, container, false)

        binding.button4.setOnClickListener {
            listener?.onClicked()
        }

        return binding.root
    }


    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

4. Activity 코드 작성

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.bottomupdrawer.demo.databinding.ActivityGooglemapBinding
import lib.bottomupdrawer.BasicBottomSheet

class GoogleMapActivity : AppCompatActivity() , GoogleMapFragment.OnClickListener {
    lateinit var binding : ActivityGooglemapBinding
    lateinit var bottomSheet: BasicBottomSheet

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityGooglemapBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val googleMapFragment = GoogleMapFragment()

        bottomSheet = BasicBottomSheet
            .Begin(this@GoogleMapActivity)
            .hide(false)
            .peekHeight(100)
            .addContents(googleMapFragment)
            .commit()
    }

    override fun onClicked() {
        bottomSheet.expend(false)
    }
}

 


github.com/DevHyeon0312/BottomDrawerDemo

 

DevHyeon0312/BottomDrawerDemo

BottomDrawer Library (바텀드로워 라이브러리). Contribute to DevHyeon0312/BottomDrawerDemo development by creating an account on GitHub.

github.com

에서 라이브러리의 사용법과 전체 Demo 코드를 확인할 수 있습니다.

 

계속 업데이트 중입니다, 궁금하신점이나 원하는 사항 등은 언제든 알려주세요~

 

GitHub ⭐️ 클릭은 5분이 아니라, 5초면 됩니다! 귀찮으시더라도, 한번씩 눌러주시면 큰 힘이 될 것 같습니다 데헷.

안녕하세요, DevHyeon 입니다.

오늘은 Fragment 이론에서 학습한 내용에 대해서 실습을 통해 직접 알아보도록 하겠습니다.

Fragmnet LifeCycle 이론 바로가기


fragment 를 삽입하는 2가지 방법을 실습해 볼것입니다.

a. Layout.xml 에서 <fragment> 요소로 삽입하는 방법

b. Code 를 사용하여 기존의 ViewGroup 에 추가하여 Layout 에 삽입하는 방법

 

생명주기는 총 2가지 테스트를 통해서 확인을 해볼겁니다.

c. Actvity 에서 생성된 Fragment 가 Activity 생명주기에 따라 어떻게 변하는가?

d. Activity 에서 생성된 Fragment 가 Activity 는 Running 상태를 유지하고 있을 때, 변화되는 상황의 Fragment 의 생명주기 알아보기


1. Fragment 삽입 실습

사전작업

1.  Fragment 에 사용되는 layout.xml 만들기 :) [ UI 가 필요없는 Fragment 의 경우 생략 가능합니다. ]

// UI 가 필요없는 Fragment 는 생략 가능합니다. 기억하시죠? Fragment 는 동작 or UI 로 사용된다는거~

 

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sample Fragment"
        android:textSize="32sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

2. Fragment를 상속받은 나만의 Fragment 클래스 만들기 :)

//저는 ViewBinding 을 사용하여 작성한 코드입니다!

class SampleFragment : Fragment() {
    private var _binding: FragmentSampleBinding? = null
    private val binding get() = _binding!!

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentSampleBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onDestroy() {
        super.onDestroy()
        _binding = null
    }
}

 

 

a. Layout.xml 에서 <fragment> 요소로 삽입하는 방법
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <fragment
        android:id="@+id/fragment3"
        android:name="com.devhyeon.fragmentlifecycle.fragments.SampleFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

<fragment> 속성에 tools:layout="@layout/fragment_sample"  를 추가하여 미리 확인해볼 수 있어요.

추가하기 전

tools:layout="@layout/fragment_sample" 추가 전

tools:layout="@layout/fragment_sample" 추가 후

 

b. Code 를 사용하여 기존의 ViewGroup 에 추가하여 Layout 에 삽입하는 방법

1. fragment 가 Activity 에서 삽입될 수 있는 ViewGroup 을 xml 에 추가해주어야 합니다. (id 는 필수~)

 * FrameLayout 뿐만 아니라, 다른 ViewGroup 인 Constraint, Linear, Table 이런 Layout 을 사용할 수도 있습니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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">

    <FrameLayout
        android:id="@+id/frameSample"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </FrameLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

2. Fragment 를 삽입하는 코드 작성

class SampleActivity : AppCompatActivity() {
    lateinit var binding : ActivitySampleBinding
    companion object {
        private val TAG = "DevHyeon : " + SampleActivity::class.java.name
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        println("$TAG : onCreate()")
        /** ViewBinding 의 코드이므로, Fragment LifeCycle Test 에 직접적인 연관이 없습니다. */
        binding = ActivitySampleBinding.inflate(layoutInflater)
        setContentView(binding.root)

		/** Fragement 삽입 코드 */
        if (savedInstanceState == null) {
            supportFragmentManager
            	.beginTransaction()
                .add(binding.frameSample.id, BodyFragment())
                .commit()
        }
    }
}

이렇게하면, Activity 가 onCreate() 되는 시점에 삽입이 가능합니다 :)

 


2. 생명주기 확인 실습

c. Activity 에서 생성된 Fragment 는 Activity 생명주기에 따라 어떻게 변하는가?

Activity 의 생성부터 파괴까지 일어나는 로그

Activity 실행

1. 액티비티 생성                        👉 Activity onCreate()

2. 프래그먼트 생성                    👉 Fragment onAttach() ~ onViewStateRestored()

3. 프래그먼트 실행                    👉 Fragment onStart()

4. 액티비티 실행                        👉 Activity onStart()

5. 액티비티 run상태                   👉 Activity onResume()
6. 프래그먼트 run상태                👉Fragment onResume()

 

Activity 정지

7. 프래그먼트 상호작용 불가        👉 Fragment onPuase()

8. 액티비티 상호작용불가            👉 Activity onPuase()

9. 프래그먼트 정지                      👉 Fragment onStop()

10. 액티비티 정지                        👉 Activity onStop()

11. 프래그먼트 상태저장               👉 Fragment onSaveInstanceState()

 

Activity 재실행

12. 액티비티 재실행                     👉 Activity onRestart()

13. 프래그먼트 실행                    👉 Fragment onStart()

14. 액티비티 실행                        👉 Activity onStart()

15. 액티비티 run상태                   👉 Activity onResume()
16. 프래그먼트 run상태               👉Fragment onResume()

 

Activity 종료

17. 프래그먼트 상호작용 불가      👉 Fragment onPuase()

18. 액티비티 상호작용불가         👉 Activity onPuase()

19. 프래그먼트 정지                   👉 Fragment onStop()

20. 액티비티 정지                     👉 Activity onStop()

21. 프래그먼트 파괴                   👉 Fragment onDestroyView() ~ onDestroy() 

22. 액티비티 파괴                     👉 Activity onDestroy()

 

Activity 생성 이후에 onPuase(사용자와의 상호작용 불가) 부터 onResume(사용자와의 상호작용 가능) 까지의 로그

Activity 실행

1. 액티비티 생성                         👉 Activity onCreate()

2. 프래그먼트 생성                     👉 Fragment onAttach() ~ onViewStateRestored()

3. 프래그먼트 실행                     👉 Fragment onStart()

4. 액티비티 실행                        👉 Activity onStart()

5. 액티비티 run상태                    👉 Activity onResume()
6. 프래그먼트 run상태                👉Fragment onResume()

 

Activity 와 사용자 상호작용 불가

7. 프래그먼트 상호작용 불가        👉 Fragment onPuase()

8. 액티비티 상호작용불가            👉 Activity onPuase()

 

Activity 와 사용자 상호작용 가능

9. 액티비티 run상태                    👉 ActivityonResume()
10. 프래그먼트 run상태                👉FragmentonResume()

 

 

* 핵심포인트 :

Running 단계로 가는 과정에서는 Fragment LifeCycle 이후에 Activity 의 다음 LifeCycle 이 동작합니다.

Destroy 단계로 가는 과정에서는 Activity LifeCycle 이후에 Fragment 의 다음 LifeCycle 이 동작합니다.

 

이해가 어렵나요? 저는 이렇게 생각했습니다.

Activity 에 Fragment 를 보여줘야 하는 과정에서 Fragment 가 먼저 준비되지 않으면 Activity 는 찰나의 순간에 아무것도 보여주지 않는다.

Activity 를 제거해야하는 과정에서 Fragment 를 먼저 제거하면, Activity 는 찰나의 순간에 아무것도 보여주지 않는다.

 

어떤가요?  사용자를 중점으로 생각해보면 조금더 와닿을수도..

 

d. Activity 에서 생성된 Fragment 가 Activity 는 Running 상태를 유지하고 있을 때, 변화되는 상황의 Fragment 의 생명주기 알아보기

Activity 가 onResume() 까지 진행 한 이후에

Fragment 를 삽입하게 되면, Fragment LifeCycle 이 onResume() 까지 동작하는 것을 알 수 있습니다.

 

 

오늘의 결론

Fragment 와 Activity 에서 엮이는 LifeCycle 이 생각보다 복잡하기 때문에, 한번쯤은 직접 확인해보는게 좋을 것 같습니다.

Fragment LifeCycle 은 직접 실행시켜보고 로그를 확인해보는것이 좀더 빠른 이해를 도울 수 있습니다.

 

아래는 테스트에 사용한 코드원본입니다.

github.com/DevHyeon0312/Fragment-LifeCycle-with-Activity

 

 

❤️ 별거 없는 내용일지라도, 공감과 Star 하나하나가 저에게는 성취감이 됩니다! ❤️

Github 에서 많은 코드들, 더 좋은 코드를 작성하기 위한 고민을 하다보면 LiveData 가 등장하게 됩니다.

 

1. LiveData 란 무엇일까요?

일단, LiveData 는 Android Jetpack 의 Component 입니다.

* Android Jetpack : Jetpack은 개발자가 관심 있는 코드에 집중할 수 있도록 권장사항 준수, 상용구 코드 제거, 모든 Android 버전과 기기에서 일관되게 작동하는 코드 작성을 돕는 라이브러리 모음입니다.

 

오늘은 문서를 따라가면서 정리를 먼저 해보겠습니다. ( 영어고수가 아닌 저는 번역본과 원문을 같이 봐야합니다.. )

ps. 저처럼 영어에 약하신 분들은, 번역본만 보시지 마시고, 꼭 원문과 같이보세요! ( 자칫 잘못하면 이해를 방해합니다. )

LiveData is an observable data holder class.

LiveData는 관측 가능한 데이터 홀더 클래스입니다.

 

Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services.

일반 관측 가능과 달리 LiveData는 수명주기 인식 기능을 갖추고 있습니다.

따라서 Activity, Fragment 또는 services와 같은 다른 앱 구성요소의 수명주기를 고려합니다.

 

This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

수명주기 인식을 통해 LiveData는 활동상태에 있는 앱 구성 요소 관찰자만 업데이트할 수 있습니다.

 

구구절절하게 써놨지만, 정리하자면...

LiveData 는 Activity,Fragment, Services 과 같은 LifeCycle 가 고려되어 동작한다.

- STARTED, RESUMED : LiveData 활동상태 , Observer 객체 업데이트 가능
- 그 외 : LiveData 비활동상태, Observer 객체 업데이트 불가능

- DESTROYED : LiveData Observer 제거 가능

 

라고 볼 수 있을 것 같습니다.


2. LiveData 를 사용했을 때 장점은 무엇일까요? 

1. UI 와 데이터 상태의 일치를 보장할 수 있습니다. ( Ensures your UI matches your data state )

LiveData 는 Observer Pattern을 따릅니다.  즉, LiveData 의 데이터가 변경될 때 마다 Observer 객체에게 알려줍니다.
Observer 객체에서는 LiveData의 데이터가 변경될 때 마다 UI 를 Update 하는 로직을 작성해둔다면
UI 와 데이터 상태의 일치를 보장할 수 있어집니다. 

 

2. 메모리 누수가 없습니다. ( No memory leaks )

메모리에 올라가있는 인스턴스를 앱이 더이상 필요하지 않음에도 GC 의 대상이 되지 못하면, 메모리누수가 발생합니다. 
LiveData 는 수명주기가 끝나면 자동으로 삭제됩니다. 즉, 메모리누수를 방지할 수 있습니다.

 

3. 중지된 액티비티로 인한 비정한 종료가 없습니다. ( No crashes due to stopped activities )

LiveData 를 관찰하고있는 Activity, Fragment, Services 등의 중지된 상태에서는 LiveData 의 변경사항에 대해 수신하지 않습니다.
따라서, LiveData 가 변경되었을 때 UI 를 수정하는 코드를 작성했다고 해도 동작하지 않으면서 비정상 종료에서 벗어날 수 있습니다.

 

4. 수동으로 수명 주기를 처리하지 않습니다.( No more manual lifecycle handling )

UI 를 구성하는 Component 들은 LiveData 를 관찰하기만 할뿐, 그 관찰을 중지하거나, 다시시작하는 행위를 하지 않습니다.
관찰하는 동안 LifeCycle 상태의 변경을 인식하기 때문에 이러한 사항들은 자동으로 관리합니다.

 

5. 항상 최신 데이터 유지합니다. ( Always up to date data )

만약 Oberver 객체가 백그라운드 상태가 되었을 때, 포그라운드 상태로 돌아오면, 그 즉시 LiveData 의 최신 데이터를 수신합니다.
따라서, 백그라운드 상태에 있을 때 데이터가 업데이트 되더라도, 포그라운드로 돌아오면 항상 최신 데이터를 유지합니다.

 

6. 구성(configuration)이 변경되었을 때, 적절하게 반응합니다. ( Proper configuration changes )

기기가 회전과 같은 구성(configuration) 의 변경으로 인한 Activity, Fragment 재생성등의 상황이 발생하면 사용가능한 LiveData 의 최신 데이터를  수신합니다.
따라서, 흔히 화면 회전시 사용해야 하는 Data 가 유실되는 경우를 쉽게 방지할 수 있어집니다.

 

7. 리소스를 공유합니다. ( Sharing resources )

LiveData 가 Observer 객체의 LifeCycle 을 인식할 수 있다는 것은, Activity, Fragment, Services 간에 객체를 공유할 수 있다는 것입니다.
LiveData 를 상속받아서 새로운 LiveData 클래스를 만들고, 싱글톤 패턴을 사용하여 구현할 수 있습니다.
<상세링크>

3. 오늘의 결론

LiveData 를 단순히 예제가 그러니까, 많은 코드들에서 쓰이고 있어서, 라는 이유로 사용하기 보다는

LiveData 의 장점들을 한번 더 기억하고, 그러한 장점을 살리는 코드를 작성해보는건 어떨까요?

'BackUp (관리중지) > Android 이론' 카테고리의 다른 글

Android Service  (0) 2021.05.14
Android Coroutine [코루틴]  (0) 2021.05.04
Fragment 와 Fragment LifeCycle 분석  (0) 2021.04.19
Activity 와 Activity LifeCycle 분석  (0) 2021.04.15
Android Context 에 대한 분석  (1) 2021.04.14

Android 학습을 위한 단톡방에서 질문이 하나 올라왔습니다.

 

질문으로 공개된 코드

이런식으로 바텀네비게이션으로 프래그먼트 전환식으로 제작을하였는데
바텀네비게이션을 누를때마다 새롭게 갱신해줄려면 어떻게해야하나요 ?
프래그먼트 null 부분조건문을 삭제해버리면
점점 어플이 느려지는 현상이나오구요 흑

 

라는 질문이 올라왔는데요,


질문을 토대로 예상해보면 제가 생각한 앱의 모습은 아래와 유사할 것이라고 판단하였습니다.

만약, 파란부분을 클릭했을 때, 초록색Fragment 가 보여진다고 했을 때,

파란부분을 클릭할 때마다 해당 Fragment 가 Update 되기를 원하는 것이라고 생각했습니다.

 

저는 이번 질문에 대해서 해결하기 위한 코드를 드리지는 않았습니다. 그와 관련된 링크를 제시하지도 않았습니다.

 

그러나, 아래와 같이 3가지 답변을 제공하였습니다.

 

1. 해당 코드에서 null 조건부를 없애면 갱신이 되는데, 에 대한 이유가 무엇인가?

일단, 만약 하단네비게이션 아이템을 클릭했을 경우에 해당 코드가 실행되는 것이라면, 데이터를 갱신하는 코드는 존재하지 않네요.

 

Fragment 는 Activity 실행중에 추가 및 삭제가 가능하다. ( devhyeon0312.tistory.com/6 에 작성된 내용) 는 특징이 존재합니다.

그렇다면 위 코드에서 null 조건을 없애지 않으면 갱신이 안되는데, 왜 null 조건을 없애면 갱신이 되는걸까요?

위 코드에서 fragment 가 null 인경우 인스턴스화를 하여 Activity 에 추가하는 코드가 존재합니다.

null 이 아니라면, 기존에 생성한 fragment 를 보여주고 있습니다.

 

null 조건부만 제거했을 경우에는, 기존에 생성한 fragment 의 인스턴스화된 여부는 고려하지 않고, 무조껀 재생성 후 추가를 하게 됩니다.

작성된 fragment들의 코드를 알 수는 없지만, fragment 를 생성할 때 데이터를 가져와서 fragment 생성을 완료하는 것이라면 당연히 데이터 갱신이 일어났다고 판단 할 수 있습니다.

 

2. null 조건부를 없앴을 때 앱이 점점 느려지는 현상에 대한 이유가 무엇인가?

context 에서 GC 가 동작하지 않는 상황에 대해서 작성했던 것을 보신분이 계실까요? ( devhyeon0312.tistory.com/3 ) 에 나와있는데요,

fragment 라고 해서 다를게 없습니다. GC 의 상황은 언제든 항상 신경써야 하는 부분이니까요.

 

해당 내부 코드가 어떻게 작성되어 있는지는 알 수 없지만, 클릭할때마다 앱이 느려진다는 말을 토대로 저는 직감했습니다.

인스턴스화된 fragment 가 제거되지 않고 있다. (메모리에서 사라지지 않고 있다.)

 

단순히 new 를 했다고 해서 fragment 가 메모리에서 사라지지 않는 것은 아닐거라고 판단됩니다. 어딘가에서 해당 프래그먼트를 참조하고 있거나, 프래그먼트가 다른 메모리를 참조하면서 해당 프래그먼트가 GC의 대상에 속하지 않는 이유가 존재할 것이라고 생각합니다.

 

 

3. 그렇다면 어떤 방법으로 접근해야하는가?

갱신을 위해서 Fragment 를 꼭 재생성해야하는가? (이미 생성되었는데?) 에 대한 질문부터 생각해보면, 당연히 No 라고 답할 것입니다.

이미 기존에 만들어준 Fragment 가 있는데, Fragment 를 다시 생성하는 것은 비효율적일 것입니다.

 

따라서 저는 Fragment 의 데이터를 갱신하는 코드를 추가로 작성해두고, 해당 코드를 실행하는 방향을 생각해 보는 쪽을 추천하였습니다.

 

해결하는 방법은 여러가지가 있을 수 있습니다. 전체적인 코드에 따라서 적절한 해결방법이 존재할 것으로 보여집니다.

단순히 "해결코드" 만을 제시하기 보다는 해당상황이 발생한 이유에 대해서 알아본다면, 자연스럽게 현재 코드에 적합한 해결방안을 찾으실 수 있을 것이라고 판단하였습니다.



제가 생각한 것이 곧 답이라고 생각하지 않습니다.

혹시 다른 의견과 지식을 갖고계신분이라면, 댓글을 통해 제시해주시면 감사하겠습니다.

+ Recent posts