하루 노트 2020

[Do it 개정7판] 여러 화면 간 전환하기 본문

프로그래밍/안드로이드

[Do it 개정7판] 여러 화면 간 전환하기

haru0118 2020. 3. 12. 17:19

책: Do it 안드로이드 앱 프로그래밍 (개정 7판)
날짜: 20-03-12
내용: Chapter02-04  

도구: 안드로이드 스튜디오
언어: 자바
환경: Windows10

 


 

04-1. 레이아웃 인플레이션 이해하기

 

[1] 레이아웃 인플레이션 이해하기

 

안드로이드는 화면 배치를 알려주는 XML 레이아웃 파일과

화면의 기능을 담당하는 소스코드 파일로 분리되어 있다.

=> 화면 배치를 담당하는 레이아웃을 별도의 파일로 분리시키면

화면만 따로 만들 수 있어 훨씬 이해하기 쉽고 관리도 편하기 때문이다.

 

MainActivity 클래스가 상속하는 AppCompatActivity에는 화면에 필요한 기능들이 들어있다.

그 중에서 setContentView( ) 메소드를 호출하면서 XML 레이아웃 파일의 이름을

파라미터로 전달하면 XML 레이아웃과 자바 소스코드가 서로 연결된다.

( R . layout . 레이아웃파일명 )

 

 

앱이 실행될 때 XML 레이아웃 파일의 내용이 메모리로 로딩되어 객체화 되고

객체화된 XML 레이아웃을 자바 소스파일에서 사용한다.

 

이렇게 XML 레이아웃에 정의된 내용이 메모리에 로딩된 후 객체화 되는 과정을

인플레이션( Inflation )이라고 한다.

 

@236p

 

XML 레이아웃은 앱이 실행되는 시점에 메모리에 객체화된다.

즉 XML 레이아웃 파일에 <Button>태그를 정의해도 앱은 자신이 실행되기 전까지 버튼이 있는지 모른다.

 

따라서 setContentView( ) 메소드를 통해 메모리에 로딩되기 전에는 XML에 접근하면 안된다.

( 접근하면 NullPointerException 에러가 발생 )

 

setContentView( )의 2가지 역할

1. 화면에 나타낼 XML 레이아웃을 지정하는 역할

2. XML 레이아웃의 내용을 메모리에 객체화 하는 역할

( 해당 레이아웃에 들어있는 뷰들이 메모리에 객체화될 수 있도록 한다.)

 

 

 

 

[2] 부분 화면 띄우기

 

화면 전체에 보여줄 XML 레이아웃이 아니라 전체 화면 중에서도

일부분만 차지하는 레이아웃을 만들어두고 그 XML 레이아웃의 내용을 로딩하여 보여줄 수 있다.

 

부분 화면을 위한 XML 레이아웃을 메모리에 객체화 하려면

별도의 인플레이션 객체를 사용해야 한다.

안드로이드는 이를 위해 LayoutInflater 라는 클래스를 제공한다.

 

LayoutInflater 클래스는 시스템 서비스로 제공된다. 

따라서 getSystemService( ) 메소드를 이용하여 LayoutInflater 객체를 참조한 후 사용해야 한다.

getSystemService( Context.LAYOUT_INFLATER_SERVICE )

 

메인 화면 : setContentView( ) 메소드

부분 화면 : LayoutInflater 클래스 // getSystemService( ) 메소드를 이용

 

 

1) activity_main.xml 과 MainActivity.java 파일

2) 추가로 sub1.xml 파일 생성 ( res / layout / sub1.xml )

 

 

버튼을 누르기 전
버튼을 누른 후

 

LayoutInflater 객체의 infalte( ) 메소드

첫 번째 파라미터로 XML 레이아웃 리소스를, 두 번째 파라미터로 부모 컨테이너를 지정한다.

View inflate( int resource, ViewGroup root )

 

LayoutInflater 객체

이 객체는 시스템 서비스로 제공되므로 getSystemService( ) 메소드를 호출하는 방법을 사용하거나

LayoutInflater 클래스의 from( ) 메소드를 호출하여 참조할 수 있다.

static LayoutInflater LayoutInflater.from ( Context context )

 

부분화면에서 findViewById를 사용할 때는 앞에 해당 XML 레이아웃 이름을 붙여야 한다.

 

인플레이션 ( Inflation )

레이아웃 객체화 과정은 앱이 실행될 때(런타임) 레이아웃 XML 파일에 정의된 내용들이 메모리에 객체화된다.

 

 

 


 

04-2. 여러 화면 만들고 화면 간 전환하기

 

안드로이드  앱의 4가지 구성요소

 

1) 액티비티 ( Activity )

2) 서비스 ( Service )

3) 브로드캐스트 수신자 ( Broadcast Receiver )

4) 내용 제공자 ( Content Provider )

 

앱을 만들어 단말에 설치했을 떄 안드로이드 시스템이 이 요소에 대한 정보를 요구한다.

이 정보들은 AndroidManifest.xml 파일에 들어있다.

* 만약 새로운 액티비티를 만들고 매니페스트에 추가하지 않으면 새 액티비티를 화면에 보여줄 수 없다.

 

 

안드로이드는 일반적으로 하나의 화면을 하나의 액티비티라고 생각할 수 있다.

startActivity( ) : 단순히액티비티를 소스코드에서 띄울 때 사용

startActivityForResult( ) : 어느 액티비티를 띄웠는지 또 액티비티간에 응답이 필요할 때 사용

 

 

 

 

 

ㅁ 실습

 

1) 메인 액티비티 : activity_main.xml / MainActivity.java

2) 메뉴 액티비티 : activity_menu.xml / MenuActivity.java

 

3) 매니페스트 등록 확인 : AndroidManifest.xml

 

