안드로이드 액티비티에서 데이터 가져오기. (How to get data from an Android Activity)

2016. 9. 28. 11:03


1. Activity에 데이터 전달하기.

새로운 Activity를 만든 다음, 다양한 타입으로 구성된 데이터를 전달하여 화면에 표시하는 방법은 [안드로이드 액티비티에 데이터 전달하기]에서 살펴보았습니다.


Activity에 데이터를 전달하기 위해, 데이터를 보내는 측에서 할 일은 Intent 객체를 생성하고 실행할 Activity를 명시적으로 지정한 다음, Intent.putExtra() 함수를 호출하여 데이터를 추가하는 것입니다. 그리고 데이터를 받는 측에서는 Activity의 getIntent() 함수를 호출하여 Intent를 획득한 다음, 데이터의 타입에 따른 Intent.getXxxExtra() 함수를 호출하여 데이터를 읽어들일 수 있죠.


결국 Activity에 데이터를 전달하는 핵심 역할을 수행하는 것은 Intent이며, Intent에서 제공되는 여러 함수(putExtra, getExtra())를 사용하여 데이터를 보낼 수 있는 것입니다.


그럼 여기서, 데이터를 보내는 것 말고, 데이터를 가져오는 경우를 한번 고민해 보도록 하죠. 즉, 데이터를 Activity에 전달하여 화면에 표시하는 것이 아니라, 새로운 데이터를 입력받기 위해 Activity를 실행하는 경우를 말하는 것입니다.

액티비티를 통한 데이터 전달


물론, 이러한 경우에도 Intent를 사용하여 데이터를 전달받을 수 있습니다. Activity 간 데이터 전달의 핵심은 바로 Intent이니까요.

1.1 데이터를 가져오기 위해 Activity를 실행하는 방법

Intent를 사용함에 있어, 데이터를 전달할 때나 가져올 때나 Intent 객체의 참조를 획득하는 것 외의 사용 방법은 동일합니다. Intent는 데이터를 보관하는 역할을 하기 때문에, Activity 간 데이터 이동 방향에 따라 사용 방법이 구분될 이유는 없는 것이죠.

Intent 사용


하지만 Activity를 실행하고, 그 실행 결과를 확인하는 코드는 다르게 작성되어야 합니다.


[안드로이드 액티비티에 데이터 전달하기]에서 살펴 본 것처럼, 단순히 데이터를 전달만 하는 경우에는 Intent 객체를 만들고 startActivity()를 호출하기만 하면 되죠. 새로 실행한 Activity로부터 데이터를 수신하지 않으므로, 관련 코드를 작성할 필요가 없는 것입니다.


그런데 실행한 Activity로부터 데이터를 가져오는 경우에는 조금 다른 작업이 필요합니다. 먼저, 새로운 Activity를 실행할 때, startActivity() 함수가 아닌 startActivityForResult()라른 함수를 사용해야 합니다. 단어 그대로, Activity를 실행한 후에 실행 결과를 확인한다는 뜻이죠.


또한 실행된 Activity에서는 실행 결과 및 데이터를 전달하기 위해, setResult()함수를 호출해야 합니다. 물론, 데이터를 전달하기 위해서 Intent 객체를 새로 하나 생성해야 하고, 그 객체를 setResult() 함수의 두 번째 파리미터로 전달해야 하죠. 그리고 finish() 함수를 호출하여 Activity의 실행을 종료해야 합니다.


마지막으로 실행된 Activity로부터 실행 결과를 확인하기 위해서는 onActivityResult()라는 이벤트핸들러 함수를 override해야 합니다. (가끔 startActivityForResult() 함수가 실행된 후, Activity 실행이 완료될 때까지 리턴되지 않고 대기하는 것으로 오해되는 경우가 있는데, startActivityForResult() 함수는 Activity 실행 완료와 관계없이 호출하자마자 바로 리턴됩니다. 대신 Activity가 실행 완료되면, 이벤트가 발생되어 onActivityResult() 함수가 호출되는 것이죠.)


