일반적인 컴퓨터에서는 애플리케이션 단위로 실행되지만 안드로이드에서는 액티비티 단위로 실행된다.
용어 정리
1. 애플리케이션(application)
- 안드로이드는 애플리케이션은 여러개의 액티비티들로 구성된다.
- 액티비티들은 애플리케이션 안에서 느슨하게 묶여 있다.
- 하나의 애플리케이션은 .apk를 확장자로 가지는 하나의 파일 안에 저장된다.
2. 액티비티(activity)
- 사용자가 어떤 작업을 할 수 있는 화면을 가지고 있는 애플리케이션 구성 요소이다.
- 각 액티비티는 사용자 인터페이스가 그려지는 윈도우를 가지고 있다.
- 안드로이드에서는 실행의 단위가 애플리케이션이 아니고 액티비티이다.
3. 태스크(task)
- 관련된 액티비티의 그룹으로 정의할 수 있고 이들 액티비티들은 액티비티 스택에 나열되어있다.
- 즉 하나의 태스크는 스택에 있는 액티비티들로 구성된다.
4. 액티비티 스택(activity stack) = 백스택(back stack)
- 사용자가 BACK 키를 누르면 안드로이드는 현재 액티비티를 제거하고 이전 액티비티로되돌아간다. 따라서 사용자가 방문한 액티비티들은 어딘가에 기억되어 있어야 한다. 이런 용도로 사용되는 것이 액티비티 스택이다.
- 안드로이드는 새로운 액티비티가 시작될 때마다 액티비티를 스택의 맨 위에 삽입된다.(PUSH)
- 만약 사용자가 BACK 키를 누르면 현재 액티비티는 스택에 제거되고(POP) 스택에 저장된 이전 액티비티로 되돌아간다.
- 스택의 맨 위에 있는 액티비티는 현재 실행되고 있는 액티비티이다.
5. 인텐트(intent)
- 하나의 액티비티에서 다른 액티비티를 시작하려면 액티비티의 실행에 필요한 여러 가지 정보들을 보내주어야 한다. 이때 사용하는 메시지가 인텐트이다.
- 액티비티와 같은 컴포넌트들은 인텐트라고 불리는 메시지를 통해서 활성화된다.
- 인텐트 메세징은 컴포넌트들을 실행 시간에 바인딩하는 기법이다.
인텐트의 종류
5-1. 명시적 인텐트(explicit intent)
- 타킷 컴포넌트의 이름을 지정한다.
예제1 )
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.jiyeon.myapplication.MainActivity">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="여기는 액티비티1입니다."/> <Button android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="이미지 표시 액티비티 열기"/> </LinearLayout>
|
layout2.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">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="여기는 액티비티2입니다."/> <Button android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="닫기"/> </LinearLayout>
|
MainActivity.java
package com.jiyeon.myapplication;
import android.app.Activity; ....
public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
Button b = (Button)findViewById(R.id.button1); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //액티비티를 실행할려면 인텐트 객체를 생성해야한다. //두번째 이름을 알고 있으므로 두 번째 액티비티의 클래스 이름을 인수로 주어서 인텐트 객체를 생성한다. //즉 명시적인 인텐트를 사용하는 것이다. Intent intent = new Intent(MainActivity.this,Activity2.class); startActivity(intent); } });
} }
|
Activity2.java
package com.jiyeon.myapplication; import android.app.Activity; ...
/** * Created by jiyeon on 2016-04-03. */ public class Activity2 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.layout2);
Button b = (Button)findViewById(R.id.button2); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //이벤트 리스너에서는 버튼이 클릭되면 finish() 메소드를 호출하여서 현재의 액티비티를 종료한다. finish(); } });
}
}
|
AndroidMainfest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.jiyeon.myapplication">
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> //android:name에는 액티비티의 클래스 이름을 적어준다. //같은 패키지에 있는 경우에는 앞에 .을 찍어나 아니면 단순히 클래스 이름만 적어준다. //다른 패키지라면 패키지 이름을 포함한 완전한 경로 이름을 적어야 한다. <activity android:name="Activity2" android:label="Activity"></activity> </application>
</manifest>
|
예제2)
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.administrator.myapplication.MainActivity">
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="서브 액티비티로부터 문자열 반환받기" android:id="@+id/button" />
<TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" />
<TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="________________________________"/>
</LinearLayout>
|
sub.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">
<EditText android:id="@+id/edit" android:layout_width="match_parent" android:layout_height="wrap_content"> <requestFocus></requestFocus> </EditText> <LinearLayout android:id="@+id/linearLayout1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center">
<Button android:id="@+id/button_ok" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="입력완료"/> <Button android:id="@+id/button_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="취소"/> </LinearLayout> </LinearLayout>
|
AndroidManifest.xml
<activity android:name=".SubActivity" android:label="SubActivity" ></activity> //추가
|
MainActivity.java
package com.example.administrator.myapplication;
import android.app.Activity; ...
public class MainActivity extends Activity { static final int GET_STRING = 1; TextView text;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button); text = (TextView) findViewById(R.id.text); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent in = new Intent(MainActivity.this, SubActivity.class); startActivityForResult(in, GET_STRING); //서브 액티비티 실행 } });
} //액티비티로부터 결과를 받는다. protected void onActivityResult(int requestCode, int resultCode, Intent data){ if(requestCode == GET_STRING){ if(resultCode == RESULT_OK){ text.setText(data.getStringExtra("INPUT_TEXT")); } } }
}
|
SubActivity.java
package com.example.administrator.myapplication; import android.app.Activity; ...
/** * Created by Administrator on 2016-04-04. */ public class SubActivity extends Activity { EditText edit;
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.sub);
edit = (EditText) findViewById(R.id.edit); Button button_ok = (Button) findViewById(R.id.button_ok); button_ok.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //메인 액티비티로 결과를 보낸다. Intent intent = new Intent(); intent.putExtra("INPUT_TEXT",edit.getText().toString()); setResult(RESULT_OK, intent); finish(); } });
Button button_cancel = (Button) findViewById(R.id.button_cancel); button_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { setResult(RESULT_CANCELED); finish(); } });
} }
|
5-2. 암시적 인텐트(implicit intent)
- 타킷 컴포넌트의 이름을 지정하지 않는다. 대신에 아주 암시적으로 컴포넌트를 지정하는 것이다.
- 특정한 타깃이 없으므로 안드로이드는 인텐트를 처리할 수 있는 가장 최적의 컴포넌트를 탐색하여야 한다.
- 안드로이드는 컴포넌트가 가지고 있는 인텐트 필터를 암시적인 인텐트와 비교하여 탐색을 수행한다.
전체적인 구조
Intent intent = new Intent(Intent.ACTION_SEND); //이메일 전송을 의미하는 인텐트 생성 intent.putExtra(Intent.EXTRA_EMAIL,recipientArray); //이메일의 송신자를 엑스트라 필드에 기술한다. startActivity(intent);
|
- 인텐트 객체
● 컴포넌트 이름(component name)
- 인텐트를 처리하는 타깃 컴포넌트의 이름이다.
- 타킷 컴포넌트의 완전한 이름과 패키지 이름을 적어주면 된다.
- 만약 이름이 없으면 암시적 인텐트가 되어서 안드로이드가 최적의 타깃 컴포넌트를 찾아준다.
- setComponent(), setClass(), setClassName()으로 설정할 수 있고 getComponent()로 읽을 수 있다.
● 액션(action)
- 액션은 수행되어야 하는 작업을 나타낸다.
- 많은 액션들이 Intent 객체 안에 String 타입의 상수로 정의되어 있다.
자주 사용하는 액션
상수 |
타깃 컴포넌트 |
액션 |
ACTION_VIEW |
액티비티 |
데이터를 사용자에게 표시한다. |
ACTION_EDIT |
액티비티 |
사용자가 편집할 수 있는 데이터를 표시한다. |
ACTION_MAIN |
액티비티 |
테스크의 초기 액티비티로 설정한다. |
ACTION_CALL |
액티비티 |
전화 통화를 시작한다. |
ACTION_SYNC |
액티비티 |
모바일 장치의 데이터를 서버 상의 데이터와 일치시킨다. |
ACTION_DIAL |
액티비티 |
전화번호를 누르는 화면을 표시한다. |
● 데이터(data)
- 작업에 필요한 데이터를 나타낸다.
- 예를들면 액션이 ACTION_VIEW이면 무엇을 사용자에게 표시할 것인지를 주어야 한다.
- 데이터는 URI 형식을 사용한다.
- setData()와 getData() 메소드를 사용하여서 인텐트 객체에 데이터를 설정하고 접근할 수 있다.
Intent intent = new Intent(Intent.ACTION_CALL); //액션이 ACTION_CALL인 인텐트를 생성한다.
intent.setData(Uri.parse("tel:01012341234")); //01012341234번 전화번호를 데이터로 설정한다. startActivity(intent); //인텐트를 시작한다.
|
● 카테고리(Categoty)
- 액션에 대하여 추가적인 정보를 제공한다.
- 예를들어 CATEGORY_LAUNCHER는 액티비티가 최상위 애플리케이션으로 론처에 나타내야한다는 것을 의미한다.
- addCategoty() 메소드는 카테고리를 인텐트 객체 안에 위치시킨다
- removeCategory()는 이전에 추가된 카테고리를 삭제한다.
- getCategory()는 현재 인텐트 객체 안에 있는 모든 카테고리를 반환한다.
카테고리 상수
상수 | 설명 |
CATEGORY_BROWSABLE | 타깃 액티비티가 브라우저에 의하여 시작되어서 이미지와 같은 데이터를 표시할 수 있다. |
CATEGORY_GADGET | 액티비티가 다른 액티비티 안에 개짓으로 내장된다. |
CATEGORY_HOME | 홈 화면을 표시하는 액티비티이다. |
CATEGORY_LAUNCHER | 액티비티가 최상위 애플리케이션으로 론처에 나열된다. |
CATEGORY_PREFERENCE | 타깃 액티비티가 환경 설정 패널이다. |
● 엑스트라(extra)
- 타깃 컴포넌트로 전달되어야 하는 추가적인 정보를 가지고 있다.
- "키-값(key-value)" 쌍으로 지정된다. 예를 들어 ACTION_TIMEZONE_CHANGED 인텐트는 새로운 시간대를 나타내는 "time-zone" 엑스트라를 가지 고 있다.
- put..() 메소드를 이용하여서 다양한 유형의 엑스트라 데이터를 추가한다.
- get..() 메소드를 이용하여서 엑스트라 데이터를 읽을 수 있다.
인텐트 필터
- 컴포넌트는 자신들이 처리할 수 있는 인텐트의 종류를 안드로이드 시스템에 알리기 위하여 하나 이상의 인텐트 필터를 가진다.
- 컴포넌트의 기능을 기술하고 컴포넌트가 수신할 수 있는 인텐트의 집합을 기술한다.
- 명시적 인텐트는 무엇을 포함하고 있든지 상관없이 항상 타깃 컴포넌트로 전달되지만 암시적 인텐트는 컴포넌트의 인텐트 필터를 통과해야만이 컴포넌 트로 전달된다.
1) 액션 비교
- 인텐트의 액션은 필터에 나열된 액션 중의 하나와 반드시 일치하여야 한다.
- 만약 필터가 어떤 액션도 나열하지 않았다면 어떤 인ㅌ넨트도 필터를 통과할 수 없다.
- 만약 인텐트 객체가 어떤 액션도 지정하지 않았다면 자동적으로 필터를 통과한다.
<intent-filter ...> <action android:name="com.example.project.SHOW_CURRENT" /> <action android:name="com.example.project.SHOW_RECENT" /> <action android:name="com.example.project.SHOW_PENDING" /> ... </intent-filter> |
2) 카테고리 비교
- 인텐트 객체 안의 모든 카테고리가 필터의 카테고리와 일치되어야 한다.
- 카테고리를 가지지 않은 인텐트 객체는 항상 카테고리 테스트를 통과한다.
- 한가지 예외가 있는데 안드로이드는 startActivity()로 전달되는 모든 암시적 인텐트는 "android:intent.category.DEFAULT" 카테고리에 속한다고 가정한다. 따라서 암시적 인텐트를 받고자 하는 액티비티들은 "android.intent.category.DEFAULT"를 인텐트 필터에 나열하여야 한다. "andoir.intent.action.MAIN"과 "android.intent.category.LAUNCHER"를 가지고 있는 액티비티는 예외인데 이들 액티비티들은 "android.intent.category.DEFAULT"를 나열하지 않아도 암시적인 인텐트를 받을 수 있다.
<intent-filter ...> <category android:name="android.intent.category.DEFULT" /> <category android:name="android.intent.category.BROWSABLE" /> ... </intent-filter> |
3) 데이터 비교
- 데이터 타입이나 URI를 지정하지 않은 인텐트는 필터가 아무런 URI나 데이터 타입을 지정하지 않은 경우에만 테스트를 통과한다.
- 데이터 타입이나 URI 중에서 하나만 지정한 인텐트는 필터도 똑같이 하나만 지정한 경우에 테스트를 통과한다.
- 데이터 타입과 URI을 모두 지정한 인텐트는 데이터 타입과 URI가 모두 필터와 일치하여야한다.
- 하나의 예외로 필터가 데이터 타입만을 지정하였다면 "file:"이나 "content:"가 붙은 URI를 가지는 인텐트는 테스트를 통과된다.
<intent-filter> <data android:mimeType="video/*" //mimeType속성은 데이터의 MIME타입을 지정한다 '*'와 같은 와일드 카드 문자를 사용할 수있다. android:scheme="http" /> ... </intent-filter> |