android:label 속성 => 화면의 제목을 설정할 때 사용
android:theme 속성 => 테마를 설정할 때 사용
@style/Theme.AppCompat.Dialog로 설정하면 액티비티가 대화상자 형태로 나타난다.

 

 

 

 

4) 메인 액티비티 작성하기

 

REQUEST_CODE_MENU : 새 액티비티를 띄울 때 보낼 요청코드 
이 값은 나중에 새 액티비티로부터 응답을 받을 때 다시 전달받는 값이다. 
이런  방식으로 어떤 액티비티로부터 온 응답인지 구분할 수 있다. 

* 코드의 값을 마음대로 지정해도 되지만 앱에 들어갈 액티비티간에 중복되면 안된다.

 

 

 

 

인텐트 객체는 액티비티를 띄울 목적으로 사용되며
액티비티 간에 데이터를 전달하는 데에도 사용될 수 있다.

첫 번째 파라미터로는 컨텍스트( Context ) 객체가 전달되는데
액티비티 객체는 컨텍스트가 될 수 있기 때문에 일반적으로 this 변수를 사용할 수 있다.

단, 해당 소스에서는 이벤트 처리 메소드 안에서 this 변수로 MainActivity 객체를 
참조할 수 없으므로 getApplicationContext( ) 메소드를 사용해 이 앱의 Context 객체를 참조한 후 전달한다.

 

두 번째 파라미터는 새로 띄울 액티비티가 들어간다.

 

 

아래 코드를 실행하면 새로운 액티비티가 실행된다.

코드 값( requestCode ) : 각각의 액티비티를 구별하기 위해서 사용

startActivityForResult( Intent intent, int requestCode )

 



 

 

5) 메뉴 액티비티 작성하기

 

 

 

 

 

setResult( 응답코드, 인텐트 ) 
새로 띄운 액티비티에서 이전 액티비티로 인텐트를 전달하고 싶을 때 사용하는 메소드 

finish( ) : 액티비티를 화면에서 없애고 싶을 때 사용 

 

인텐트 객체
putExtra( ) : 이 메소드를 사용하면 인텐트에 데이터를 넣을 수 있다.
키(key)와 데이터 값(Value)을 쌍으로 넣어야 한다.

 

 

 

 

 

6) 메뉴 액티비티로부터 받는 메인 액티비티 작성하기

 

 

 

 

이 메소드는 새로 띄웠던 메뉴 액티비티가 응답을 보내오면 그 응답을 처리하는 역할을 한다.
새로 띄운 MenuActivity로부터 받은 응답을 처리하는 메소드

protected void onActivityResult( int requestCode, int resultCode, Intent intent ) 

 

첫 번째 파라미터는 액티비티를 띄울 때 전달했던 요청 코드와 같다.
이 값으로 어떤 액티비티로부터 응답을 받은 것인지 구분할 수 있다.

두 번째 파라미터는 새 액티비티로부터 전달된 응답코드이다.
응답 코드는 새 액티비티에서 처리한 결과가 정상인지 아닌지를 구분하는데 사용된다.
Activity.RESULT_OK 상수 ( 정상 ), 물론 임의의 코드를 만들어 정상과 실패를 만들수도 있다.

세 번째 파라미터는 새 액티비티로부터 전달 받은 인텐트이다.
인텐트 객체는주로 새 액티비티로부터 원래의 액티비티로 데이터를 전달할 때 사용한다.

 

 

인텐트 객체
getExtra( ) : 이 메소드를 사용하면 키(key) 값으로 인텐트에서 데이터를 꺼낼수 있다.

 

 

 

 

 


 

04-3. 인텐트 살펴보기

 

[1]. 인텐트

 

인텐트는 앱 구성 요소 간에 작업 수행을 위한 정보를 전달하는 역할을 한다.

인텐트는 android.content 패키지 안에 정의되어 있다.

 

인텐트는 다른 액티비티를 띄우거나 기능을 동작시키기 위한 수단으로 사용된다.

즉 무언가 작업을 수행하기 위해 사용되는 일종의 명령 또는 데이터 전달 수단이 된다.

 

인텐트를 만든 후 startActivity( ) 또는 startActivityForResult( ) 메소드를
호출하면서 전달하면 이 인텐트는 시스템으로 전달된다.
그러면 시스템은 그 인텐트 안에 들어 있는 명령을 확인하고
만든 액티비티 또는 단말에 설치되어 있는 다른 앱들의 액티비티를 띄울 수 있다.

 

인텐트에 포함되어 있는 데이터는 그 포맷이 어떤 것인가를 시스템이 확인한 후

적절한 액티비티를 자동으로 찾아 띄워준다. ( http://의 문자열 => 웹피이지로 인식 )

즉 MIME 타입을 구분한 후 설치된 앱들 중에서 적절한 것을 찾아 액티비티를 띄워주는 것이다.

 

 

ㅁ 인텐트를 전달할 수 있는 대표적인 메소드

 

startActivity( ) , startActivityForResult( ) : 액티비티를 화면에 띄울 때 사용

startService( ) , bindService( ) : 서비스를 시작할 때 사용

broadcastIntent( ) : 인텐트 객체를 브로드캐스팅 방식으로 전송할 때 사용

 

 

ㅁ 인텐트의 구성 요소

 

액션( Action ) : 수행할 기능

데이터( Data ) : 액션이 수행될 대상 데이터를 의미

ex) ACTION_DIAL tel:01010001000 : 주어진 전화번호를 이용해 전화걸기 화면을 보여줌

 

 

ㅁ 인텐트 클래스에 정의된 다양한 액션정보 ( 약 20여개 )

 

ACTION_MAIN

ACTION_VIEW

ACTION_ATTACH_DATA

ACTION_EDIT

ACTION_CALL

 

 

ㅁ 인텐트의 생성자

 