위에서 꽤 많은 문장으로 복잡하게 설명되었지만, 기술한 내용을 간단하게 그림으로 표현하면 다음과 같습니다. 특히 빨간색 글씨로 작성된 내용이, 바로 위에서 설명한 과정을 나타낸 것입니다.

액티비티 데이터 전달 흐름


1.2 여러 종류의 요청에 대한 식별.

안드로이드 앱에서 데이터를 가져오기 위해 다른 Activity를 실행하는 것은 매우 흔한 일입니다. 직접 만들거나, 아니면 누군가 이미 만들어놓은, 다양한 종류의 Activity로부터 데이터를 가져올 수 있죠. 아래 그림은 A 액티비티에서 B, C, D 액티비티를 실행하여 데이터를 가져오는 흐름을 표현한 것입니다.

여러 종류의 액티비티 실행


그런데 Activity의 실행 결과를 처리하는 이벤트 핸들러 함수는 onActivityResult() 함수 하나 뿐입니다. 만약 startActivityForResult() 호출 시마다 이벤트 리스너를 생성하여 전달하는 방법을 제공한다면, Button 등의 이벤트 처리와 비슷한 방법으로 Activity 실행 결과를 처리할 수 있겠으나, 그런 방법은 제공되고 있지 않습니다. 즉, Activity 실행 결과에 대한 처리는 onActivityResult() 함수에서만 가능하도록 만들어져 있는 것이죠. 그렇다면 여러 종류의 Activity를 실행했을 때, 실행한 Activity에 대한 실행 결과는 어떻게 구분될 수 있을까요?


방법은 바로 요청 코드(requestCode)의 사용입니다. startActivityForResult() 함수와 onActivityResult() 함수의 정의를 보시죠.

    void startActivityForResult (Intent intent, int requestCode)
    void onActivityResult (int requestCode, int resultCode, Intent data)

두 함수 모두 "requestCode"라는 정수형(int) 파라미터가 정의되어 있는 것을 확인할 수 있습니다. 즉, onActivityResult() 이벤트 핸들러 함수가 실행될 때, 어떤 startActivityForResult() 함수에 대응된 이벤트인지를, "requestCode" 값으로 식별하는 것입니다. 위의 그림에서 요청과 결과 화살표에서 ()에 들어간 숫자가 바로 요청 코드(requestCode) 값를 나타낸 것입니다. (요청 코드(requestCode)는 0 이상의 정수 값을 사용해야 합니다.)

public class MainActivity extends AppCompatActivity {
    static final int GET_DATA_REQUEST = 1 ;

    private void getData() {
        // ...

        startActivityForResult(intent, GET_DATA_REQUEST) ;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == GET_DATA_REQUEST) {
            // ...
        }
    }
}

1.3 Activity 실행의 성공 여부 판단

새로운 Activity를 실행한다고 해서, 반드시 원하는 데이터가 전달된다고 가정해서는 안됩니다. 사용자가 데이터 입력을 명시적으로 취소하거나, 뒤로(Back) 버튼을 누르는 등, 여러 가지 이유로 데이터 입력 없이 Activity가 종료될 수도 있기 때문에, 데이터를 가져오는 작업은 Activity의 실행이 성공했을 때만 수행되어야 하는 것이죠.


Activity 실행의 설공 여부는 onActivityResult() 함수의 두 번째 파라미터인 "resultCode" 값을 확인하여 판단할 수 있습니다.


resultCode 값이 "RESULT_OK"라면 Activity의 실행이 성공했음을 의미하고, "RESULT_CANCELED" 라면 실패를 의미합니다. 즉, resultCode가 RESULT_OK인 경우에만 데이터를 가져오는 코드가 수행되도록 만들면 되는 것입니다.

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        // ...

        if (resultCode == RESULT_OK) {
            // TODO : get data from intent.
        }
    }

2. Activity에서 데이터 가져오기.

위에서 설명한 내용들에 대한 구체적인 코드 작성법을, 예제를 통해 살펴보도록 하겠습니다.

2.1 예제 구성도

예제 화면의 구성도는 아래와 같습니다. [안드로이드 액티비티에 데이터 전달하기]에서 작성한 예제와 정반대로 구성되어 있습니다.

Activity에서 데이터 가져오기 예제 화면 구성도


