13. [액티비티 컴포넌트]

728x90

[13. 액티비티 컴포넌트]

[13-1. 인텐트 이해]

[인텐트] : intent

-인텐트: 컴포넌트 실행 정보 담긴 객체

-인텐트 객체를 시스템에 전달하면 시스템에서 해당 인텐트 정보 분석 후 그에 맞는 컴포넌트를 실행하는 구조.

일반 클래스 -개발자가 직접 생명주기 관리 O
컴포넌트 클래스 -시스템이 생명주기 관리O
- 이 클래스에 한해서는 개발자가 직접 접근 불가능
시스템에 요청하고 시스템이 실행하는 구조로 접근만 가능 O

-스템은 런타임 때 매니페스트 파일참조하여 앱을 실행

   -> 컴포넌트(액티비티/서비스/브로드캐스트 리시버/콘텐츠 프로바이더)를 '매니페스트 파일'에 등록하여 알림.

-따라서, 시스템에 사용할 컴포넌트를 매니페스트 파일통해서 먼저 알려야 함

-이제 인텐트 단위로 시스템에 요청해서 해당 컴포넌트 실행 가능한 상태.


   ➀ 매니페스트 파일에 컴포넌트등록

-‘액티비티의 경우, 액티비티 클래스 하나당 <actiity> 태그 하나로 등록. name 속성 필수

   ➁ 인텐트를 시스템에 전달 : startActivity() 함수


[인텐트 엑스트라 데이터‘]

-엑스트라 데이터 : 인텐트 안에 담는 부가 정보 데이터

-실행요청과 함께 전달할 데이터를 엑스트라 데이터로 인텐트에 담아서 전달함

   <인텐트에 ’엑스트라 데이터 추가> : putExtra() 함수

putExtra(데이터식별자, 전달데이터)

    <‘엑스트라 데이터가져오기> : get_Extra() 함수

-intent 객체로 접근하여 데이터 가져오는 함수를 ‘타입별 제공

getIntExtra()
getStringExtra()
getDoubleExtra()

[화면 전환 후 되돌아오기 | ‘사후처리’]

-화면 전환 후 다시 되돌아온 화면에서 사후처리필요 여부에 따라 방법 나뉨

    <인텐트를 시스템에 전달 방식> (3가지)

startActivity 사후처리 X
startActivityForResult (전통) 사후처리 O (11버전 이전까지 사용)
ActivityResultLauncher (최근 권장) 사후처리 O (11버전 이상부터 권장)

  ▶[전통 방식] : startActivityForResult() 함수 이용

* 사후처리: 현재의 액티비티에서 인텐트로 실행 후 결과 돌려받아야 할 때.

 결과 돌려받을 액티비티 시작

startActivityForResult(intent, 10)

 결과코드 지정 후 자동 화면 종료 finish()

intent.putExtra(“resultData”, “world”)
setResult(RESULT_OK, intent) //결과코드 지정
finish() //현재 액티비티 화면 자동 종료 시스템에 요청

       <결과코드 지정> : setResult()

   RESULT_OK, RESULT_CANCLELED 등 상수로 지정

 결과 돌려받아서 사후처리 : onAtivityResult() 자동 호출

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
. . .
}

     <함수 매개변수 설명>

      -requestCode: 인텐트 시작한 곳에서 인텐트 구분 위한 코드

      -resultCode : 인텐트 실행된 곳에서 돌려받은 결과 코드

       -data : 인텐트 객체


[최근 권장] : ActivityResultLauncher

-Contract -> ActivityResultLauncher -> launch 구조

 

[Contract ]

-ActivityResultContract 상속받은 서브 클래스

       <대표적인 Contract >

PickContact 선택한 연락처의 Uri 획득
RequestPermission 권한요청, 허락여부 파악
RequestMultiplePermissions 여러 권한 동시 요청
StartActivityForResult 인텐트 발생, 액티비티 실행 결과 획득
TakePicturePreview 사진촬영 후 비트맵 획득
TakePicture 사진촬영, 저장, 비트맵 획득

 

[ActivityResultLauncer]

-registerForActivityResult() 함수로 생성하는 객체

-이 함수 매개변수에 (Contract객체, Callback객체) 등록해줌

-Contract객체 : 실제 작업자 
-Callback 객체: 결과 처리

 

[launch() ]