인텐트 객체는 액션과 데이터를 인수로 하여 만들수도 있지만

다른 인텐트나 클래스 객체를 인수로 하여 만들기도 한다.

Intent( )
Intent( intent o )
Intent( String action [,Uri uri] )
Intent( Context packageContext, Class<?> cls )
Intent( String action, Uri uri, Context packageContext, Class<?> cls )

 

 

[2]. 인텐트의 구분

 

1) 명시적 인텐트 ( Explicit Intent )

인텐트에 클래스 객체나 컴포넌트 이름을 지정하여

호출할 대상을 확실히 알 수 있는 경우

 

 

2) 암시적 인텐트 ( Implicit Intent )

액션과 데이터를 지정하긴 했지만 호출할 대상이 달라질 수 있는 경우

 

암시적 인텐트는 MIME 타입에 따라 시스템에서

적절한 다른 앱의 액티비티를 찾은 후 띄우는 방식을 사용한다.

 

즉 설치된 앱 정보를 알고 있는 안드로이드 시스템이 인텐트를 이용해

요청한 정보를 처리할 수 있는 적절한 컴포넌트를 찾아본 다음

사용자에게 그 대상과 처리 결과흫 보여주는 과정을 거치게 된다.

 

암시적 인텐트는 액션과 데이터로 구성되지만 그 외에도 여러 가지 속성을 가진다.

 

범주( Category ) : 액션이 실행되는 데 필요한 추가적인 정보를 제공한다.

타입( Type ) : 인텐트에 들어가는 데이터의 MIME 타입을 명시한다.

컴포넌트( Component ) : 인텐트에 사용될 컴포넌트 클래스 이름을 명시적으로 지정한다.

부가 데이터( Extra Data ) : 인텐트는 추가적인 정보를 넣을 수 있도록 번들( Bundle ) 객체를 담고 있다.

                                   이 객체를 통해 인텐트 안에 더 많은 정보를 넣어 다른 앱 구성 요소에 전달할 수 있다.

 

 

 

 

[3] 실습1. 인텐트에 액션과 데이터를 넣어 다른 앱의 액티비티를 띄우는 경우

 

 

입력상자에 전화번호를 입력하면 데이터로 저장하고

전화걸기 화면을 보여줄 인텐트 객체를 생성할 때 데이터(파라미터)로 넣어준다.

 

startActivity( intent ) 메소드로 액티비티를 띄운다.

 

 

 

 

 

 

[4] 실습2. 컴포넌트 이름을 이용해 새로운 액티비티를 띄우는 경우

 

 

1) 메인 액티비티 : activity_main.xml / MainActivity.java

2) 메뉴 액티비티 : activity_menu.xml / MenuActivity.java ( 돌아가기 기능만 구현 )

 

컴포넌트 이름은 ComponentName 객체를 만들어 인텐트에 설정한다.

첫 번째 파라미터는 패키지이름, 두 번째 파라미터는 클래스 이름으로 작성한다.

* 대상 액티비티의 이름을 지정할 때도 클래스 이름까지 함꼐 작성해야 한다.

 ComponentName(String pkg, String cls)

 

 


 

04-4. 플래그와 부가 데이터 사용하기

 

 

[1] 플래그 ( Flag )

 

startActivity( ) 또는 startActivityForResult( ) 메소드가 반복적으로 호출되면

동일한 액티비티가 메모리에 여러 개 만들어지는데

이때 플래그를 사용하면 동작 방식을 조정할 수 있다.

 

만약 동일한 액티비티를 여러 번 실행한다면 동일한 액티비티가 여러 개 스택에 들어가게 되고
동시에 데이터를 여러 번 접근하거나 리소스를 여러 번 사용하는 문제가 발생할 수 있다.
이러한 문제들을 해결할 수 있도록 도와주는 것이 바로 플래그 이다.

 

 

ㅁ 액티비티 처리 방식

 

액티비티는 액티비티 매니저( ActivityManager )라는 객체에 의해

액티비티 스택( ActivityStack )이라는 것으로 관리된다.

 

그리고 이 스택은 액티비티를 차곡차곡 쌓아두었다가 가장 상위에 있던 액티비티가 없어지면

이전의 액티비티가 다시 화면에 보이게 한다.

 

새로운 액티비티가 실행되어 화면에 띄워지면 이전에 있던 액티비티는 액티비티 스택에 저장되고

새로운 액티비티가 화면에 보이는 구조이다.

즉 새로운 화면이 보이게 되면 이전의 화면들은 그 화면의 뒤에 차곡차곡 가려진다.

 

@261p

 

 

[2] 대표적인 플래그

 

FLAG_ACTIVITY_SINGLE_TOP

FLAG_ACTIVITY_NO_HISTORY

FLAG_ACTIVITY_CLEAR_TOP

 

 

1) FLAG_ACTIVITY_SINGLE_TOP

 

액티비티를 생성할 때 이미 생성된 액티비티가 있으면

그 액티비티를 그대로 사용하라는 플래그이다.

 

이때 액티비티를 재사용하는 경우 새로운 인텐트 객체를 받기 위해

onCreate( ) 메소드가 아닌 onNewIntent( ) 메소드를 사용해야 한다.

 

원래 새 액티비티가 만들어질 때 부모 액티비티로부터 전달하는 인텐트는

새로 만들어진 인텐트의 onCreate( ) 메소드 안에서 getIntent( ) 메소드로 참조할 수 있다.

하지만 액티비티가 새로 만들어지지 않고 재사용된다면 액티비티의 onCreate( )메소드는 호출되지 않는다.

 

액티비티가 메모리에 객체로 만들어져 있다면 액티비티를 다시 띄우더라도 onCreate() 메소드가 호출되지 않는다.

따라서 onNewIntent( ) 메소드를 재정의하여 사용해야한다.

