Android Activity 핵심 메커니즘과 실전 활용

Activity 직접 구성하기

Android Studio에서 새 프로젝트를 생성할 때 No Activity를 선택합니다. res 디렉터리 하위에 layout 폴더를 추가하고, Empty Activity 템플으로 새 Kotlin/Java 파일을 만듭니다. Generate layout file 옵션은 해제한 채로 진행하여 수동으로 레이아웃을 연결합니다.

구성 순서

  1. XML 레이아웃 리소스 작성
  2. Activity 클래스 생성 및 onCreate() 오버라이드
  3. 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);
    }
}

태그: Android Activity Intent Activity Lifecycle onSaveInstanceState

5월 29일 07:26에 게시됨