웹/모바일 안드로이드 기초

3.1 액티비티와 인텐트

소개

액티비티는 안드로이드 UI 의 핵심 구성요소로 플랫폼 기반 애플리케이션 모델의 기본 요소가 됩니다. 예를 들어 C언어나 자바의 경우 main() 메서드가 프로그램을 실행하기 위한 진입점 이라면 안드로이드의 경우 사용자가 직접 프로그램을 실행하는 것이 아니라 플랫폼을 통해 실행되는 구조 이기 때문에 UI를 가지는 기본 앱의 시작점이 액티비티가 되는 것입니다.

인텐트는 기본적으로 액티비티와 액티비티를 연결해 주는 역할을 수행 합니다.

Activity

안드로이드 앱에서 액티비티는 사용자와의 상호작용을 위한 하나의 화면을 말합니다. 예를 들어 노트앱에서 목록을 보여주는 메인 화면은 MainActivity가 될수 있으며 새로운 노트 작성 버튼을 클릭했을 때 실행되는 입력 화면은 또다른 액티비티가 될 수 있습니다.

새로운 액티비티가 시작되면 이전의 액티비티는 멈추게 되고 스택에 저장되게 됩니다. 현재 액티비티 사용을 마치고 Back 버튼을 통해 이전 액티비티로 돌아가게 되면 현재 액티비티는 제거 되고 기존 액티비티가 다시 시작되게 됩니다.

액티비티의 라이프사이클에 대해서는 3.2에서 다루게 됩니다.

[그림: 액티비티 활용 예], 임시 그림임.

Activity 생성

새로운 안드로이드 프로젝트를 만들게 되면 기본적으로 MainActivity 클래스가 생성되고 AndroidManifest.xml 에 등록되게 됩니다. 새로운 프로젝트 생성 없이 기존의 프로젝트에 새로운 액티비티만 추가하려면 다음과 같은 과정을 거쳐야 합니다.

  1. 액티비티 클래스 생성(AppCompatActivity 상속)
  2. xml 레이아웃파일을 이용해 UI 구현
  3. 액티비티 클래스의 onCreate() 메서드에서 레이아웃 리소스로 화면 구성
  4. AndroidManifest.xml 에 세로운 액티비티 등록

물론 이 과정은 직접하지 않아도 되며 프로젝트에서 File -> New -> Activity 메뉴를 통해 프로젝트 생성때와 같이 템플릿을 선택할 수 있으며 안드로이드 스튜디오를 통해 자동으로 관련 작업들이 진행 됩니다.

이미 여러번 살펴본 기본 액티비티 클래스 코드는 다음과 같습니다.

MainActivity.java

public class MainActivity extends AppCompatActivity {
   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
   }
}

매니페스트파일에는 앱의 정보들이 들어가는데 여기에 추가되지 않은 액티비티 클래스는 앱에서 사용할 수 없습니다.

AndroidManifest.xml

<activity android:name=".MainActivity" >
   <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>


Intent

인텐트는 안드로이드 런타임에서 액티비티 혹은 다른 컴포넌트나 앱을 실행하도록 요청하는 일종의 메시지 객체라고 할 수 있습니다.

앱이 처음 시작되면 안드로이드 런타임은 인텐트AndroidMenifest.xml에 정의된 앱의 메인 액티비티 에 전달 합니다.

[그림: 인텐트 개념], 출처:https://developer.android.com

앱에서 다른 액티비티(앱에 포함되어 있거나 다른앱에 포함된)를 시작하려면 인텐트 를 만들고 startActivity() 메서드를 호출해 인텐트 를 전달해야 합니다.

액티비티를 시작하는것 이외에도 인텐트는 액티비티간의 데이터 교환을 지원 합니다. 독립된 액티비티 혹은 앱간에 데이터를 주고받는 것은 중요한 기능이며 액티비티를 잘 이해하고 앱 개발에 활용할 수 있어야 합니다.

인텐트는 Explicit intentImplicit intent 두가지 유형으로 구분됩니다.