onNewIntent( ) 메소드는 액티비티가 이미 객체로 만들어져 있을 때

시스템으로부터 자동으로 호출되며 파라미터로 인텐트 객체를 전달 받을 수 있다.

 

@262p
@263p

 

 

2) FLAG_ACTIVITY_NO_HISTORY

 

처음 이후에 실행된 액티비티는 액티비티 스택에 추가되지 않는 플래그이다.

 

플래그가 설정되지 않은 경우에는 이전에 실행되었던 액티비티가 스택에 추가되므로

시스템 [ BACK ] 키를 누르면 이전의 액티비티가 보인다.

하지만 이 플래그를 사용하면 항상 맨 처음에 실행되었던 액티비티가 바로 보이게 된다.

 

알람 이벤트가 발생하여 사용자에게 한 번 알림 화면을 보여주고 싶을 때 유용하게 사용할 수 있다.

알림 화면은 한 번만 보여주면 되므로 여러 번 알람 이벤트가 발생하더라도

그 화면만 한 번 보여주는 형태로 만들 수 있다.

 

@264p

 

 

3) FLAG_ACTIVITY_CLEAR_TOP

 

액티비티 위에 있는 다른 액티비티를 모두 종료시킨다.

 

홈 화면과 같이 다른 액티비티보다 항상 우선하는 액티비티를 만들 때 유용하게 사용할 수 있다.

 

@264

 

 

 

[3] 부가 데이터 ( Extra Data )

 

ㅁ 부가데이터

 

로그인화면에서 메뉴화면으로 아이디를 전달 할때 별도의 클래스를 만들고

그 안에 클래스 변수를 만들어 두 개의 화면에서 모두 그 변수를 참조하게 만들 수 있다.

 

하지만 안드로이드는 다른 앱에서 내가 만든 화면을 띄울 수도 있기 때문에

변수를 공유하는 방식으로 데이터를 전달하는 것이 불가능할 수도 있다.

 

따라서 기본적으로 액티비티를 띄울 때 전달되는 인텐트 안에 부가 데이터를 넣어 전달하는 방법을 권장한다.

( 한 화면에서 다른 화면을 띄울 때 데이터를 전달해야 하는 경우 )

 

 

ㅁ 번들객체

 

인텐트 안에는 번들( Bundle )이라는 객체가 들어있는데

이렇게 번들 객체 안에 넣은 데이터를 부가 데이터라고 한다.

시스템에서 건드리지 않고 다른 앱 구성 요소로 전달한다.

 

기본적으로 기본 자료형을 넣었다 뺄 수 있지만

바이트 배열이나 Serializable 객체도 넣었다 뺄 수 있다.

 

번들 객체

putExtra( ) : 데이터를 넣을 때

getOOOExtra( ) : 데이터를 뺄 때 ( OOO은 자료형을 의미 )

 

번들 안에 문자열이나 정수와 같은 부가 데이터를 넣을 때는

키( Key )와 값( Value )을 쌍으로 만들어 넣는다.

 

Intent putExtra( String name, String value )
Intent putExtra( String name, int value )
Intent putExtra( String name, Boolean value )


String getStringExtra( String name )
int getIntExtra( String name, int default Value )
boolean getBooleanExtra( String name, boolean defaultValue )

 

 

ㅁ 객체 자료형

 

객체( Object ) 자료형인 경우 객체 자체를 전달 할 수 없다.

따라서 객체 데이터는 바이트 배열로 변환하여 전달하거나

Serializable 인터페이스를 구현하는 객체를 만들어 직렬화한 다음 전달해야 한다.

 

안드로이드에서는 객체를 전달할때 Serializable 인터페이스와 유사한 Parcelable 인터페이스를 권장한다.

Parcelable 인터페이스는 Serializable 인터페이스와 유사하지만 직렬화 했을 때 크기가 작아

안드로이드 내부의 데이터 전달에 자주 사용된다.

이 인터페이스를 사용하면 객체를 직접 번들에 추가하여 데이터를 전송할 때 사용할 수 있다.

 

단, 아래 2가지 메소드를 모두 구현해야한다.

 

describeContents( ) : 직렬화하려는 객체의 유형을 구분할 때 사용한다.

writeToParcel( ) : 객체가 가지고 있는 데이터를 Parcel 객체로 만들어 주는 역할을 한다.

public abstract int describeContents( )

public abstract void writeToParcel( Parcel dest, int flags )

 

Parcel 객체는 Bundle 객체처럼 readOOO( )와 writeOOO( )형태를 가진 메소드를 제공하므로

기본 데이터 타입을 넣고 확인할 수 있도록한다.

 

위의 2가지 메소드를 모두 구현한 다음에 CREATOR라는 상수를 만들어야 한다.

이 상수는 Paecel 객체로부터 데이터를 읽어 들여 객체를 생성하는 역할을 한다.

이 객체는 상수로 정의되고 반드시 static final로 선언되어야 한다.

 

 

Parcelable 인터페이스를 사용하면 객체를 정의해 데이터를 전달할 수 있으므로

코드가 좀 더 단순해지고 재사용성이 높아지는 장점이 생긴다.

하지만 데이터를 담아둘 새로운 객체를 일일이 정의하는 것이 번거롭다는 단점이 있다.

 

직렬화란?

시스템 내부에서 사용되는 객체를 외부의 시스템에서도 사용할 수 있도록

객체를 바이트 형태로 데이터 변환하는 기술과

바이트로 변환된 데이터를 다시 객체로 변환하는 기술을 말한다.

 

 

 

@265p

 

 

 

[4] 부가데이터 실습

 

Parcelable 인터페이스를 구현하는 새로운 클래스를 정의하고

그 객체를 인텐트에 넣어 전달하는 앱 만들기

 

1) SimplaData.java 만들기 ( SimpleData 클래스 작성 => Parcelable 인터페이스 구현 )

 

