ANDROID/Android 앱 프로그래밍

[Android] 플래그(flag)와 부가 데이터(Extra Data)

주 녕 2021. 5. 24. 20:53
반응형

모든 내용은 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)

Parcelable 인터페이스의 필수 메서드

👍 객체를 정의해 코드를 전달할 수 있으므로 코드가 단순해지고, 재사용성이 높아짐

👎 데이터를 담아둘 새로운 객체를 일일이 정의해야 한다는 번거로움이 있음

 

[예시]

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

 

 

 

반응형