Explicit intent

명시적 인텐트라고 하며 실행하고자 하는 액티비티의 클래스 이름을 사용하는 형태를 말합니다. 보통 자신이 만든 앱의 다른 액티비티를 실행할 때 사용하게 됩니다. 앱 화면에서 이벤트에 따라 다른 화면으로 이동하는 형태를 생각하면 됩니다.

명시적 인텐트 생성

만일 MemberListActivity 라는 액티비티 클래스가 있다고 하면 다음과 같이 인텐트를 만들어 실행할 수 있습니다.

Intent listIntent = new Intent(this, MemberListActivity.class);
startActivity(listIntent);

액티비티 돌아가기

인텐트에 의해 새로운 액티비티가 시작된 다음 이전 화면으로 돌아가기 위해서는 백버튼을 누르면 됩니다. 이때 액티비티는 자동 종료 되며 원래의 액티비티로 돌아가게 됩니다.

안드로이드 스마트폰 기기마다 다소 차이가 있지만 화면 하단의 소프트웨어 백버튼(혹은 물리버튼)을 이용하는것이 기본입니다.

앱 구현에 따라 App Bar(Action Bar) 에 백버튼을 표시할수도 있습니다. 이런 경우 메니페스트 파일에 다음과 같이 parentActivityName을 지정하면 됩니다.

<activity android:name=".SubActivity"
    android:parentActivityName=".MainActivity">
</activity>

만일 추가적인 설정이 필요한 경우라면 액티비티의 onCreate() 메서드에서 Action Bar에 이전 액티비티로 돌아가기 위한 텍스트나 아이콘을 지정할 수 있습니다.

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setTitle("Back");
}

매니페스트 파일에 parentActivityName 이 지정되어 있기 때문에 자동으로 해당 액티비티로 이동하게 됩니다. 만일 매니페스트 파일에 등록되지 않거나 이벤트 발생시 추가적인 처리가 필요한 경우 메뉴에서와 같이 onOptionsItemSelected() 메서드를 오버라이딩하고 android.R.id.home에서 이벤트가 발생한 경우 onBackPressed() 메서드를 호출하도록 구현하면 됩니다.

@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
    switch(item.getItemId()) {
        case android.R.id.home: onBackPressed();return true;
        default: return super.onOptionsItemSelected(item);
    }
}

액티비티 종료

만일 특정 이벤트 동작으로 액티비티를 프로그램 코드로 종료하려면 이벤트 콜백 메서드에서 다음과 같이 finish() 메서드를 호출하면 됩니다. 현재 액티비티가 종료되면 바로 이전 액티비티로 돌아가게 됩니다.

public void closeActivity (View view) {
    finish();
}

Implicit intent

암시적 인텐트라고 하며 클래스명이 아닌 Intent Filter 정보를 사용하는 형태로 클래스 이름을 알 수 없는 다른 앱의 컴포넌트를 실행할 때 사용하게 됩니다. 예를 들면 전화번호부, 이메일, 웹브라우저등으로 연결하는 경우를 생각하면 됩니다.

물론 자신이 만든 앱의 액티비티를 다른 앱에서 실행 가능하게 할수도 있으며 이 경우 필요한 정보들이 메니페이스 파일에 등록되어야 합니다.

암시적 인텐트를 통해 다른 앱과 데이터를 공유하는 경우 특정 앱에 최적화된 정보를 제공하는 것은 한계가 있기 때문에 경우에 따라서는 해당 앱에서 제공하는 공유 라이브러리등을 사용해야 합니다.
예를 들어 대표적인 스마트폰 메시지 프로그램인 카카오톡의 경우 앱에서 카카오톡으로 데이터 공유를 지원하기 위해 카카오링크, 카카오톡 API를 제공하고 있습니다.

암시적 인텐트 생성

기본적으로 명시적 인텐트 생성과 동일 합니다. 다만, 클래스 이름 대신 해당 앱의 <intent-filter>에 등록된 인텐트 정보가 필요 합니다.