describeContents( ) 와 writeToParcel( ) 메소드를 구현하고 CREATOR라는 상수를 만들어야 한다.

이 상수는 Paecel 객체로부터 데이터를 읽어 들여 객체를 생성하는 역할을 한다.

이 객체는 상수로 정의되고 반드시 static final로 선언되어야 한다.

 

describeContents( ) : 직렬화하려는 객체의 유형을 구분할 때 사용한다.

writeToParcel( ) : SimpleData 객체 안에 들어있는 데이터를 Parcel 객체로 만드는 역할을 한다.

 

SimpleData 클래스의 생성자를 보면 Parcel 객체를 파라미터로 받게 되는데 이경우 

readOOO( )와 writeOOO( ) 메소드를 이용해 데이터를 읽어들인다.

 

 

 

 

 

 

2) MainActivity.java 작성하기

 

버튼을 클릭하면 activity_munu 레이아웃 화면을 띄운다.

이때 Parcelable 인터페이스를 구현한 SimpleData 클래스의 객체를 생성해

인텐트에 부가데이터로 넣어 전달한다.

 

 

 

 

 

 

3) MenuActivity.java 작성하기

 

ㅁ 버튼 관련 소스

 

버튼을 클릭하면 이전 액티비티로 돌아간다.

돌아가기 전에 인텐트 객체를 생성하고 name이라는 key 값으로 haru 라는 value를 넣고 보낸다.

 

setResult( ) : 호출된 액티비티에서 결과를 저장하는 메소드

성공 : RESULT_OK

실패 : RESULT_CANCEL

이후 종료 : finish( ) 메소드

 

 

ㅁ 텍스트 관련 소스

 

getIntent( ) : 해당 메소드를 호출하면 인텐트 객체가 반환된다. ( 전달받은 인텐트를 받을 수 있다. )

getExtras( ) : 해당 메소드를 호출하면 Bundle 자료형의 객체가 반환된다.

 

 

 

 

번들 객체를 참조한 후 getOOO( ) 형태의 메소드를 사용 또는

번들 객체를 참조하지 않고 인텐트 객체에 정의되어 있는 getOOOExtra( ) 형태의 메소드를 사용해도 된다.

 

 

 

 

 


 

04-5. 태스크 관리 이해하기

 

[1] 태스크( Task )

 

테스크는 어떤 작업을 수행하기 위한 액티비티들의 집합이다.

 

앱을 만들어 실행하면 그 앱은 하나의 프로세스 위에서 동작한다.

다시 말해, 프로세스가 하나 실행되고 그 위에 가상머신이 만들어지며,

또다시 가상머신 위에서 앱이 실행된다.

 

내가 만든 앱에서 시스템으로 인텐트를 보내는 방법으로 전화 앱의 화면을 띄울 수 있다.

이렇게 하면 전화 앱은 별도의 프로세스로 동작하게 된다.

 

하지만 프로세스는 독립적인 상자와 같아서 프로세스 간의 정보 공유는 어렵다.

그래서 태스크라는 것이 만들어져 있다.

 

 

태스크는 앱이 어떻게 동작할지 결정하는데 사용된다.

즉 태스크를 이용하면 프로세스처럼 독립적인 실행 단위와 상관없이

어떤 화면들이 같이 동작해야 하는지 흐름을 관리할 수 있다.

 

@272p

 

프로세스는 독립적인 하나의 상자와 같아서

다른 프로세스와 어떤 정보를 공유할 수 없다.

따라서 하나의 프로세스에서 다른 프로세스의 화면을 띄우려면 시스템의 도움이 필요하다.
시스템에서 이런 액티비티의 각종 정보를 저장해두기 위해 태스크를 만든다.

만약 내가 만든 앱에서 전화 앱의 화면을 띄우지 않고 
전화 앱을 따로 실행시키면 전화 앱의 태스크는 별도로 만들어지게 된다.

시스템은 알아서 태스크를 관리하지만 직접 제어해야 하는 경우도 있다.
이를 위해 매니페스트 파일에 액티비티를 등록할 때 태스크도 함께 설정할 수 있다.

 

 

 

 

[2] launchMode 태스크 속성 4가지

 

AndroidManifest.xml 파일에서 <activity> 태그에 launchMode 속성을 추가하고 

태스크를 설정할 수 있다.

 

( 아래 속성에 대한 내용은 나중에 좀 더 공부하고 정리 할 예정이다. )

 

1) standard (디폴트 값)

android:launchMode= "standard"

 

2) singleTop

android:launchMode= "singleTop"

 

3) singleTask

android:launchMode= "singleTask"

 

4) singleInstance

android:launchMode= "singInstance"

 

 

 

[3] 태스크 실습

 

버튼을 누르면 메인 액티비티를 띄우는 소스를 작성하고

테스크 속성 값을 추가하여 새 액티비티가 만들어지지 않게 설정한다.

 

태스크의 값은 AndroidManifest.xml에서 설정 가능하다.

 

singleTop으로 속성으 추가하면 태스크의 가장 위쪽에 있는 액티비티는

더 이상 새로 만들지 않게 된다. ( 플래그의 FLAG_ACTIVITY_SINGLE_TOP 설정과 같음 )

이 경우 MainActivity 쪽으로 전달되는 인텐트는 onNewIntent( ) 메소드로 전달받아야 한다.

adnroid:launchMode="singleTop"

 

1) 태스크 속성 추가 전 : 버튼을 누르면 새로운 메인 액티비티가 계속 실행되고 [ BACK ] 를 여러번 눌러야 한다.

2) 태스크 속성 추가 후 : 버튼을 눌러도 새로운 메인 액티비티가 추가되지 않으며 [ BACK ] 를 한 번 누르면 꺼진다.

 

 

MainActivity.java

 