-launch() 함수 호출하는 순간 ActivityResultLaunch에 등록된 Contract 객체 실행됨

     ➀ ActivityResultLauncher 생성

-registerForAcitivityResult() 함수 안에 Contract, Callback 객체 등록하여 ActivityResultLauncher 객체 받음.

val requestLauncher :ActivityResultLauncher<Intent> = registerForActivityResult (
   ActivityResultContracts.StartActivityForResult() )   //Contract 객체
   {                                                      //Callback 객체 
        val resultData = it.data?.getStringExtra(“result”)
        binding.mainResultView.text= “result:$resultData”
   }

     ActivityResultLauncher 실행

위에서 생성한 ActivityResultLauncher 객체를 launch() 로 실행시켜줌

val intent: Intent = Intent(this, DetailActivity::class.java)
requestLauncher.launch(intent)

[인텐트에 실행 대상 컴포넌트 정보 지정 방식]

     명시적인텐트

-내부 앱 컴포넌트 요청하는 인텐트 객체 만들 때 사용

-클래스 타입 레퍼런스 정보 활용한 인텐트

-매니페스트 파일에 액티비티 등록 시, android:name 속성만 선언

val inent:Intent = Intent(this, DetailActivity::class.java)

     암시적인텐트

-외부 앱 컴포넌트 요청하는 인텐트 객체 만들 때 사용

-인텐트 필터 정보를 활용한 인텐트

-매니페스트 파일에 액티비티 등록 시, name 속성 + <인텐트 필터> 설정 필요

         <인텐트 필터> : <intent-filter> 태그 사용

-<activity> / <service> / <receiver> ’컴포넌트 등록 태그 하위에 작성 가능

                다시 <인텐트 필터>의 하위에는 <action> / <category> / <data> 이용하여 정보 설정 가능

<action> 컴포넌트 기능 나타내는 문자열
<category> 컴포넌트 포함 범주 나타내는 문자열
<data> 컴포넌트에 필요한 데이터 정보

인텐트 필터 대표 사용 예시

-MainActivity 는 사용자가 런처화면에서 앱 아이콘터치 시 실행되는 메인 액티비티

-결국, ’앱 아이콘은 런처화면의 앱 액티비티로 분류됨 (, 외부 앱)

-따라서 MainActivity 또한 매니페스트 파일에서 <intent-filter> 지정하여 사용함

<activityt android:name = “.MainActivity”>
    android:exported = “true”
    <intent-filter>
       <action android:name = “android.intent.action.MAIN” /> //기능
       <category android:name=“android.intent.category.LAUNCHER”/>//범주 
     </intent-filter>
</activity>

[매니페스트에 선언된 컴포넌트 정보를 이용하는 방법]

    ➀ 인텐트의 프로퍼티 이용하는 방법

val intent = Intent()  
intent.action = “ACTION_EDIT”  //기능 정보 사용 
intent.data=Uri.parse(“http://www.google.com”) //데이터 정보 사용 
startActivity(intent)

    ➁ 인텐트의 생성자 이용하는 방법

var intent = Intent(“ACTION_EDIT”, Uri.parse(“http://www.google.com”))
startActivity(intent)

[액티비티 인텐트 동작 방식]

1) 인텐트로 실행할 액티비티 1개 : 정상 실행

2) 인텐트로 실행할 액티비티 X : 오류 발생

             -> try-catch() 구문으로 ’예외 처리 하여 실행

val intent = Intent(“ACTION_HELLO”)
try {   startActivity(intent)
} catch(e:Exception) {
   Toast.makeText(this, “no...app”, Toast.LENGTH_SHORT).show()
}

 

3) 인텐트로 실행할 액티비티 n개: 사용자 선택 or 패키지 지정 필

     ⓐ 사용자 선택

-다이얼로그 띄워서 사용자에게 어떤 앱 액티비티 실행할지 묻고 선택한 액티비티 실행함

     ⓑ 해당 앱의 패키지명 지정 : setPackage(“ ”)


[패키지 공개 상태]

-안드로이드 11 버전 이상부터 앱 패키지 공개상태 지정 안하면 외부 앱 정보 접근 불가능

     <패키지 공개 상태에 따라 영향 받는 함수들>

PackageManager.getPackageInfo()
PackageManager.queryIntentACtivities()
Intent.resolveActivity()
PackageManager.getInstalledPackages()
PackageManager.getInstalldeApplications()
bindService()

 

    <매니페스트 파일에 외부 앱 정보 접근 선언 필수>