Intent phoneIntent = new Intent(Intent.ACTION_DIAL);
startActivity(phoneIntent);

<intent-filter>

인텐트 필터 정보는 안드로이드 시스템에 제공하는 정보로 다른앱에서 내가 만든 앱의 액티비티 호출에 필요한 정보들을 담고 있습니다.

MainActivity 이외 다른 액티비티를 생성했다면 기본적으로 다음과 같은 내용이 메니페스트 파일에 추가 됩니다.

<activity android:name=".ListActivity" >
   <intent-filter>
      <action android:name="android.intent.action.SEND" />
      <category android:name="android.intent.category.DEFAULT" />
      <data android:scheme="http" />
   </intent-filter>
</activity>

action 컴포넌트(액티비티)의 기능 유형으로 현재 액티비티가 어떤 인텐트 요청에 동작하도록 할 것인지 결정.

category

data



액티비티간 데이터 전달

액티비티간 데이터 전달은 기본적으로 인텐트를 통해 이루어집니다. 텍스트, 바이너리 데이터의 전달이 가능하며 다중 데이터의 전달도 가능합니다.

텍스트 데이터 주고 받기

인텐트를 생성하고 해당 인텐트에 putExtra() 메서드를 통해 텍스트 정보를 키값과 함께 전달합니다. 이때 사용자 정의 키값을 사용하는 경우 내가 만든 앱이 아닌 다른 앱에서는 해당 정보를 알 수 없기 때문에 범용으로 사용하기 어렵습니다. 이경우 Intent.EXTRA_TEXT와 같이 사전정의된 몇몇 키값을 사용할 수 있습니다.

...
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT, "전달할 문자열");

데이터를 받는 액티비티에서는 다음과 같이 getStringExtra() 메서드를 사용하면 됩니다.

...
textTxt.setText(intent.getStringExtra(Intent.EXTRA_TEXT));

바이너리 데이터 보내기

바이너리 데이터를 보내기 위해서는 mime-type 을 image/jpeg 등 적절한 타입을 사용해야 합니다

Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_STREAM, uriToImage);
shareIntent.setType("image/jpeg");
startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.send_to)));

카메라 촬영 사진 가져오기

앱에서 카메라 혹은 앨범을 호출하는 것도 앞에서와 마찬가지로 액티비티를 호출하는 것이며 암시적 인텐트를 사용 합니다. 이때 카메라에서 촬영한 사진을 받아오기 위해서는 별도의 작업이 필요 합니다.

먼저 카메라를 호출하기 위한 이벤트 콜백 메서드를 작성 합니다. 기존과 같이 단순하게 startActivity() 를 호출하는 것이 아니라 startActivityForResult() 를 호출합니다.

private final int REQUEST_PHOTO = 1;

public void photoBtnClick(View v) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_PHOTO);
    }
}

카메라로 부터 가져온 사진을 처리하기 위해서는 onActivityResult() 라는 메서드를 오버라이딩 해주어야 합니다. 이때 액티비티 호출에 사용된 구분값과 상태코드, 데이터등이 인자로 전달 됩니다.

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode == REQUEST_PHOTO && resultCode == RESULT_OK) {
        Bitmap bitmap = (Bitmap) data.getExtras().get("data");
        photoBtn.setImageBitmap(bitmap);
    }
}

실습 및 참고자료

실습: 두개의 화면으로 구성된 앱을 통해 액티비티와 인텐트의 동작을 이해하기 위한 예제

메인 액티비티 이외의 액티비티를 가지는 앱을 만들고 명시적, 암시적 액티비티 호출을 통해 액티비티간 화면 전환과 데이터 교환 방법을 실습 합니다.

본 예제는 다음과 같은 내용들을 학습하기 위한 내용으로 구성 됩니다.

프로젝트 준비

실행 결과

따라하기 및 코드 설명

유튜브 동영상 강좌를 참고해 주세요


참고자료