AndroidManifest.xml

 

 

 


 

04-6. 액티비티의 수명주기와 SharedPreferences 이해하기

 

 

[1] 액티비티의 생명주기

 

안드로이드 시스템은 실행되는 앱의 상태를 직접 관리한다.

 

액티비티는 처음 실행될 때 메모리에 만들어지는 과정부터 시작해서
실행과 중지, 그리고 메모리에서 해제되는 여러 과정을 상태 정보로 갖고 있으며,
이런 상태 정보는 시스템이 관리하면서 각각의 상태에 해당하는 메소드를 자동으로 호출하게 된다.

 

ㅁ 대표적인 상태 정보

실행( Running )
화면상에 액티비티가 보이면서 실행되어 있는 상태
액티비티 스택의 최상위에 있으며 포커스를 가지고 있음

일시 정지( Pasued )
사용자에게 보이지만 다른 액티비티가 위에 있어 포커스를 받지 못하는 상태
대화상자자가 위에 있어 일부가 가려진 경우에 해당

중지( Stopped )
다른 액티비티에 의해 완전히 가려져 보이지 않는 상태

 

액티비티가 처음 만들어진 후 없어질 때까지 상태가 변화하면서
각각에 해당하는 메소드가 자동으로 호출된다.

 

이렇게 액티비티의 상태 정보가 변화하는 것을
액티비티의 '수명주기( Life Cycle )' 또는 생명주기라 한다.

 

* 시스템에서 자동으로 호출하는 메소드를 '콜백 메소드( Callback Method )라고 한다.

* 상태에 따라 호출되는 콜백 메소드를 수명주기 메소드라고 한다.

 

 

[2] 액티비티의 생명주기 다이어그램 ( 상태 메소드 )

 

1) onCreate( )

액티비티가 처음에 만들어졌을 때 호출됨

화면에 보이는 뷰들의 알반적인 상태를 설정하는 부분

이전 상태가 저장되어 있는 경우에는 번들 객체를 참조하여 이전 상태 복원 가능

이 메소드 다음에는 항상 onStart( ) 메소드가 호출됨

onCreate( )

 

2) onStart( )

액티비티가 화면에 보이기 바로 전에 호출됨

액티비티가 화면 상에 보이면 이 메소드 다음에 onResume( ) 메소드가 호출됨

액티비티가 화면에서 가려지게 되면 이 메소드 다음에 onStop( ) 메소드가 호출됨

onStart( )

 

3) onResume( )

액티비티가 사용자와 상호작용하기 바로 전에 호출됨

onResume( )

 

4) onRestart( )

액티비티가 중지된 이후에 호출되는 메소드로 다시 시작되기 바로 전에 호출됨

이 메소드 다음에는 항상 onStart( ) 메소드가 호출됨

onRestart( )

 

5) onPause( )

또 다른 액티비티를 시작하려고 할 때 호출됨

저장되지 않은 데이터를 저장소에 저장하거나 애니메이션 중인 작업을 중지하는 등의 기능을 수행하는 메소드임

이 메소드가 리턴하기 전에는 다음 액티비티가 시작될 수 없으므로 이 작업은 매우 빨리 수행된 후 리턴되어야 함

액티비티가 이 상태에 들어가면 시스템은 액티비티를 강제 종료할 수 있음

onPause( )

 

6) onStop( )

액티비티가 사용자에게 더 이상 보이지 않을 때 호출됨

액티비티가 소멸되거나 또 다른 액티비티가 화면을 가릴 때 호출됨

액티비티가 이 상태에 들어가면 시스템은 액티비티를 강제 종료할 수 있음

onStop( )

 

7) onDestroy( )

액티비티가 소멸되어 없어지기 전에 호출됨

이 메소드는 액티비티가 받는 마지막 호출이 됨

엑티비티가 앱에 의해 종료되거나(finish 메소드 호출) 시스템이 강제로 종료시키는 경우에 호출될 수 있음

위의 두 가지 경우를 구분할 때 isFinishing( ) 메소드를 이용함

액티비티가 이 상태에 들어가면 시스템은 액티비티를 강제 종료할 수 있음

onDestroy( )

 

@276p

 

새로운 액티비티가 만들어진다면

onCreate( ) - onStart( ) - onResume( ) 메소드가 차례대로 호출되며 그런 다음 화면에 보이게 된다.

 

게임 앱을 실행하던 중 전화 앱이 실행되었을 경우 다시 그 상태부터 시작할 수 있게 만들어야 한다.

이때 사용되는 액티비티의 수명주기는 onPause( ) 메소드와 onResume( ) 메소드이다.

 

이 두가지 메소드는 앱이 멈추거나 없어질 때, 그리고 앱이 다시 보이거나 새로 실행될 때 호출되므로

이 두가지 메소드를 구현하여 앱의 상태를 저장하거나 복원해야 한다.

 

 

이러한 방법 이외에도 액티비티를 중지시키기 전에 호출되는

onSaveInstanceState( ) 메소드를 이용해 데이터를 임시로 저장할 수도 있다.

 

onSaveInstanceState( ) 메소드의 파라미터로 전달되는 번들 객체를 이용해 데이터를 저장하면

onCreate( ) 메소드나 onRestoreInstanceState( ) 메소드로 저장했던 데이터가 전달된다.

 

이 방식을 이용하면 앱이 강제로 종료되거나 비정상 종료된 이후에

앱이 재실행되었을 때도 그 상태 그대로 보일 수 있도록 만들어준다.

 

 

 

 

[3] 액티비티 생명주기 실습

 

메인 액티비티에서 버튼을 클릭하면 메뉴 액티비티가 실행되고

메뉴 액티비티에서 돌아오기 버튼을 클릭하면 메인 액티비티로 돌아오는 앱

 

화면에 보일 때와 화면에 보이지 않을 때 항상 호출되는 메소드가 있다.