특정 외부 앱에 접근 선언 (권장)

-<queries> 태그의 하위에 <package> 태그 이용 -> 접근할 앱 패키지명 선언

<manifest . . .
   <queries>
      <package android:name=“com.example.test_outter”/>
   </queries>
. . .
</manifest>

모든 외부 앱에 접근 선언

-<uses-permission> 태그로 선언

<uses-permission android:name = “anroid.permission.QUERY_ALL_PACKAGES”/>

[13-2. 액티비티 생명주기]

* 생명주기 : 액티비티 생성 ~ 소멸 과정

[액티비티 상태]

활성 액티비티 화면 출력 O. 사용자 이벤트 처리 O (포커스 얻음)
일시 정지 액티비티 화면 출력 O. 사용자 이벤트 처리 X (포커스 잃음)
비활성 액티비티 화면 출력 X. 종료된 건 X 단지 비활성 상태

   ⇒[활성상태] : onCreate() -> onStart() -> onResume() 까지 호출된 상태

onCreate() -> onStart() -> onResume() 까지 호출

   ⇒[일시정지 상태] : -> onPause() 까지 호출된 상태

다시 포커스 얻으면 -> onResume() 함수 자동 호출돼서 활성상태로 되돌아감

-> onPause() 까지 호출

   ⇒[비활성 상태] ->onPause() -> onStop() 까지 호출된 상태

다시 액티비티 화면 보이면 -> onRestart() -> onStart() -> onResume() 까지 호출됨

>onPause() -> onStop() 까지 호출

    ⇒[액티비티 종료] ->onDestroy() 까지 호출된 상태

->onDestroy() 까지 호출
  <액티비티 종료되는 경우>

-안드로이드 12버전부터 루트 액티비티 아닐 때 뒤로가기버튼 누를 경우

-finish() 함수를 내부적으로 호출하는 상황
-화면 회전

[액티비티 상태 저장] : Bundle 객체 사용

-액티비티 종료 후 다시 실행시 복원 필요한 데이터를 Bundle 객체로 담아둠

<’상태 저장 필요한 대표 사례 | 화면 회전>

| 액티비티 실행 상태
onCreate() -> onStart() -> onResume()

| ‘화면 회전’ 시. 액티비티 종료됐다가
-> onPause() -> onStop() -> onSaveInstanceState() -> onDestroy()

| 다시 액티비티 객체 자동 생성되어 회전’된 액티비티가 화면에 출력됨
-> onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()

[‘Bundle 객체를 매개변수로 갖는 생명주기 함수]

-번들 객체 이용하여 데이터 저장/복원할 수 있음

onCreate (savedInstanceState: Bundle)
onSaveInstanceState (outState: Bundle)
onRestoreInstanceState (savedInstanceState: Bundle)

 

[Bundle 객체에 데이터 저장] | putString() , putInt() 함수

-개발자가 번들객체에 데이터 담아두면 자동으로 데이터를 자체 캐싱 파일에 저장

-그리고 다시 액티비티 실행 될 때 캐싱 파일존재하면 해당 내용 읽어서 번들객체에 담아

onCreate(), onRestoreInstanceState()의 매개변수에 담아 전달해줌

override fun onSavedInstanceState(outState : Bundle) {
    super.onSavedInstanceState(outState)

    outState.putString(“data1”, “hello”)
    outState.putInt(“data2”, 10)
}

[Bundle 객체에 저장된 데이터 가져오기] | getString(), getInt() 함수

-액티비티 종료되었다가 다시 실행될 때, Bundle 객체를 통해 데이터 계속 유지 가능


[13-3. 액티비티 제어]

[소프트 키보드 제어]

*소프트 키보드 : 시스템 제공 키보드

-기본 동작 : 사용자가 액티비티 화면 속 글 입력 뷰에 포커싱하는 순간 자동 올라옴
: 사용자가 뒤로가기 버튼 누르는 순간 자동으로 사라짐

 ▷[입력 매니저] | InputMethodManager 클래스

-코드에서 특정 순간키보드 제어 함수 제공하는 InputMethodManager 클래스 사용

hideSoftInputFromWindow() 화면에서 키보드 사라짐
showSoftInput () 화면에 키보드 나타나게 함
toggleSoftInput () 키보드를 토글방식으로 제어함

