[Do it! 안드로이드 앱 프로그래밍 #9] 인텐트, 플래그 그리고 부가 데이터 사용하기
Android

[Do it! 안드로이드 앱 프로그래밍 #9] 인텐트, 플래그 그리고 부가 데이터 사용하기

09-1 인텐트 살펴보기

바로 앞 실습에서 인텐트는 다른 액티비티를 띄우거나 기능을 동작시키기 위한 수단으로 사용했다. 즉, 무언가 작업을 수행하기 위해 명령하거나 데이터를 전달하는 데 사용했다. 이 과정을 조금 더 자세히 설명하면 인텐트를 만든 후 startActivity나 startActivityForResult 메서드를 호출하면서 인텐트를 시스템에 전달했다. 그러면 시스템은 그 인텐트 안에 들어 있는 명령을 확인하고 액티비티를 띄운 것이다.

 

 

9-1-1 인텐트의 역할과 사용 방식

.android.content 패키지 안에 정의되어 있는 인텐트는 앱 구성 요소 간에 작업 수행을 위한 정보를 전달하는 역할을 한다. 안드로이드 앱의 구성 요소는 네 가지로, 액티비티(Activity), 서비스(Service), 브로드캐스트 수신자(Broadcast Receiver), 내용 제공자(Content Provider)가 있다.

 

인텐트의 기본 구성 요소는 액션(Action)과 데이터(Data)이다. 액션은 수행할 기능이고 데이터는 액션이 수행될 대상의 데이터를 의미한다.

 

액션과 데이터를 이용해 인텐트를 만들고 필요한 액티비티를 띄워주는 대표적인 경우는 다음과 같다.

 

속성 설명
ACTION_DIAL tel:01077881234 주어진 전화번호를 이용해 전화걸기 화면을 보여줌
ACTION_VIEW tel:01077881234 주어진 전화번호를 이용해 전화걸기 화면을 보여줌
URI 값의 유형에 따라 VIEW 액선이 다른 기능을 수행함
ACTION_EDIT content://contacts/people/2 전화번호부 데이터베이스에 있는 정보 중에서 ID 값이 2인 정보를 편집하기 위한 화면을 보여줌
ACTION_VIEW content://contacts/people 전화번호부 데이터베이스의 내용을 보여줌

 

인텐트는 그 외에도 여러 가지 속성을 가지고 있다. 대표적인 것으로 범주(Category), 타입(Type), 컴포넌트(Component), 부가 데이터(Extra Data)를 들 수 있다.

인텐트를 이용하는 대표적인 두 가지 경우는 인텐트에 액션과 데이터를 넣어 다른 앱의 액티비티를 띄우는 경우와 컴포넌트 이름을 이용해 새로운 액티비티를 띄우는 경우가 있다. 첫 번째 경우는 버튼을 눌러 전화걸기 화면으로 이동하는 것을 예로 들 수 있으며, 첫 글에서 배워보았고, 두 번째 경우는 바로 전 글에서 배워보았다.

 

09-2 플래그와 부가 데이터 사용하기

액티비티로 만든 화면이 한 번 메모리에 만들어졌는데도 계속 startActivity나 startActivityForResult 메서드를 호출하면 동일한 액티비티가 메모리에 여러 개 만들어진다. 중복된 액티비티가 만들어지게 되는데, 이때 시스템의 [BACK] 버튼을 누르면 이전에 만들어진 액티비티가 나타난다.

 

 

9-2-1 플래그

액티비티는 액티비티 매니저(ActivityManager)라는 객체에 의해 액티비티 스택(ActivityStack)이라는 것으로 관리된다. 그리고 이 스택은 액티비티를 쌓아두었다가 가장 상위에 있던 액티비티가 없어지면 이전의 액티비티가 다시 화면에 보이게 한다. 만약 동일한 액티비티를 여러 번 실행한다면 동일한 액티비티가 여러 개 스택에 들어가게 되고 동시에 데이터를 여러 번 접근하거나 리소스를 여러 번 사용하는 문제가 발생할 수 있다. 이 문제를 해결할 수 있도록 도와주는 것이 플래그(Flag)이다. 대표적인 플래그들은 다음과 같다.

 

FLAG_ACTIVITY_SINGLE_TOP

FLAG_ACTIVITY_NO_HISTORY

FLAG_ACTIVITY_CLEAR_TOP

 

새 액티비티를 띄우는 것이 아닌, 플래그를 이용해 기존의 액티비티를 사용하는 경우라면 인텐트 객체는 어떻게 전달받을까? 원래는 새로 만들어진 액티비티의 onCreate 메서드 안에서 getIntent 메서드로 참조할 수 있었지만, 액티비티를 재사용하는 경우라면 onCreate 메서드가 호출되지 않는다. 그래서 onNewIntent 메서드를 재정의해 인텐트 객체를 전달받는다. 이 내용은 추후에 더 자세하게 설명한다.

 

 

9-2-2 부가 데이터

한 액티비티에서 다른 액티비티를 띄울 때 데이터를 전달해야 하는 경우가 있다. 가장 간단한 방법은 별도의 클래스를 만든 다음 그 안에 클래스 변수(static 키워드를 이용해 선언한 변수)를 만들어 두 개의 화면에서 모두 그 변수를 참조하게 하는 방법이다. 하지만 안드로이드는 다른 앱에서 우리가 만든 화면을 띄울 수도 있기 때문에 변수를 공유하는 방식으로 데이터를 전달하는 것이 불가능할 수도 있다. 따라서 액티비티를 띄울 때 전달되는 인텐트 안에 부가 데이터(Extra data)를 넣어 전달하는 방법을 권장한다.

 

인텐트 안에는 번들(Bundle) 객체가 들어 있는데, 번들 객체는 putExtra와 get___Extra 메서드로 데이터를 넣거나 빼낼 수 있다. 대표적인 메서드는 다음과 같다.

 

Intent putExtra(String name, String value)

Intent putExtra(String name, int value)

Intent putExtra(String name, boolean value)

 

String getStringExtra(String name)

int getIntExtra(String name, int defaultValue)

boolean getBooleanExtra(String name, boolean defaultValue)

 

안드로이드는 객체를 전달할 때 Parcelable 인터페이스의 사용을 권장한다. Parcelable 인터페이스를 사용하기 위해서는 다음의 두 가지 메서드를 모두 구현해야 한다.

 

public abstract int describeContents()

public abstract void writeToParcel(Parcel dest, int flags)

 

▼ Parcelable 인터페이스 구현하기

public class SimpleData implements Parcelable {

    int number;
    String message;

    public SimpleData(int num, String msg){
        number = num;
        message = msg;
    }

    // Parcel 객체에서 읽기
    public SimpleData(Parcel src){
        number = src.readInt();
        message = src.readString();
    }

    // CREATOR 상수 정의
    public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){

        // SimpleData 생성자를 호출해 Parcel 객체에서 읽기
        @Override
        public SimpleData createFromParcel(Parcel in) {
            return new SimpleData(in);
        }

        @Override
        public SimpleData[] newArray(int size) {
            return new SimpleData[size];
        }
    };

    @Override
    public int describeContents()  {
        return 0;
    }

    // Parcel 객체로 쓰기
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(number);
        dest.writeString(message);
    }
}