onResume( ) 과 onPause( ) 메소드이다. ( 이 2개의 메소드는 아주 중요하다. )

 

왜냐하면 앱이 갑자기 중지되거나 또는 다시 화면에 나타날 떄 앱 데이터의 저장과 복원이 필요하기 때문이다.

예를 들어, 게임을 할 때 사용자의 점수가 갑자기 사라지지 않도록 하려면

onPause( ) 메소드 안에서 데이터를 저장하고 onResume( ) 메소드 안에서 복원해야 한다.

 

 

1) MainActivity

 

onCreate( ), onStart( ), onStop( ). onResume( ), onPause( ), onDestroy( ) 메소드 만들기

팁 : Generate = Override Methods ( 빠른 메소드 만들기 )

 

 

 

2) MenuActivity

 

버튼에 finish( ) 메소드를 사용하여 돌아가기 기능 만들기

 

 

 

3) 결과 화면

 

activity_main.xml
activity_menu.xml

 

메인 액티비티 실행 -> 메뉴 액티비티 실행 -> 메인 액티비티로 돌아오기 -> 메인 액티비티 종료

처음 메인 액티비티 실행 시 화면

메뉴 액티비티 실행 시 화면

메인 액티비티로 돌아온 후 화면

메인 액티비티 종료

 

 

메인 & 메뉴 액티비티

 

1) 메인 액티비티 실행

2) 메뉴 액티비티 실행

3) 메인 액티비티로 돌아오기 ( finish 메소드 호출 )

4) 메인 액티비티 종료

 

 

 

 

ㅁ 참고

Logcat 탭에서 로그 확인 시 Edit Filter Configuration 항목을 선택하면 로그에 필터를 걸어 검색할 수 있다.

 

Log Tag: 에 Main이라는 글자를 입력하면 소스코드에서

첫 번째 파라미터로 넣었던 Main 글자로 검색할 수 있다.

이렇게 필터에 사용되는 글자를 태그( Tag )라고 부른다.

Log.d( "Main", data )

Log Tag

 

 

 

 

[4] SharedPreferences

 

앱 안에서 간단하게 데이터를 저장하거나 복원할 때는 SharedPreferences를 사용할 수 있다.

 

SharedPreferences는 앱 내부에 파일을 하나 만드는데

이 파일 안에서 데이터를 저장하거나 읽어올 수 있게 한다.

개발자는 실제로 파일을 만들 필요 없이 SharedPreferences의 저장, 복원 메소드를 호출하면 된다.

 

더 많은 데이터의 저장을 원할 때는 데이터 베이스를 사용한다.

 

 

 

예제

 

메인 액티비티에 텍스트 에디트( id = nameInput)를 생성 후

값을 입력 후 종료 -> 다시 실행하면 그 값이 그대로 저장되어 있는 앱

 

onPause( ) 메소드 안에 데이터를 저장하고 // saveState( )

onResume( ) 메소드 안에서 복원하는 코드 // restoreState( )

 

onSavaInstanceState( ) 메소드와 onRestoreInsanceState( ) 메소드도

액티비티의 상태와 관련하여 호출되므로 위의 코드 대신 사용할 수 있다.

 

 

앱 종료 후 다시 실행해도 입력상자에 값이 남아있다.
메인 액티비티 실행 - 메인 액티비티 종료 - 메인 액티비티 실행

 

 

설명1) 현재 입력 상자에 입력된 데이터 저장할시 saveState( )

 

 

SharedPreferences를 사용하며 pref 문자열을 저장소의 이름으로 사용한다. ( name:"pref" )

SharedPreferences 객체를 사용하려면 getSharedPreferences( ) 메소드를 참조해야 한다.

접근할 수 있는 범위를 주는 것 : Activity.MODE_PRIVATE

 

SharedPreferences.Editor 객체는 데이터를 저장할 수 있도록 edit( ) 메소드를 제공하는데

edit( ) 메소드를 호출한 후 putOOO 메소드로 저장하려는 데이터를 설정할 수 있다.

 

데이터를 저장한 후에는 commit( ) 메소드를 호출해야 실제로 저장된다.

( 단말 내에 파일로 저장되기 때문에 앱이 종료되더라도 유지가 된다. )

 

 

 

설명2)

 

restoreState( ) 메소드는 설정 정보에 저장된 데이터를 가져온다

 

 

 

 


 

[ 도전!  07. 로그인 화면과 메뉴 화면 전환하기 ]

 

대부분의 업무용 앱에서 필요한 로그인 화면과 메뉴 화면을 간단하게 만들고

두 화면 간을 전환하면서 토스트로 메시지를 띄워주도록 만들어 보세요.

 

1) 로그인 화면과 메뉴 화면 각각을 액티비티로 만든다.

2) 로그인 화면에는 하나의 버튼이 들어가도록 한다.

3) 메뉴 화면에는 세 개의 버튼이 들어가도록 하고 각각 '고객관리', '매출관리', '상품관리'라는 이름으로 표시한다.

4) 로그인 화면의 버튼을 누르면 메뉴 화면으로 이동한다.

5) 메뉴 화면의 버튼중에서 하나를 누르면 로그인 화면으로 돌아온 후 

   선택된 메뉴의 이름을 토스트 메시지로 보여준다.

 

Hint. 각 화면은 액티비티로 만들고 화면 간 전환 시에는 startActivityForResult( ) 메소드를 사용한다.

 

01. activity_main.xml & activity_menu.xml

 

activity_main.xml
activity_menu.xml

02. MainActivity.java

 

 

03. MenuActivity.java

 

04. 실행화면

 

 

05. 답안 확인 후 소스코드 변경

 

나의 경우 MenuActivity 자바 파일에서 resultCode 번호 값을 다르게 하여 