2.2 MainActivity에 표시될 Layout 구성.

예제 화면 구성도에서 설계한 내용대로 MainActivity에 표시될 Layout을 구성합니다.

[STEP-1] "content_main.xml" - MainActivity에 표시될 Layout 구성.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.activityexample3.MainActivity"
    tools:showIn="@layout/activity_main">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:gravity="center"
        android:background="#FFE0E0"
        android:layout_marginBottom="8dp"
        android:id="@+id/textViewNo"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:gravity="center"
        android:background="#E0FFE0"
        android:layout_marginBottom="8dp"
        android:id="@+id/textViewName"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:gravity="center"
        android:background="#E0E0FF"
        android:layout_marginBottom="8dp"
        android:id="@+id/textViewPhone"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:gravity="center"
        android:background="#FFE0FF"
        android:id="@+id/textViewOver20"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="24sp"
        android:text="Add Contact"
        android:id="@+id/buttonAddContact" />

</LinearLayout>

2.3 연락처 Activity 추가 및 Layout 구성.

다음, MainActivity에서 실행되어, 사용자로부터 연락 정보를 입력받는 기능을 수행할 연락처 Activity를 추가합니다.


먼저 프로젝트에 새로운 Activity를 추가합니다. (Activity를 추가하는 방법은 [안드로이드 액티비티 실행하기 - 3.2 Activity 추가를 위한 쉬운 방법]의 내용을 참고하시기 바랍니다.)

[STEP-2.1] ContactActivity 추가.

ContactActivity 추가


예제 화면 구성도에 따라 ContactActivity에 표시될 Layout을 구성합니다.

[STEP-2.2] "content_contact.xml" - ContactActivity를 위한 Layout 작성.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.recipes4dev.examples.activityexample3.ContactActivity"
    tools:showIn="@layout/activity_contact">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:stretchColumns="1">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="No" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextNo"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Name" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextName"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Phone" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:textSize="24sp"
                android:id="@+id/editTextPhone"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:textSize="24sp"
                android:text="Over20" />

            <CheckBox
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="20dp"
                android:id="@+id/checkBoxOver20"/>
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_span="3"
                android:textSize="24dp"
                android:text="Save"
                android:id="@+id/buttonSave" />

        </TableRow>
    </TableLayout>

</RelativeLayout>

2.4 연락처 Activity 실행하기

연락처 Activity(ContactActivity)를 실행하기에 앞서, Activity 실행 요청을 식별하기 위한 요청 코드(requestCode)를 정의합니다. (1 또는 2 등과 같이, 숫자를 바로 써도 상관없지만, 실행할 Activity가 많아졌을 때를 대비하여 상수를 정의해서 사용하는 것이 좋습니다.)

[STEP-3.1] "MainActivity.java" - 요청 코드(requestCode) 정의.
public class MainActivity extends AppCompatActivity {

    static final int REQ_ADD_CONTACT = 1 ;

    // 코드 계속 ...
}

MainActivity에서 "Add Contact" 버튼 클릭 시, 연락처 Activity(ContactActivity)를 실행하는 코드를 작성합니다.

[STEP-3.2] "MainActivity.java" - "Add Contact" Button 클릭 이벤트 추가.
public class MainActivity extends AppCompatActivity {

    // ... 코드 계속
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button buttonAddContact = (Button) findViewById(R.id.buttonAddContact) ;
        buttonAddContact.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO : create intent and start activity.
                Intent intent = new Intent(MainActivity.this, ContactActivity.class) ;
                startActivityForResult(intent, REQ_ADD_CONTACT);
            }
        });
    }
}

2.5 연락처 Activity에서 입력받은 연락처 전달하기

연락처 Activity(ContactActivity)에서 각 필드에 입력된 데이터를, Intent를 통해 MainActivity에 전달하는 코드를 작성합니다. 이는 "Save" 버튼을 눌렀을 때만 적용되어야 하므로, "Save" 버튼의 클릭 이벤트에서 실행되도록 작성합니다.

