Activity 직접 구성하기
Android Studio에서 새 프로젝트를 생성할 때 No Activity를 선택합니다. res 디렉터리 하위에 layout 폴더를 추가하고, Empty Activity 템플으로 새 Kotlin/Java 파일을 만듭니다. Generate layout file 옵션은 해제한 채로 진행하여 수동으로 레이아웃을 연결합니다.
구성 순서
- XML 레이아웃 리소스 작성
- Activity 클래스 생성 및
onCreate()오버라이드 - AndroidManifest.xml에 Activity 등록
<activity android:name=".EntryActivity"
android:label="앱 진입점">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
public class EntryActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.entry_layout);
}
}
Toast 메시지 출력
짧은 알림 메시지를 화면에 표시하고 일정 시간 후 자동으로 사라지는 UI 요소입니다.
public class EntryActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.entry_layout);
Button submitBtn = findViewById(R.id.submit_button);
submitBtn.setOnClickListener(v -> {
Toast.makeText(EntryActivity.this,
"처리가 완료되었습니다",
Toast.LENGTH_SHORT).show();
});
}
}
makeText()의 세 인자는 각각 Context 인스턴스, 표시할 텍스트, 지속 시간을 의미합니다.
옵션 메뉴 구현
res 하위에 menu 리소스 디렉터리를 생성하고 options.xml 파일을 추가합니다.
<?xml version="1.0" encoding="utf-8"? >
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_save"
android:title="저장"/>
<item android:id="@+id/menu_delete"
android:title="삭제"/>
</menu>
Activity에서 메뉴를 inflate하고 클릭 이벤트를 처리합니다.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
int selectedId = item.getItemId();
if (selectedId == R.id.menu_save) {
Toast.makeText(this, "저장 선택", Toast.LENGTH_SHORT).show();
return true;
} else if (selectedId == R.id.menu_delete) {
Toast.makeText(this, "삭제 선택", Toast.LENGTH_SHORT).show();
return true;
}
return super.onOptionsItemSelected(item);
}
Activity 종료
물리 Back 키 외에 코드로 Activity를 종료하려면 finish()를 호출합니다.
Button exitBtn = findViewById(R.id.exit_button);
exitBtn.setOnClickListener(v -> {
finish();
Log.d("EntryActivity", "Activity 종료 호출");
});
Intent를 통한 화면 전환
명시적 Intent
출발지와 목적지 Activity를 명확히 지정하여 화면을 전환합니다.
Intent explicitIntent = new Intent(EntryActivity.this, DetailActivity.class);
startActivity(explicitIntent);
암시적 Intent
수행할 작업(action)과 범주(category)를 기술하고, 이에 부합하는 컴포넌트가 실행됩니다.
<activity android:name=".DetailActivity">
<intent-filter>
<action android:name="com.myapp.ACTION_OPEN_DETAIL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
Intent implicitIntent = new Intent("com.myapp.ACTION_OPEN_DETAIL");
startActivity(implicitIntent);
추가 category를 명시하려면 addCategory()를 사용하며, 매칭되는 intent-filter에 동일한 category가 선언되어 있어야 합니다.
시스템 앱 연동
웹 브라우저, 전화 앱 등 외부 앱을 호출할 수 있습니다.
// 웹 페이지 열기
Intent webIntent = new Intent(Intent.ACTION_VIEW);
webIntent.setData(Uri.parse("https://developer.android.com"));
startActivity(webIntent);
// 전화 걸기
Intent dialIntent = new Intent(Intent.ACTION_DIAL);
dialIntent.setData(Uri.parse("tel:1588-0000"));
startActivity(dialIntent);
intent-filter의 <data> 태그로 scheme, host, port, path, mimeType 등을 제한할 수 있습니다.
<data android:scheme="https"
android:host="developer.android.com"
android:pathPattern="/.*"/>
Activity 간 데이터 교환
다음 화면으로 데이터 전달
Intent forwardIntent = new Intent(EntryActivity.this, DetailActivity.class);
forwardIntent.putExtra("user_nickname", "AndroidDev");
startActivity(forwardIntent);
수신 측에서는 getIntent()로 값을 추출합니다.
Intent received = getIntent();
String nickname = received.getStringExtra("user_nickname");
Log.d("DetailActivity", "수신: " + nickname);
이전 화면으로 결과 반환
현재는 ActivityResultContracts API가 권장되나, 전통적인 startActivityForResult 방식도 이해해야 합니다.
// EntryActivity (호출 측)
Intent resultIntent = new Intent(EntryActivity.this, DetailActivity.class);
startActivityForResult(resultIntent, 100);
// DetailActivity (반환 측)
Intent returnData = new Intent();
returnData.putExtra("feedback", "작업 성공");
setResult(RESULT_OK, returnData);
finish();
// EntryActivity (결과 수신)
@Override
protected void onActivityResult(int reqCode, int resCode, Intent data) {
super.onActivityResult(reqCode, resCode, data);
if (reqCode == 100 && resCode == RESULT_OK && data != null) {
String feedback = data.getStringExtra("feedback");
Log.d("EntryActivity", "반환값: " + feedback);
}
}
사용자가 Back 키로 종료하는 경우에도 데이터를 반환하려면 onBackPressed()에서 동일한 setResult 로직을 처리합니다.
Activity 생명주기
백 스택(Back Stack)
Android는 Task 단위로 Activity를 관리하며, 각 Task는 백 스택에 Activity를 쌓아둡니다. 새 Activity가 시작되면 스택에 푸시되고, finish() 호출 시 팝됩니다.
Activity 상태
| 상태 | 설명 |
|---|---|
| 활성(Running) | 스택 최상위, 사용자와 상호작용 중 |
| 일시정지(Paused) | 화면에 부분적으로 보이나 포커스 없음 |
| 중단(Stopped) | 완전히 가려짐, 메모리 부족 시 수거 대상 |
| 소멸(Destroyed) | 스택에서 제거됨 |
생명주기 콜백
| 콜백 | 호출 시점 |
|---|---|
onCreate() | 최초 생성 시, 초기화 작업 수행 |
onStart() | 화면에 보이기 시작할 때 |
onResume() | 사용자 상호작용 가능한 최전방 상태 |
onPause() | 다른 Activity가 최전방으로 올라올 때 |
onStop() | 완전히 가려질 때 (다이얼로그 Activity는 onPause만 호출) |
onDestroy() | 소멸 직전 |
onRestart() | Stopped 상태에서 다시 활성화될 때 |
생명주기 구간
- 전체 생존기:
onCreate()~onDestroy() - 가시 생존기:
onStart()~onStop() - 전경 생존기:
onResume()~onPause()
상태 저장과 복원
시스템에 의해 Activity가 소멸되기 전에 onSaveInstanceState()가 호출됩니다. Bundle에 데이터를 저장하면 onCreate()나 onRestoreInstanceState()에서 복원할 수 있습니다.
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("draft_content", editText.getText().toString());
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String draft = savedInstanceState.getString("draft_content");
editText.setText(draft);
}
}
Activity 관리 유틸리티 패턴
모든 Activity를 중앙에서 관리하면 한 번에 종료하거나 현재 실행 중인 화면을 파악하기 쉽습니다.
public class ActivityTracker {
private static final List<Activity> runningActivities = new ArrayList<>();
public static void attach(Activity activity) {
runningActivities.add(activity);
}
public static void detach(Activity activity) {
runningActivities.remove(activity);
}
public static void terminateAll() {
for (Activity activity : runningActivities) {
if (activity != null && !activity.isFinishing()) {
activity.finish();
}
}
}
}
BaseActivity를 만들어 모든 Activity에서 자동으로 등록/해제되도록 구성합니다.
public abstract class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityTracker.attach(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityTracker.detach(this);
}
}