MainActivity에서 그 번호에 따라 다른 토스트 메세지를 출력하게 하였다.

 

하지만 답안에서는 MenuActivity 자바 파일에서 putExtra를 통해 값을 넣고

MainActivity에서 getStringExtra( ) 메소드를 통해 값을 읽어왔다.

 

새로운 액티비티 부분에서 인텐트에 메시지를 넣고 이전 액티비티로 돌아와

그 인텐트 값을 꺼내어 출력해주는 소스가 더 간결하고 배운 내용을 많이 사용하는 느낌을 받았다.

 

또 문제에는 없었지만 메인 액티비티 화면에서 아이디와 비밀번호를 입력한 후

로그인 버튼을 누르면 메뉴 화면에서 이를 토스트 메시지로 출력해주었다.

 

 

 

 

이번 문제를 풀며 가장 눈이 갔던 부분은 아래 내용이다.

메인 액티비티에서 putExtra( ) 메소드로 인텐트에 넣어 보내준 내용을

메뉴 액티비티에서 onActivityResult( ) 메소드 안에서가 아닌

onCreate( ) 메소드 내에서 getIntent( ) 를 통해서 받았다.

 

 

원래 intent 값을 받았던 경우

 

 

 

 

 

 

 

 

[ 도전! 08. 세 개 이상의 화면 만들어 전환하기 ]

 

앱에서 사용될 수 있는 여러 화면을 구성하고 각 화면을 전환하면서

토스트로 메시지를 띄워주도록 만들어 보세요.

 

1) 로그인 화면과 메뉴 화면 그리고 세 개의 서브 화면( 고객관리, 매출관리, 상품관리)을 각각 액티비티로 만든다.

2) 로그인 화면에는 두 개의 입력상자와 하나의 버튼이 들어가도록 한다.

3) 메뉴 화면에는 세 개의 버튼이 들어가도록 하고 각각 '고객관리', '매출관리', '상품관리' 라는 이름으로 표시한다.

4) 로그인 화면의 [로그인] 버튼을 누르면 메뉴 화면으로 이동한다. 

   만약 사용자 이름이나 비밀번호가 입력되어 있지 않은 상태에서 [로그인] 버튼을 누르면 

   토스트로 입력하라는 메시지를 보여주고 대기한다.

5) 메뉴 화면의 버튼 중에서 하나를 누르면 해당 서브 화면으로 이동한다.

   메뉴 화면에 있는 [로그인] 버튼을 누르면 로그인 화면으로 이동하고

   각 서브 화면에 있는 [메뉴] 버튼을 누르면 메뉴 화면으로 이동한다.

 

Hint. 각 화면은 액티비티로 만들고 startActivityForResult( ) 메소드로 새로 띄우거나

       finish( ) 메소드를 사용해서 원래의 화면으로 돌아올 수 있게 한다.

       그리고 어떤 화면으로부터 보내온 응답인지 모두 확인하여 토스트 메시지로 보여준다.

 

 

 

1) activity_main.xml / MainActivity.java

 

아이디와 비밀번호 입력이 안되었을 때 토스트 메시지를 띄울 방법을 생각했다.

처음에는 null 값으로 비교했는데 생각과는 다르게 해결하지 못하였다.

그래서 아래와 같이 문자열의 길이가 0이 아닐때 라는 조건으로 작성하였다.

 

 

 

 

 

2) activity_menu.xml / MenuActivity.java

 

원래 문제는 메인화면 / 메뉴화면 / 그리고 메뉴에 따른 3개의 화면을 액티비티로 만들라고 하였다.

내가 생각하기에는 단순히 출력을 위한 것이라면 그렇게 만들지 않고 

각 메뉴의 버튼의 이름을 서브화면으로 넘겨줘 그에 해당하는 서브화면을 만들면 되겠다고 생각했다.

( 갑자기 생각났지만 버튼의 text를 추출하는 방법도 나중에 시도해 봐야겠다. )

 

또 서브화면에서 '로그인으로'라는 버튼을 클릭하면 로그인화면으로 이동해야하는데

어떻게 하면 중간에 있는 메뉴 화면을 건너띄고 갈 수 있을까 고민하다가

resultcode를 메뉴 액티비티에서 onActivityResult 메소드로 받아서 '로그인 화면으로' 버튼이면

또 한 번 finish( ) 메소드를 실행해야 겠다고 생각했다.

 

사실 중간에 메뉴 액티비티가 실행되고 빠르게 로그인 액티비티로 바뀌지는 않을까? 라는 생각을 했는데

다행이 그렇게 되지는 않았다. 콜백 메소드는 시스템이 자동으로 호출하는 메소드인데

이 메소드가 맨 처음 화면 생성 시 어느 시기에 호출되는지 궁금증도 생겼다.

 

 

 

 

3) activity_sub.xml / Subactivity.xml

 

textView에 기본 텍스트 값을 설정하지 않고

어느 메뉴가 클릭되었는지에 따라 그 값을 읽어 텍스트 메뉴를 설정해 출력해 주었다.

 

 

4) 실행화면

 

앱을 제작하면서 느낀점은 

 

첫 번째로 '로그인으로 이동' 버튼을 클릭하면 메인 메뉴 화면이 뜨는지 

아니면 너무 빨리 사라져서 안보이는지 궁금증이 들었다.

 

또 데이터베이스에 대해 배우면 회원가입 및 그 정보를 바탕으로 

로그인 기능을 내가 구현할 수 있겠구나 라는 자신감이 들었다.

 

마지막으로는 xml화면 구성하는게 생각보다 익숙하지 않고 부족하다는걸 느꼈다.

더 좋은 앱은 어떻게 xml을 구성했는지 직접 한 번 보고 싶다.

 

 

5) 심심풀이

 

버튼의 텍스트를 읽어와 토스트 메시지로 출력하기