[STEP-4] "ContactActivity.java" - "Save" 버튼 클릭 시, 필드에 입력된 데이터 전달.
public class ContactActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // ... 코드 계속

        Button buttonSave = (Button) findViewById(R.id.buttonSave) ;
        buttonSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Intent 객체 생성.
                Intent intent = new Intent() ;

                // No 입력 값을 int 값으로 변환하여 전달.
                EditText editTextNo = (EditText) findViewById(R.id.editTextNo) ;
                String strNo = editTextNo.getText().toString() ;
                if (!strNo.isEmpty() && strNo.matches("^[0-9]*$")) { // check numbers by RegEx.
                    intent.putExtra("contact_no", Integer.parseInt(strNo)) ;
                } else {
                    intent.putExtra("contact_no", 0) ;
                }

                // Name 입력 값을 String 값으로 그대로 전달.
                EditText editTextName = (EditText) findViewById(R.id.editTextName) ;
                intent.putExtra("contact_name", editTextName.getText().toString()) ;

                // Phone 입력 값을 String 값으로 그대로 전달.
                EditText editTextPhone = (EditText) findViewById(R.id.editTextPhone) ;
                intent.putExtra("contact_phone", editTextPhone.getText().toString()) ;

                // Over20 값을 boolean 값으로 전달.
                CheckBox checkBoxOver20 = (CheckBox) findViewById(R.id.checkBoxOver20) ;
                intent.putExtra("contact_over20", checkBoxOver20.isChecked()) ;

                setResult(RESULT_OK, intent) ;
                finish() ;
            }
        });
    }
}

2.6 연락처 Activity로부터 가져온 데이터 표시

연락처 Activity(ContactActivity)의 기능을 작성하였으므로, 연락처 Activity로부터 전달받은 연락처 정보를 MainActivity에 표시하는 코드를 작성합니다. [1.1 데이터를 가져오기 위해 Activity를 실행하는 방법]에서 설명한대로, onActivityResult() 함수를 override하여 Activity 실행 결과를 처리합니다.

[STEP-5] "MainActivity.java" - onActivityResult() 함수에서 Intent로 전달된 데이터를 가져와서 표시.
public class MainActivity extends AppCompatActivity {

    // ... 코드 계속.

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        if (requestCode == REQ_ADD_CONTACT) {
            if (resultCode == RESULT_OK) {
                // No 값을 int 타입에서 String 타입으로 변환하여 표시.
                TextView textViewNo = (TextView) findViewById(R.id.textViewNo) ;
                int no = intent.getIntExtra("contact_no", 0) ;
                textViewNo.setText(Integer.toString(no)) ;

                // Name 값을 String 타입 그대로 표시.
                TextView textViewName = (TextView) findViewById(R.id.textViewName) ;
                String name = intent.getStringExtra("contact_name") ;
                if (name != null)
                    textViewName.setText(name) ;

                // Phone 값을 String 타입 그대로 표시.
                TextView textViewPhone = (TextView) findViewById(R.id.textViewPhone) ;
                String phone = intent.getStringExtra("contact_phone") ;
                if (phone != null)
                    textViewPhone.setText(phone) ;

                // Over 20 값을 boolean 타입 그대로 검사하여 표시.
                TextView textViewOver20 = (TextView) findViewById(R.id.textViewOver20) ;
                boolean over20 = intent.getBooleanExtra("contact_over20", false) ;
                if (over20)
                    textViewOver20.setText("Over 20") ;
                else
                    textViewOver20.setText("Not over 20") ;
            }
        }
    }
}

3. 예제 실행 화면.

예제를 작성하고 실행하면, 공백의 TextView들과 함께 "Add Contact" 버튼이 표시되고, "Add Contact" 버튼을 누르면 연락처 Activity(ContactActivity)가 표시됩니다. 연락처 Activity에서 연락처 정보를 입력하고, "Save" 버튼을 누르면 입력된 연락처 정보가 화면에 표시되는 것을 확인할 수 있습니다.

액티비티 예제 실행 화면


만약 "Save" 버튼을 누르지 않고, "Back" 키를 입력하면 MainActivity에 어떠한 값도 표시되지 않는 것을 확인할 수 있습니다.

Back 버튼 눌렀을 때


4. 참고

.END.


ANDROID 프로그래밍/ACTIVITY