<InputMehtodManager 소속 함수들>

*주의*
showSoftInput(View view, int flags) 의 첫 번째 매개변수는 글 입력될 뷰인데
이 뷰 포커싱 있는 상태에서만 키보드 나타날 수 O
requestFocus()로 해당 뷰에 강제 포커싱 후 -> showSoftInput() 사용할 것

[액티비티 화면 속 입력모드설정]

-액티비티 화면에서 소프트 키보드올라올 때 액티비티 화면 상태 조정

-필수 설정 X. 일반적으로 시스템에서 액티비티 상태 알아서 조정해주기 때문

-<, 개발자가 직접 설정 원할 경우>

             |매니페스트 파일에 <activity> 태그의 windowSoftInputMode 속성 이용해야 함


[액티비티 방향 설정]

<activity android:screenOrientation 속성에 값을 줌>

landscape 가로 고정
portrait 세로 고정

[액티비티 전체 화면설정]

-전체 화면 설정 : 액션바 조차 출력되지 X. 오직 콘텐츠 화면으로 꽉 채운 화면 의미

액티비티 적용 테마에 액션바 출력 안되도록 설정 | NoctionBar 테마
액티비티 코드에서 전체화면 출력 코드 설정
             - WindowInsetsController 클래스의 함수 이용
     ➀ <액션바 출려하지 않기> 
<style name="Theme.AndroidLab" parent="Theme.MaterialComponents.DayNight.NoActionBar">
     ➁ <전체 화면 출력 코드 설정> 

- API 레벨 29 까지는 window.setFlags() 함수로 사용했으나, 

- API 레벨 30 이상부터는 WindowInsetsController 클래스 함수 이용하여 액티비티 창 설정함

      -> 따라서, API 호환성 고려하여 코드 작성 필수

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
 	window.setDecorFitsSystemWindows(false) 
    val controller = window.insetsController
    if(controller != null) [
     	controller.hide(WindowInsets.Type.statusBars() or
        WindowInsets.Type.navigationBars() )
        controller.systemBarBehavior=
        	WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
    }
 }else {
 	window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
    WindowManager.LayoutParams.FLAG_FULLSCREEN)
}

[13-4. 태스크 관리]

*태스크 관리 : ‘액티비티 생성/관리 방식 제어하는 일

          -기본적으로 시스템이 제공하는 액티비티 태스크 관리 기본규칙 존재 O

          -다만, 개발자가 별도로 코드 작성하여 액티비티 태스크 제어 O

*액티비티 태스크 : 앱 실행될 때, 시스템에서 액티비티 각종 정보 저장하는 공간


[ ‘시스템에서태스크 관리 ]

* ‘태스크’ : 사용자 관점에서 프로그램의 논리적 실행 단

-시스템은 액티비티 실행 정보를 저장하기 위해 태스크를 만든다.

-사용자 관점에서의 하나의 앱 실행 단위를 태스크 정보에 묶어 저장하는데
                                                  실제로는 진짜 앱 하나가 아니라, 2개 연동된 구조일 수도 있음.

그래서 사용자 입장에서의 논리적인 실행 단위라고 하는 것.

        ➀ <진짜 1개의 앱 실행 | 태스크 1>

        <실제로는 2개 앱 연동되어 실행 | 태스크 1>

-액티비티는 인텐트 발생 시 무조건 객체 생성
-이미 같은 액티비티 실행된 상태라 같은 객체가 있더라도, 다시 인텐트 발생 시 해당 객체 생성 후 태스크 정보에 등록된다.

   
, 태스크 속 액티비티 객체들은 모두 사용자 관점에서의 논리적 실행 단위라는 것.

[ ‘개발자가 태스크 제어 ]

-개발자가 원하는 대로 액티비티 객체 생성 후 태스크에 등록, 제어하는 방식

방법 . 매니페스트 파일 속 <activity> 태그의 launchMode 에 속성값 지정
방법 . 코드에서 인텐트의 flags 속성 정보 설정 제어

       방법.  <매티페스트 파일에서 launchMode로 제어>

<activity android:name=“.TwoActivity” android:launchMode=“singleTop”>

                     <launchMode 설정 가능 속성값 종류>    : 액티비티에서 인텐트 발생 시

