자료구조 (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 의 데이터를 갱신하는 코드를 추가로 작성해두고, 해당 코드를 실행하는 방향을 생각해 보는 쪽을 추천하였습니다.

 

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

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



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

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

1. Fragment 란?

Fragment 는 FragmentActivity 내에서 동작이나 UI(부분) 을 나타냅니다.
( FragmentActivity 는 추후 학습, Activity 의 한 종류라고 생각하고 넘어가시고 나중에 정리해두겠습니다. (Activity, AppBarActivity, AppcompatActivity, FragmentActivity 등등...)

 

2. Fragment 특징

1. 하나의 Activity 내에서 여러개의 Fragment를 결합하여 window 가 여러개인 UI 를 빌드할 수 있다.

2. Fragment 는 여러개의 Activity에서 재사용이 가능하다.

3. Fragment 는 자체적인 수명주기를 가지고, 자체적인 입력 이벤트를 수신할 수 있다.

4. Fragment 는 Activity 실행중에 추가 및 삭제가 가능하다.

5. Fragment 는 항상 Activity 내에서 사용되어야 한다. 이 때, Fragment 수명주기는 Activity 의 수명주기에 직접적인 영향을 받는다.

6. Frgment 를 Activity 에서 사용하는 Layout 에 추가하는 경우 해당 Fragment 는 해당 Activity 의 ViewGruop 에 속하게 되고,

자체적인 View Layout 을 정의한다.

7. Fragment 는 Layout.xml 에서 <fragment> 요소로 선언하거나, 기존의 ViewGroup 에 추가하는 방법을 사용하면 Code 에서 Fragment 를 Layout 에 삽입이 가능하다.

 

3. Fragment LifeCycle

https://developer.android.com/guide/fragments/lifecycle

 

 

1. Fragment LifeCycle 가져오기 ( kotlin )

this.lifecycle.currentState

onCreateView 에서 해당 코드 출력결과

2. View LifeCycle 가져오기 ( kotlin )

this.viewLifecycleOwner.lifecycle.currentState

onCreateView 에서 해당 코드 출력결과

Fragme LifeCycle 단계 정리

Fragment LifeCycle Method View LifeCylce explanation
INITIALIZED onAttach NULL 프래그먼트가 액티비티와 연결되는 순간에 호출
CREATED onCreate NULL 프래그먼트를 처음 생성할 때 호출
CREATED onCreateView INITIALIZED 프래그먼트에 UI 를 그려야하는 순간에 호출하고, 
프래그먼트에 UI 를 그린다면 view 를 반환.
만약 프래그먼트가 UI 를 그리지 않는다면, null 을 반환
CREATED onActivityCreated INITIALIZED 프래그먼트생성이 완료되었다는 것을 액티비티에 알릴 때 호출
CREATED onViewStateRestored CREATED 프래그먼트에 저장된 상태를 복원했을 때 호출
STARTED onStart STARTED 프래그먼트가 화면에 보여지는 순간에 호출
RESUMED onResume RESUMED 프래그먼트가 사용자와 상호작용을 하는 순간에 호출
STARTED onPause STARTED 프래그먼트가 사용자와 상호작용을 하지 못하는 순간에 호출
CREATED onStop CREATED 프래그먼트가 화면에서 사라지는 순간에 호출
CREATED onSavedInstanceState CREATED 프래그먼트의 상태를 저장할 때 호출
CREATED onDestroyView DESTROYED 프래그먼트의 UI 가 사라져야 하는 순간에 호출
DESTROYED onDestroy NULL 프래그먼트가 제거될 때 호출
DESTROYED onDetach NULL 프래그먼트가 액티비티와 연결이 끊어질 때 호출

* LifeCycle 이 NULL 일때 State 를 가져오려고 하면 에러가 발생한다는 당연한 사실을 기억하세요

 

Activity LifeCycle 과 비교하면, 더 복잡한 LiceCycle 을 가지고 있지만, 완전히 전혀 다른 느낌의 구조는 아닙니다.

 

결국 우리는 Activity 내에서 Fragment 를 결합하여 사용하기 때문에 Fragment 는 사용된 Activity 의 LifeCycle 의 영향에서 벗어날 수가 없습니다.

 

그렇다면, Activity 에 Fragment 를 결합할 때 발생하는 전체적인 LifeCycle 은 어떻게 되는지 알아보겠습니다.

 

https://stackoverflow.com/posts/58029686/revisions

이 그림만 봐서는 와...뭐야 이거? 싶으실 수 도 있습니다.

Android Fragment 실습에서 해당 동작에 대해 직접적으로 Log 를 찍어보면서 확인하는 글을 작성하겠습니다. :)

 

4. Fragment 특징별 설명

1. 하나의 Activity 내에서 여러개의 Fragment를 결합하여 window 가 여러개인 UI 를 빌드할 수 있다.

- 스마트폰에서의 메모장을 생각해볼까요? 첫 화면에서 제목(TitleFragment)들을 보여주고, 해당 제목을 누르면 내용(MemoFragment)
을 보여준다고 해보겠습니다.

- 태블릿에서는 어떨까요? 좌측에 제목(TitleFragment)들을 보여주고, 우측에 내용(MemoFragment) 보여줄 수도 있습니다.

즉, 태블릿에서는 하나의 Activity 에 동시에 TitleFragment 와 MemoFragment . 즉, 여러개의 Fragment 가 결합되어 사용된다는 것을 알 수 있습니다.

 

2. Fragment 는 여러개의 Activity에서 재사용이 가능하다.

- 스마트폰에서 첫화면(MainActivity) 에서 TitleFragment 만 보여주었다고, 해당 제목을 클릭했을 시에 새로운 상세화면(DetailActivity) 에서 MemoFragment 만 보여줄 수 있습니다. 이렇게 여러 Activity 에서 필요로 하는 Fragment 만을 결합하여 사용할 수 있습니다.

좌측 : 스마트폰, 우측 : 태블릿

 

3. Fragment 는 자체적인 수명주기를 가지고, 자체적인 입력 이벤트를 수신할 수 있다.

- 3. Fragment LifeCycle 설명 참조

 

4. Fragment 는 Activity 실행중에 추가 및 삭제가 가능하다.

- 실습예정

 

5. Fragment 는 항상 Activity 내에서 사용되어야 한다. 이 때, Fragment 수명주기는 Activity 의 수명주기에 직접적인 영향을 받는다.

- 3. Fragment LifeCycle 설명 참조

 

6. Frgment 를 Activity 에서 사용하는 Layout 에 추가하는 경우 해당 Fragment 는 해당 Activity 의 ViewGruop 에 속하게 되고,

자체적인 View Layout 을 정의한다.

 

7. Fragment 는 Layout.xml 에서 <fragment> 요소로 선언하거나, 기존의 ViewGroup 에 추가하는 방법을 사용하면 Code 에서 Fragment 를 Layout 에 삽입이 가능하다.

- 실습예정

 

 

 

5. 오늘의 결론

1. 실습을 통해서 알아보겠지만, 가장 중요한것은 Fragment 의 LifeCycle 을 Activity LifeCycle 과 함께 신경써야 한다는 것입니다.

Fragment LifeCycle 만 생각한채로 진행하면, 그 과정도 결과도 쉽지 않을 수 있습니다.

 

2. 저는 1Activity n Fragment 를 지향하지 않습니다. 그렇다고 해서 n Activity 0 Fragment 를 지향하지도 않습니다.

때에 따라서 적절한 n 개의 Activity 와, 재사용성이 필요하고 고려되는 측면들에서 n 개의 Fragment 를 사용하는 등으로

적절하게 섞어주는게 좋을 것 같습니다.

 

만약, 하나의 DeviceSize 만을 지원하는 앱이면 n Activity 0 Fragment 로 해도 되나요? 라는 질문을 받는다면

저는 그 Device에서 지원하는 앱이 그 내부적으로 재사용 가능한 UI 가 없습니까? 를 묻고 싶습니다.


모든 Activity 로 Fragment 를 대체할 수 있고, 1Activity n Fragment 로도 n Activity 를 대체할 수 있습니다.

A가 좋으니 A만 쓴다.

B가 좋으니 B만 쓴다.

이런 생각보다는 A의 장점과 B의 장점을 고려하여 상황에 맞게 쓴다. 로 생각하시면 좋을 것 같습니다.


ps. 정말 극단적으로 A보다 B가 모든 측면에서 좋다면, A라는 기술을 사라질 것입니다. (그것이 라이브러리라면, 더이상 업데이트를 지원하지 않거나, 없애거나 하는 등의 결과가 나오겠죠?)
ex. butterknife 는 공식적으로 업데이트하지 않겠다고 선언한 라이브러리입니다. viewBinding 을 사용하라고 안내하고 있습니다.

 

 

작성하다보니까 깨달은 것은 실습 글을 빨리 작성해야겠네요.

 

developer.android.com/reference/android/app/Fragment

 

Fragment  |  Android 개발자  |  Android Developers

 

developer.android.com

 

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

Android Service  (0) 2021.05.14
Android Coroutine [코루틴]  (0) 2021.05.04
[Android Jetpack] LiveData란,  (0) 2021.04.21
Activity 와 Activity LifeCycle 분석  (0) 2021.04.15
Android Context 에 대한 분석  (1) 2021.04.14

1. AndroidStudio New Project

배포 전 테스트를 위해서 새프로젝트를 생성합니다. 

 

 

2. New Module ( Type : Library )

모듈을 추가해 줍니다.

 

3.Edit Func

클래스나, 기본파일 등 만들고자 하는 기능들에 대한 코드를 작성하여 줍니다.

 

4. Project Add Lib ( Project Structure - Dependencies - app - Add Module - Check - Apply - OK)

배포하기전에 프로젝트에 모듈을 등록시켜줍니다. (잘 동작하는지는 테스트 해야하니까요! - Must 는 아닙니다.)

 

5. Test

원하는 테스트로 정상적인지 확인해줍니다.

 

6. GitHub Push

GitHub 에 프로젝트를 Push 해줍니다.

 

7. GitHub Releases ( Releases - Create a new release - Write - Publish release )

Releases 로 버전관리를 해주어야 합니다. (등록등록)

 

 

8. https://jitpack.io/

 

JitPack | Publish JVM and Android libraries

JitPack makes it easy to release your Java or Android library. Publish straight from GitHub or Bitbucket.

jitpack.io

Jitpack 에 접속하여
[GitHubUserName]/[ProjectName]  을 작성하고 Look up 을 눌러줍니다.

 

8.1 [GitHubUserName]/[ProjectName]  & Look up

Fail : 1.0.0 -> Red Log Image & Report

Success : 1.0.1 -> Green Log Image & Get it

 

 

8.2 How to

자, 이제 끝났으니 제대로 동작하는지 테스트 해보겠습니다.

 

 

9. New Project

새프로젝트를 만들어줍니다. (실제 사용될 프로젝트가 되겠지요)

 

 

10. build.gradle

build.gradle 을 수정해줍니다.

이 곳에 작성하는 내용은 Jitpack.io 에서 How to 를 통해 알 수 있었습니다.

 

11. USE

사용을 하면 됩니다~!

12. Lib Update

라이브러리를 업데이트할 때는 코드가 수정되어 Push Push Babe 를 해준 후에

Releases 에서 버전을 올려서 update 하는 식으로 진행하시면 version 에 맞게 사용이 가능합니다.! (버.전.관.리.필.수.)

1. Android System 은 LifeCycle(생명주기) 의 특정단계에 해당하는 특정한 콜백 메소드를 호출하여 Activity Instance 의 코드를 시작합니다.

- Activity Class 에는 Main 이 없다.

- Activity Class 에는 LifeCycle 에 해당하는 콜백 메소드가 존재한다.

- Activity Instance 에서 콜백 메소드는 Android System 이 호출한다.

 

2. Activity는 앱이 UI를 그리는 window를 제공합니다. 이 window는 일반적으로 Screen을 채우지만 (Full-Screen) Screen보다 작고 다른 window 위에 떠 있을 수 있습니다. 일반적으로 하나의 Activity 는 앱에서 하나의 Screen을 구현합니다.

- Activity 는 UI가 있는 App Component(앱 구성요소) 이다.

- Activity 는 window을 가득 채울수도 있고, 그보다 작은 상태로 window위에 있을 수 있다.

- 일반적으로는 Activity 는 앱에서 하나의 Screen을 구현한다.
* widnow 와 screen 은 다릅니다.

3. Activity LifeCycle

 

https://developer.android.com/reference/android/app/Activity

 

Activity 는 사진과 같은 LifeCycle 을 갖는다. 

 

onCreate()

Android System 에서 Activity 를 Instance화하기 위하여 호출하는 메소드

 

이 메소드에서는 setContentView() 를 이용하여 UI Layout 을 정의할 수 있습니다. 

* setContentView() 를 사용하지 않으면 어떻게 되나요? UI Layout이 없으니까 에러인가요?

 

setContentView() 를 작성하지 않으면, UI Layout 이 정의되지 않은 window 위에 빈 screen이 생성됩니다.
Activity 는 Screen보다 작은 크기로 다른 window 위에 존재할 수 있다 역시 성립합니다.

onStart()

Screen 이 보여지는 순간에 실행됩니다.

 

어라? setContentView() 가 Screen 의 UI Layout 을 정의하는 거라면, 어짜피 보여질 때 onStart() 가 호출되니까 이때 setContentView() 를 사용해도 되지 않아요?

 

setContentView() 를 onStart() 에서 호출한 경우

가능은 합니다. 그런데, 왜? setContentView() 를 onCreate() 에 쓰는걸까?

Docs 에 의하면,

onCreate()
활동이 처음 생성 될 때 호출됩니다.
 여기에서 뷰 생성, 목록에 데이터 바인딩 등 일반적인 정적 설정을 모두 수행해야합니다.이 메서드는 활동의 이전에 고정 된 상태 (있는 경우)를 포함하는 번들도 제공합니다.
라고 되어있습니다.

음? 으음? 싶으시다면, 한가지 예를 들어보겠습니다.

Bundle 에는 Activity Instance 의 상태들을 저장할 수 있습니다. ( 변수를 저장할수도 있습니다. ) 이때 Bundle 에 저장된 상태를 이용하여 layout 을 수정한다고 생각하면,

우리는 onStart() 에서 Bundle 에 접근하기 위한 대책을 추가해야합니다. (변수를 추가로 넣는 등)

savedInstanceState 가 null 이므로, USER 에 해당하는 activity_next 를 layout UI 로 정의

또한 꼭 이 때문은 아니더라도, 문서에서 제공하는 onCreate() 의 정의에 맞게 사용하는게 옳바른 코드의 작성방법 이라고 할 수 있겠죠?

 

onResume()

이 메소드는 사용자와 상호작용을 하는 순간에 실행됩니다.

즉, 사용자의 터치이벤트, 키보드로 입력넣기, 핸드폰을 흔들 때 반응해야하기 등등은

이 메소드가 호출되어야 가능합니다.

 

Activity Running

만약 우리가 C,Java 등의 언어에서 main() 을 실행하고 종료되기 전까지는 Running 단계겠죠?

마찬가지로 onResume() 이 실행된 이후부터는 Running 단계입니다.

 

onPause()

이 메소드는 사용자와 상호작용을 하지 못하는 순간에 호출됩니다.

그렇다고, 화면이 사라지는 상태를 말하는 것은 아닙니다.

또한, 사용자와 상호작용을 하지 못할뿐, 화면에 사라진 상태는 아니기 때문에 UI 를 업데이트 할 수 있습니다.

MainActivity 의 상호작용은 멈추었지만, UI 업데이트중

* 위와같은 상황에서 다시 MainActivity 로 돌아간다면, MainActivity 가 사용자와 상호작용이 가능해지니까 onResume() 이 실행됩니다.

 

onStop()

이 메소드는 더이상 Activity 의 Screen 이, 사용자에게 보여지지 않는 순간에 실행됩니다.

하지만, Instance 가 없어진 상태는 아닙니다. 

* 특정한 조건없이 1초마다 count를 증가하는 Thread 가 실행중이였다면, onStop() 이후에도 Thread는 멈추지 않고 count를 계속 증가시깁니다. 때에 따라서는 필요할수도, 굉장히 위험할수도 있습니다.

(불필요한 코드가 계속 돌고 있다는 것은 성능저하까지 이어질 수 있...)

onStop() 이후에 굳이 필요없는 코드는 잠시 멈춰두는 것을 추천합니다. 밑에 나오는 onRestart() 에서 다시 실행시키면 되죠!

 

 

onRestart()

onStop() 메소드가 실행된 이후에, Activity 의 Screen 이, 다시 사용자에게 보여지는 순간에 호출됩니다.

즉, onStop() 이후에 screen이 보여진다면 onStop() -> onRestart() -> onStart() 순으로 진행되고,

onCreate() 이후에 screen이 보여진다면 onCreate() -> onStart() 순으로 진행됩니다.

* onPause() 에서 onResume() 이 호출되고, onRestart()도 결국 onRestart() -> onStart() -> onResume() 이 호출되면서

onResume() 에 onStop() 이후에 얻어지는 데이터를 넣는 경우가 있습니다.

저는 onResume() 에서는 onResume() 상황에 맞는 코드.

onRestart() 에서는 onRestart() 상황에 맞는 코드 만을 작성하는 것을 추천합니다.

 

onDestroy()

이 메소드는 Activity 가 완전히 사라지는 순간에 호출됩니다. 문서에 의하면, 현재 Activity Instance 에서 finish 를 사용하는 경우에 호출되거나, Android System 에서 메모리절약을 위해 일시적으로 파괴하면서 호출이 된다고 하는데, 이는 isFinishing() 으로 구분이 가능하다고 작성되어 있습니다.

 

만약 finish 를 사용하거나, Back 키를 사용하는 등으로 Activity 가 사용자에 의해 파괴되는 경우에는 isFinishing() 은 true 를 반환합니다.

Android System 에서 메모리절약을 위해 파괴하는 거라면 false 를 호출합니다.

* onDestroy() 에서 isFinishing() 가 true 일 때 데이터를 저장하거나 하는 행위는 위험합니다.

( 쉽게 onDestroy() 에서 데이터를 저장하지 마세요. )

*Android System 은 Process 만을 kill 합니다. 

*만약, Process 가 제거되는 순간을 경험하고 싶다면, 개발자옵션 -> 프로세스수제한 -> 개수선택

이후 다른 앱들을 실행시켜보세요. 개수가 가득차면 Process 가 제거되면서  false 를 리턴합니다.

 

 

 

4. 정리

1. Activity 는 Android System 가 콜백메소드(onCreate)를 호출하여 생성하며 콜백메소드(Create~Destroy)는 LifeCycle 에 맞게 호출된다.

 

2. Activity 는 UI Layout을 정의하여 screen에 보여줄 수 있고, 만약 UI Layout 을 정의하지 않는다면 빈 screen이 보여진다. 또한 UI 가 없기 때문에 사용자에 맞춤 기능이 아니라, LifeCycle 에 해당하는 작성된 코드들만이 실행된다.

 

3. 일반적으로 1개의 Activity 는 1개의 Screen 을 구현한다.

 

4. Activity 의 window 와 screen 은 다르다.

 

5. LifeCycle 은 onCreate() , onStart(), onRestart(), onResume(), onPause(), onStop(), onDestroy()로 이루어져있다.

onCreate() : Android System 에서 Actvitiy 를 Instance화 하는 순간

onStart() : Screen 이 보여지는 순간

onRestart() : onStop() 이후에 Screen 이 보여지려고 하기(onStart()) 직전

onResume() : 사용자와 상호작용이 일어나기 시작하는 순간

onPause() : 사용자와 상호작용이 끊어지는 순간

onStop() : Screen 이 보여지지 않는 순간 : 단 1pixcel 이라도 Screen 이 보여진다면, onStop() 에 진입하지 않는다.

onDestroy() : Code(finish) or 사용자에 의해(back or finish) or AndroidSystem 이 Process 를 kill 하는 순간 - isFinishing()으로 구분

 

5. 오늘의 결론

생명주기에 맞게 코드를 작성하고, Android 문서는 영어로 보거나, 영어와 한글을 같이 보자.
( Window 랑 Screen 을 둘다 "화면" 이라고 번역시켜놔서 뇌에 혼란이 엄청왔다. )

PS.

일반적으로 1Activity 는 1개의 Screen 을 구현한다. 라고 되어있는 부분에서

1Activity 에서 어떤 경우에 2개 이상의 Screen 을 구현하는지는 좀더 찾아봐야 할 것 같습니다.


여러 의견 모음 (2021.04.15 기준)

1. Fragment 를 이용하여 화면에 보여주는 View 가 다른데, 이런 경우에 2개 이상의 Screen 을 구현하는 것 같다.
- 저는 Activity 가 Window 에 Screen 을 보여주고, 이때 Screen 은 setContentView() 를 사용하여 정의한다고 되어 있는 부분에서 이 부분이 의아했습니다. Fragment 에 대해서는 추가 글에서 정리하겠지만, Fragment 는 Activity 이후에 나온 기술로, Activity 에 대해 설명하는 기준에 있어서 Fragment 를 사용하는 경우를 2개 이상의 Screen 을 구현했다고 생각하지 않습니다.

뭔가 근본적으로 Screen 에 대한 정의가 확실히 잡혀있지 않은 상태여서 그 부분을 찾아보고 있지만, Fragment 가 나오기 전부터 Screen 을 2개 이상 구현할 수 있었고, 그 로직의 단점을 보완하고자 Fragment 가 등장한것이 아닐까 라는 생각을 합니다.

 

이 부분에 대해 정확하게 설명이 가능한 블로그 or 지식을 보유중이신 분이 있으시다면 공유부탁드리겠습니다..

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

Android Service  (0) 2021.05.14
Android Coroutine [코루틴]  (0) 2021.05.04
[Android Jetpack] LiveData란,  (0) 2021.04.21
Fragment 와 Fragment LifeCycle 분석  (0) 2021.04.19
Android Context 에 대한 분석  (1) 2021.04.14

+ Recent posts