[Android] 플래그(flag)와 부가 데이터(Extra Data)
모든 내용은 Do it! 안드로이드 앱 프로그래밍을 바탕으로 정리한 것입니다.
액티비티로 만든 화면이 한 번 메모리에 만들어졌는데도 계속 startActivity()나 startActivityForResult() 메서드를
여러 번 호출하면 동일한 액티비티가 메모리에 여러 개 만들어 질 것
∵ 시스템이 인텐트 별로 새 액티비티를 띄워주기 때문
💡 중복된 액티비티를 띄우지 않으려면? 플래그(Flag)로 조정할 수 있다!
플래그(flag)
액티비티가 처리되는 방식
액티비티는 액티비티 매니저(ActivityManager)라는 객체에 의해 '액티비티 스택(Activity Stack)'으로 관리됨.
이 스택은 액티비티를 쌓아두었다가 가장 상위에 있던 액티비티가 없어지면 이전의 액티비티가 다시 화면에 보이게 함
- 새로운 액티비티를 만들어 AndroidManifest.xml 파일에 등록 시, startActivity() / startActivityForResult() 메서드를 사용해 실행됨.
- 새로운 액티비티가 화면에 띄워지면 이전에 있던 액티비티는 액티비티 스택에 저장되고 새로운 액티비티가 화면에 보이는 구조.
- 화면에 보이던 액티비티가 없어지면 액티비티 스택의 가장 위에 있던 액티비티가 화면에 보이면서 화면 기능이 다시 동작함.
→ 새로운 화면이 보이면 이전 화면들은 그 화면 뒤에 차곡차곡 가려져 있는 것!
☝ 동일한 액티비티를 여러 번 실행한다면?
🤦♀️동일한 액티비티 여러 개가 스택에 들어가게 되고 동시에 데이터를 여러 번 접근하거나 리소스를 여러 번 사용하는 문제 발생!
💡 이것을 해결하는 것이 플래그(flag)
플래그(flag)
FLAG_ACTIVITY_SINGLE_TOP
액티비티를 생성할 때 이미 생성된 액티비티가 있으면 그 액티비티를 그대로 사용하라는 플래그
→ 기존에 만든 동일한 액티비티를 그대로 사용하는 것
☝ 화면에 보이는 액티비티가 새로 만들어지지 않고 기존에 있는 것이 보인다면, 시스템에서 전달하는 인텐트 객체는 어떻게 전달받는가?
🤷♀️ 재사용된 액티비티는 onCreate() 메서드가 호출되지 않음.
그렇다면 기존의 onCreate() 메서드 안에서 getIntent() 메서드로 참조하는 방식을 사용하지 못함.
새로 띄워지는 액티비티에서 인텐트를 전달 받아 처리하는 방법이 따로 있어야 함!
💡 onNewIntent() 메서드 사용하기!
여기서 부모 액티비티는 새로운 액티비티를 띄워주는 액티비티를 말함.
→ onNewIntent() 메서드를 재정의하면 액티비티가 새로 만들어지지 않을 때 인텐트 객체만 전달할 수 있음
public void onNewIntent(Intent intent)
→ 액티비티가 이미 객체로 만들어져 있을 때 시스템으로 자동으로 호출되며 파라미터로 인텐트 객체를 전달받을 수 있음
FLAG_ACTIVITY_NO_HISTORY
처음 이후에 실행된 액티비티는 액티비티에 추가하지 않는다는 플래그
→ 플래그가 설정되지 않은 경우 [Back] 키를 누르면 이전 액티비티가 보이지만, 해당 플래그가 설정되면 항상 맨 처음에 실행되었던 액티비티가 바로 보이게 되는 것
👍 알람 이벤트가 발생하여 사용자에게 한 번 알림 화면을 보여주고 싶을 때 유용함
→ 알람 화면은 한 번만 보여주면 되므로 여러 번 알람 이벤트가 발생하더라도 그 화면만 한 번 보여주는 형태로 만들 수 있음
FLAG_ACTIVITY_CLEAR_TOP
이 액티비티 위에 있는 다른 액티비티를 모두 종료시키는 플래그
👍 홈 화면과 같이 다른 액티비티보다 항상 우선하는 액티비티를 만들 때 유용함
→ 홈 화면이 여러 개 있는 것이 아니라 하나만 만들어지는 것으로 하고 싶을 때는 해당 플래그를 사용하여 항상 하나의 객체가 메모리에 존재하게 하면서 그 상위의 액티비티를 모두 종료시킬 수 있음
부가 데이터(Extra Data)
액티비티를 띄울 때 전달되는 인텐트 안에 부가 데이터(Extra Data)를 넣어 전달하는 방법 권장
☝ 클래스 변수(static 변수)를 만들어 두 개의 화면에서 해당 변수를 참조하는 쉬운 방법도 있지만, 다른 앱에서도 참조할 수 있기 때문에 변수를 공유하는 방식은 불가능한 경우도 있음
번들(Bundle) 객체
인텐트 안에는 번들 객체가 들어있음
- 해시 테이블과 유사해서 putExtra()와 getOOOExtra() 메서드로 데이터를 넣거나 빼낼 수 있음 (OOO은 기본 자료형 이름)
- 기본 자료형(Primitive Data Type)도 넣고 뺄 수 있지만, 바이트 배열이나 Serializable 객체도 가능함
부가 데이터(Extra Data)
이 번들 객체 안에 들어있는 데이터가 부가 데이터!
- 시스템에서는 건들이지 않고 다른 앱 구성요소로 전달함
- 번들 안에 문자열이나 정수와 같은 부가 데이터를 넣을 때는 키(key)와 값(value)을 쌍으로 만들어 넣음
대표적인 메서드는
- putExtra()
- Intent putExtra(String name, String value)
- Intent putExtra(String name, int value)
- Intent putExtra(String name, boolean value)
- getOOOExtra()
- String getStringExtra(String name)
- int getIntString Extra(String name, int defaultValue)
- boolean getBooleanExtra(String name, boolean defaultValue)
- 데이터 값이 없으면 디폴트로 설정한 값이 반환됨 (defaultValue)
❓ 인텐트로 객체 전달은 어떻게 하는 것일까?
객체(Object) 자료형은 객체 자체를 전달할 수 없으며, 바이트 배열로 변환 or Serializable 인터페이스를 구현한 객체를 만들어 직렬화한 다음에 전달
🙆♀️ 안드로이드는 Serializable 인터페이스와 유사한 Parcelable 인터페이스를 권장함
→ Serializable과 유사하지만 직렬화했을 때 크기가 작아 안드로이드 내부의 데이터 전달에 자주 사용됨
→ 객체를 직접 번들에 추가하여 데이터를 전송할 때 사용할 수도 있음
- public abstract int describeContents() : 직렬화하려는 객체의 유형 구분
- public abstract void writeToParcel(Parcel dest, int flags) : 객체가 가지고 있는 데이터를 Parcel 객체로 만듦
- CREATOR : Parcel 객체로부터 데이터를 읽어 들여 객체를 생성하는 역할을 하는 상수(staic final)
👍 객체를 정의해 코드를 전달할 수 있으므로 코드가 단순해지고, 재사용성이 높아짐
👎 데이터를 담아둘 새로운 객체를 일일이 정의해야 한다는 번거로움이 있음
[예시]
SimpleData.java
import android.os.Parcel;
import android.os.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() {
@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 인터페이스를 상속받음
- SimpleData 생성자는 Parcel 객체를 파라미터로 받는데, readInt()와 readString() 메서드를 통해 데이터를 읽음
- wirteToParcel() 메서드 : SimpleData 객체 안에 들어있는 데이터를 Parcel 객체로 만드는 역할을 함
- CREATOR 객체는 상수로 정의되어 있고, 새로운 객체가 만들어지는 코드가 들어감
- new SimpleData()로 객체를 생성함
→ Parcel 객체의 데이터를 읽는 부분과 Parcel 객체로 쓰는 부분을 정의함
SendActivity.java
public class SendActivity extends AppCompatActivity {
public static final String KEY_SIMPLE_DATA = "data";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send);
Button button = findViewById(R.id.send_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent(getApplicationContext(), ReceiveActivity.class);
SimpleData data = new SimpleData(100, "Hello Android");
intent.putExtra(KEY_SIMPLE_DATA, data);
startActivity(intent);
}
});
}
}
- 인텐트 객체의 putExtra() 메서드를 사용하여 SimpleData 객체를 부가 데이터로 추가함
ReceiveAcitivity.java
public class ReceiveActivity 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_receive);
textView = findViewById(R.id.receive_textview);
Button button = findViewById(R.id.back_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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);
if (intent != null) {
textView.setText("전달받은 데이터\nNumber : "+data.number+"\nMessage : "+data.message);
}
}
}
}
- 전달 받은 객체를 참조하기 위해 onCreate() 메서드 안에서 getIntent() 메서드를 호출함
- getIntent() 메서드를 호출하면 인텐트 객체가 반환되므로 이 객체 안의 번들 객체를 참조할 수 있음
- getExtras() 메서드를 호출하면 Bundle 자료형의 객체가 반환됨
- 번들 객체를 참조한 후 getOOO() 형태 or 인텐트 객체에 정의되어 있는 getOOOExtra() 형태로 사용 가능
- 위의 예시에서는 getParcelable() 메서드로 객체를 참조함 → 키(key)는 KEY_SIMPLE_DATA