standard (기본값) | ‘스탠더드’ 항상 객체 생성 -> 태스크에 등록
singleTop | ‘싱글탑‘ 태스크 위쪽에 있는 액티비티의 경우 중복 등록 X
대신 기존 객체의 onNewIntent() 함수 자동 호출됨
singleTask |‘싱글 태스크’ 다시 새로운 태스크 만들어 등록
singleInstance |‘싱글 인스턴스’ 해당 설정 적용된 액티비티 하나만
새로운 태스크 만들어 등록 

       방법.  <코드에서 인텐트 flags 속성에 설정>

val intent = Intent(thism TwoActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP  //속성 설정 
startActivity(intent)

[13-5. 액티비티 ANR 문제와 코루틴]

[ANR 문제]

-ANR (Activity Not Respaonse) 문제 : 액티비티가 사용자 이벤트에 반응X 오류

-시스템에서 액티비티 실행함

-시스템의 수행 흐름에서 시간 오래 걸리는 작업 끝나지 않으면, 사용자 이벤트에 반응 X

                                 (ex, 서버 통신 네트워크 작업 등)

*UI 스레드(메인 스레드) : 시스템에서 화면 액티비티 출력하는 수행 흐름 일컫는 말


[ANR 문제 해결 방법]

      ⓐ 네트워크 통신 지원 전문 라이브러리 사용

-라이브러리 내부적으로 ANR 문제 해결. 개발자가 ANR 문제 고려X

      오래 걸리는 작업 -> 별도의 개발자 스레드만들어서 위임

-이 경우, ANR 문제는 해결되지만, 화면 처리는 메인 스레드가 주관하기 때문에

결과 출력 시 다시 메인 스레드에 의뢰하여 처리.

-개발자 스레드 자체에서는 UI 변경 처리 불가하기 때문.

      [‘코루틴’으로 ANR 오류 해결]

- 코루틴(coroutine) : 코틀린 언어가 제공하는 비동기 경량 스레드기능

- 비동기적으로 여러 작업을 처리함.

<코루틴 장점>
-경량
-메모리 누수 적다
-다양한 기능 지원
-제트팩 라이브러리에 이미 적용되어 있음

[안드로이드에서 코루틴이용]

     ① build.gradle 파일 dependencies 항목에 코루틴사용 등록

implementation ‘org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9’

     ② 코루틴 구동을 위해 스코프(Scope)’ 준비 : 스코프에서 코루틴을 구동함

                     *스코프(scope) : 성격이 같은 여러 코루틴을 묶는 개념

                     *코루틴 스코프 :CoroutineScore 구현 클래스의 객체

     ③코루틴 만들면서 디스패터 지정

                     *디스패처 : 해당 스코프에서 구동한 코루틴이 어디에서 동작해야 하는지 나타냄

                      *Channel 클래스 : 코루틴 값 전달받을 수 있는 방법 제공

                                   send(): 코루틴에 데이터 전달 | receive(), consumeEach() : 코루틴이 데이터 받음

Dispatchers.Main 메인 스레드에서 동작. (UI 변경작업도 여기서 가능)
Dispatchers.IO 파일 읽기/쓰기/네트워크 작업에서 동작
Dispatchers.Default 복잡하고 시간 잡아먹는 작업 실행

 

           <코루틴으로 작성한 소스>

val channel = Channel<Int> ()  //Chennl 은 코루틴 값 전달받을 수 있는 방법 제공 클래스.

//스코프 (backgroundScope) 만듬 -> 동시에 지정한 디스패처 (.Default) 즉. 복잡한 작업 여기에 묶음 
val backgroundScope = CoroutineScope(Dispatchers.Default + Job() )
backgroundScope.launch {
    var sum = 0L
    var time = measureTimeMillis {
        for(i in 1..2_000_000_000) {
            sum += I
        }
    }
    Log.d(“kkang”, “time : $time”)
    channel.send(sum.toInt())
}

//스코프 (mainScope) 만듬 -> 동시에 지정한 디스패처 (.Main) 즉, 메인 스레드 동작 여기에 묶음 
val mainScope = GlobalScope.launch(Dispatchers.Main) {
     channel.consumeEach {
       binding.resultView.text = “sum: $it”   //액티비티 출력 O
     }
}

[13-6. 할 일 목록 만들기] : 실습

 
[참고] : Do It 안드로이드 앱 프로그래밍 with 코틀린 

728x90