-SimpleData 클래스는 Parcelable 인터페이스를 구현하고 있다.

 

-클래스 안에 정의된 인스턴스 변수는 두 개이며, 하나는 문자열이고 하나는 정수이다.

 

-CREATOR 상수는 Parcel 객체로부터 데이터를 읽어 들여 객체를 생성하는 역할을 한다. 이 객체는 상수로 정의되고 반드시 static final로 선언되어야 한다.

 

-describeContents 메서드는 직렬화하려는 객체의 유형을 구분할 때 사용한다. 여기서는 단순히 0을 반환하도록 한다.

 

-writeToParcel 메서드는 객체가 가지고 있는 데이터를 Parcel 객체로 만들어주는 역할을 한다.

 

일단은 책에 나와있는 대로 코드를 작성하긴 했는데, 설명을 아무리 봐도 CREATOR 상수를 만드는 이유와 describeContents 메서드의 역할을 이해하지 못하겠다.ㅜㅜ

 

▼ 인텐트 객체 만들고 부가 데이터 전송하기

public class MainActivity extends AppCompatActivity {
    public static final int REQUEST_CODE_MENU = 101;
    public static final String KEY_SIMPLE_DATA = "data";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), MenuActivity.class);
                
                // SimpleData 객체 생성
                SimpleData data = new SimpleData(100, "Hello Android!");
                
                // 인텐트에 부가 데이터로 넣기
                intent.putExtra(KEY_SIMPLE_DATA, data);
                startActivityForResult(intent, REQUEST_CODE_MENU);
            }
        });
    }
}

 

▼ 전달받은 데이터 보여주기

public class MenuActivity extends AppCompatActivity {
    TextView textView;

    public static final String KEY_SIMPLE_DATA = "data";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_menu);

        textView = findViewById(R.id.textView);
        Button button = findViewById(R.id.button2);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });

        Intent intent = getIntent();
        processIntent(intent);
    }

    private void processIntent(Intent intent){
        if(intent != null){
            Bundle bundle = intent.getExtras();
            SimpleData data = bundle.getParcelable(KEY_SIMPLE_DATA);
            textView.setText("전달받은 데이터 \n Number: " + data.number
                    + "\n Message: " + data.message);
        }
    }
}

위의 두 소스 코드는 바로 전 글에서도 해보았기 때문에 이해하기 어렵지 않다.

 

부가 데이터로 객체 전달하기
부가 데이터로 객체 전달하기