Android LiveData의 자세한 설명 및 사용

1. 라이브데이터란?

LiveData관찰 가능한 데이터 스토리지 클래스입니다.

일반 관찰 가능 클래스와 달리 수명 주기를 인식하므로 , 또는 와 같은 다른 애플리케이션 구성 요소 의 수명 주기를 LiveData따릅니다 . 이 인식은 활성 수명 주기 상태에 있는 애플리케이션 구성 요소 관찰자만 업데이트되도록 합니다.ActivityFragmentServiceLiveData

관찰자(클래스로 표시됨 )는 Observer수명이 STARTED또는 RESUMED상태 인 경우 LiveData활성으로 간주됩니다 . LiveData활성 관찰자에게만 업데이트 알림이 전송됩니다. 개체를 관찰하도록 등록 LiveData된 비활성 관찰자는 변경 사항에 대한 알림을 받지 않습니다.

LifecycleOwner인터페이스를 구현하는 개체와 쌍을 이루는 관찰자를 등록할 수 있습니다 . 이 관계가 있으면 해당 Lifecycle개체의 상태가 변경될 DESTROYED때 관찰자를 제거할 수 있습니다 . 이것은 누수에 대한 걱정 없이 객체를 안전하게 관찰할 수 있기 때문에 Activity및 에 특히 유용합니다 (시스템은 객체의 수명이 소멸되는 즉시 구독을 취소합니다 ) .FragmentLiveDataActivityFragment

2. LiveData의 장점

사용하면 LiveData다음과 같은 이점이 있습니다.

  • 인터페이스가 데이터 상태를 준수하는지 확인

    LiveData관찰자 패턴을 따르십시오. ** 개체는 기본 데이터가 변경될 때 LiveData알림을 받습니다 Observer. Observer코드를 통합하여 이러한 개체의 인터페이스를 업데이트 할 수 있습니다 . 이렇게 하면 앱의 데이터가 변경될 때마다 UI를 업데이트할 필요가 없습니다. 관찰자가 자동으로 업데이트를 수행하기 때문입니다.

  • 메모리 누수 없음

    옵저버는 개체에 바인딩되며 Lifecycle관련 수명 주기가 소멸된 후 정리됩니다.

  • 활동이 중지될 때 충돌 없음

    옵저버의 수명이 비활성 상태이면(백 스택에서와 같이 ) 이벤트를 Activity수신하는 것은 좋지 않습니다.LiveData

  • 더 이상 수명 주기를 수동으로 처리할 필요가 없습니다.

    UI 구성 요소는 단순히 관련 데이터를 관찰하고 관찰을 중지하거나 다시 시작하지 않습니다. LiveData이러한 모든 작업은 관찰할 때 관련 수명 주기 상태 변경을 인식하므로 자동으로 관리됩니다.

  • 데이터는 항상 최신 상태입니다.

    수명 주기가 비활성화되면 다시 활성화될 때 최신 데이터를 수신합니다. 예를 들어 백그라운드에 있던 활동은 포그라운드로 돌아오자마자 최신 데이터를 받습니다.

  • 적절한 구성 변경

    장치 회전과 같은 구성 변경으로 인해 Activity또는 가 재생성되면 Fragment사용 가능한 최신 데이터를 즉시 수신합니다.

  • 리소스 공유

    싱글톤 패턴을 사용하여 객체를 확장하여 LiveData애플리케이션 간에 공유할 수 있도록 시스템 서비스를 캡슐화할 수 있습니다. LiveData개체는 시스템 서비스에 한 번 연결되고 해당 리소스가 필요한 관찰자는 LiveData개체를 관찰하기만 하면 됩니다.

3. LiveData 개체 사용

LiveData개체를 사용하려면 아래 단계를 따르세요 .

  1. LiveData특정 유형의 데이터를 저장하기 위해 인스턴스가 생성됩니다 . 이것은 일반적으로 ViewModel수업에서 수행됩니다.

  2. 개체에 저장된 데이터가 변경될 때 발생하는 상황을 제어 onChanged()하는 Observer​​메서드를 정의하는 개체를 만듭니다 . 일반적으로 Activity 또는 Fragment와 같은 인터페이스 컨트롤러에서 개체를 LiveData만듭니다 .Observer

  3. observe()방법을 사용하여 Observer객체를 LiveData객체에 부착합니다. observe()메서드는 LifecycleOwner개체를 사용합니다. 이렇게 하면 개체가 변경 내용을 알 수 있도록 개체를 Observer구독하게 됩니다. 일반적으로 Activity 또는 Fragment와 같은 인터페이스 컨트롤러에 개체를 LiveData연결합니다 .Observer

    참고observeForever(Observer) : 연결된 개체 없이 관찰자를 등록하는 방법을 사용할 수 있습니다 LifecycleOwner. 이 경우 관찰자는 항상 활성 상태인 것으로 간주되므로 항상 수정 사항에 대한 알림을 받습니다. removeObserver(Observer)메서드를 호출하여 이러한 관찰자를 제거 할 수 있습니다 .

개체에 저장된 값을 업데이트하면 LiveData등록된 모든 관찰자가 트리거됩니다(연결된 관찰자가 활성화되어 있는 한 LifecycleOwner).

LiveDataUI 컨트롤러 관찰자가 업데이트를 구독하도록 허용합니다. 개체 저장소의 데이터가 변경 되면 LiveData인터페이스가 자동으로 업데이트되어 응답합니다.

  • LiveData객체 생성

    LiveDataCollections와 같이 구현하는 개체를 포함하여 모든 데이터에 사용할 수 있는 래퍼입니다 List. LiveData개체는 일반적으로 ViewModel개체에 저장되고 다음 예제와 같이 getter 메서드를 통해 액세스됩니다.

    public class NameViewModel extends ViewModel {
          
          
    
    // Create a LiveData with a String
    private MutableLiveData<String> currentName;
    
        public MutableLiveData<String> getCurrentName() {
          
          
            if (currentName == null) {
          
          
                currentName = new MutableLiveData<String>();
            }
            return currentName;
        }
    
    // Rest of the ViewModel...
    }
    

    처음에는 LiveData개체의 데이터가 설정되지 않습니다.

참고 : 다음과 같은 이유로 인터페이스를 업데이트하는 데 사용되는 LiveData객체가 Activity 또는 Fragment가 아닌 객체에 저장되어 있는지 확인하십시오 . 너무 큰 Activity 및 Fragment를 피하십시오. ViewModel이제 이러한 UI 컨트롤러는 데이터 표시를 담당하지만 데이터 상태 저장은 담당하지 않습니다. LiveData특정 활동 또는 프래그먼트 인스턴스에서 인스턴스를 분리 하고 LiveData개체가 구성 변경을 유지하도록 합니다.

  • 관측 LiveData대상

    대부분의 경우 onCreate()구성 요소를 적용하는 방법은 LiveData다음과 같은 이유로 개체를 살펴보기에 적합한 위치입니다.

    • 시스템이 Activity 또는 Fragment 메서드에서 중복 호출을 만들지 않는지 확인합니다 onResume().
    • Activity 또는 Fragment가 활성화된 직후에 표시할 수 있는 데이터를 가지고 있는지 확인하십시오. 응용 프로그램 구성 요소가 상태가 되면 STARTED관찰 중인 개체에서 최신 값을 받습니다 LiveData. LiveData이는 감시할 개체가 설정된 경우에만 발생합니다.

    일반적으로 LiveData는 데이터가 변경될 때만 활성 관찰자에게만 업데이트를 보냅니다. 이 동작의 예외는 관찰자가 비활성에서 활성으로 변경될 때 업데이트도 수신한다는 것입니다. 또한 관찰자가 두 번째로 비활성에서 활성으로 변경되면 마지막으로 활성화된 이후 값이 변경된 경우에만 업데이트를 수신합니다.

    다음 샘플 코드는 LiveData객체 관찰을 시작하는 방법을 보여줍니다.

    public class NameActivity extends AppCompatActivity {
          
          
    
        private NameViewModel model;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
          
          
            super.onCreate(savedInstanceState);
    
            // Other code to setup the activity...
    
            // Get the ViewModel.
            model = new ViewModelProvider(this).get(NameViewModel.class);
    
            // Create the observer which updates the UI.
            final Observer<String> nameObserver = new Observer<String>() {
          
          
                @Override
                public void onChanged(@Nullable final String newName) {
          
          
                    // Update the UI, in this case, a TextView.
                    nameTextView.setText(newName);
                }
            };
    
            // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
            model.getCurrentName().observe(this, nameObserver);
        }
    }
    

    가 전달된 nameObserver매개변수와 함께 호출되면 observe()즉시 호출되어 에 저장된 최신 값을 onChanged()제공합니다 . 개체에 이미 에 설정된 값이 없으면 mCurrentName호출 되지 않습니다 .LiveDatamCurrentNameonChanged()

  • LiveData개체 업데이트

    LiveData에는 저장된 데이터를 업데이트하기 위해 공개적으로 사용할 수 있는 방법이 없습니다. MutableLiveData클래스는 개체 에 저장된 값을 수정해야 하는 경우 사용해야 하는 메서드를 setValue(T)노출 합니다. 일반적으로 에서 사용 되며 변경 불가능한 객체 관찰자에게 노출합니다 .postValue(T)LiveDataViewModelMutableLiveDataViewModelLiveData

    관찰자 관계를 설정한 후 LiveData사용자가 버튼을 누를 때 모든 관찰자가 트리거되도록 객체의 값을 업데이트할 수 있습니다(다음 예 참조).

    button.setOnClickListener(new OnClickListener() {
          
          
        @Override
        public void onClick(View v) {
          
          
            String anotherName = "John Doe";
            model.getCurrentName().setValue(anotherName);
        }
    });
    

이 예제에서 호출하면 setValue(T)관찰자가 해당 메서드를 값으로 John Doe호출하게 됩니다. 이 예는 버튼 누르기를 보여주지만 네트워크 요청에 대한 응답이나 데이터베이스 로드 완료 등 다양한 이유로 호출 하거나 업데이트 onChanged()할 수도 있습니다 . 모든 경우에 관찰자를 호출 하거나 트리거하고 인터페이스를 업데이트합니다.setValue()postValue()mNamesetValue()postValue()

참고 : 메인 스레드에서 객체를 setValue(T)업데이트하려면 메서드를 호출해야 합니다 . LiveData작업자 스레드에서 코드를 실행하는 경우 postValue(T)메서드를 LiveData대신 사용하여 객체를 업데이트할 수 있습니다.

  • LiveData방과 함께 사용

    Room 지속성 라이브러리는 LiveData개체를 반환하는 관찰 가능한 쿼리를 지원합니다. 관찰 가능한 쿼리는 데이터베이스 액세스 개체(DAO)의 일부입니다.

    데이터베이스가 업데이트되면 Room은 LiveData객체를 업데이트하는 데 필요한 모든 코드를 생성합니다. 생성된 코드는 필요할 때 백그라운드 스레드에서 비동기적으로 쿼리를 실행합니다. 이 패턴은 UI에 표시된 데이터를 데이터베이스에 저장된 데이터와 동기화 상태로 유지하는 데 도움이 됩니다. Room Persistence Library Guide 에서 Room 및 DAO에 대해 자세히 알아볼 수 있습니다 .

4. 애플리케이션 아키텍처의 LiveData

LiveData수명 주기 인식이 있으며 및 와 같은 엔터티의 수명 주기를 따릅니다 activity. fragment다음을 사용하여 LiveData수명 소유자와 수명이 다른 다른 객체(예: ViewModel객체) 간에 데이터를 전달할 수 있습니다. 의 주요 책임은 인터페이스 관련 데이터를 로드하고 관리하는 것이므로 개체를 유지하기 위한 대안 ViewModel으로 매우 적합합니다 . 에서 개체를 만든 다음 이러한 개체를 사용하여 UI 계층에 상태를 노출할 LiveData수 있습니다 .ViewModelLiveData

activity인스턴스의 목적은 상태를 유지하는 것이 아니라 데이터를 표시하는 것이기 때문에 인스턴스를 fragment지속해서는 안 됩니다 . LiveData또한 데이터를 유지하지 않고 활동과 프래그먼트를 지속적으로 만들어 단위 테스트 작성을 더 쉽게 만듭니다.

데이터 영역 클래스에서 개체를 사용하고 싶은 유혹이 있을 수 LiveData있지만 LiveData데이터의 비동기 스트림을 처리하는 데 적합하지 않습니다. LiveData변환을 사용하여 MediatorLiveData이를 달성할 수 있지만 이 접근 방식의 단점은 데이터 스트림을 결합하는 기능이 매우 제한적이고 모든 LiveData개체(변환으로 생성된 개체 포함)가 기본 스레드에서 관찰된다는 것입니다. 다음은 기본 스레드를 블록 에 Repository유지하는 방법을 보여주는 샘플 코드입니다 .LiveData

class UserRepository {
    
    

    // DON'T DO THIS! LiveData objects should not live in the repository.
    LiveData<List<User>> getUsers() {
    
    
        ...
    }

    LiveData<List<User>> getNewPremiumUsers() {
    
    
    return Transformations.map(getUsers(),
        // This is an expensive call being made on the main thread and may cause
        // noticeable jank in the UI!
        users -> users.stream()
            .filter(User::isPremium)
            .filter(user ->
                user.getTimeCreated() > dao.getLastSyncedTime())
            .collect(Collectors.toList()));
    }
}

앱의 다른 레이어에서 데이터 흐름을 사용해야 하는 경우 Kotlin Flow 사용을 고려한 다음 Kotlin Flow를 asLiveData()in 사용으로 변환하세요 . 이 Codelab 에서 Kotlin 과 함께 Kotlin을 사용하는 방법을 자세히 알아보세요 . Java로 빌드된 코드베이스의 경우 콜백 또는 .ViewModelLiveDataFlowLiveDataRxJava

5. LiveData 확장

관찰자는 수명이 STARTED또는 RESUMED상태 인 경우 활성으로 간주됩니다. LiveData다음 샘플 코드는 LiveData클래스를 확장하는 방법을 보여줍니다.

public class StockLiveData extends LiveData<BigDecimal> {
    
    
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
    
    
        @Override
        public void onPriceChanged(BigDecimal price) {
    
    
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
    
    
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
    
    
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
    
    
        stockManager.removeUpdates(listener);
    }
}

이 예제의 가격 수신기 구현에는 다음과 같은 중요한 메서드가 포함됩니다.

  • LiveData이 메서드는 개체에 활성 관찰자가 있을 때 호출됩니다 onActive(). 즉, 이 방법에서 주가 업데이트를 보기 시작해야 합니다.
  • 이 메서드는 개체에 활성 관찰자가 LiveData없을 때 호출됩니다 onInactive(). 듣고 있는 관찰자가 없기 때문에 StockManager서비스 연결을 유지할 이유가 없습니다.
  • setValue(T)LiveData메서드는 인스턴스 값을 업데이트 하고 활성 관찰자에게 변경 사항을 알립니다.

StockLiveData다음과 같이 클래스를 사용할 수 있습니다 .

public class MyFragment extends Fragment {
    
    
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    
    
        super.onViewCreated(view, savedInstanceState);
        LiveData<BigDecimal> myPriceListener = ...;
        myPriceListener.observe(getViewLifecycleOwner(), price -> {
    
    
            // Update the UI.
        });
    }
}

observe()메소드는 첫 번째 매개변수 로 Fragment연결된 보기를 전달합니다. LifecycleOwner이렇게 하면 이 관찰자가 소유자와 연결된 개체에 바인딩되어 있음을 나타냅니다 Lifecycle. 즉, 다음을 의미합니다.

  • 개체가 살아 있지 않으면 Lifecycle값이 변경되더라도 관찰자가 호출되지 않습니다.
  • Lifecycle관찰자는 개체가 소멸되면 자동으로 제거됩니다.

LiveData개체가 수명 주기를 인식한다는 사실은 이러한 개체를 여러 Activity, Fragment및 간에 공유할 수 있음을 의미합니다. Service예제를 간단하게 유지하기 위해 LiveData다음과 같이 클래스를 싱글톤으로 구현할 수 있습니다.

public class StockLiveData extends LiveData<BigDecimal> {
    
    
    private static StockLiveData sInstance;
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
    
    
        @Override
        public void onPriceChanged(BigDecimal price) {
    
    
            setValue(price);
        }
    };

    @MainThread
    public static StockLiveData get(String symbol) {
    
    
        if (sInstance == null) {
    
    
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }

    private StockLiveData(String symbol) {
    
    
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
    
    
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
    
    
        stockManager.removeUpdates(listener);
    }
}

다음과 같이 내부에서 사용할 수 있습니다 Fragment.

public class MyFragment extends Fragment {
    
    
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    
    
        super.onViewCreated(view, savedInstanceState);
        StockLiveData.get(symbol).observe(getViewLifecycleOwner(), price -> {
    
    
            // Update the UI.
        });
    }
}

다중 FragmentActivityObservable MyPriceListener인스턴스. 하나 이상의 시스템 서비스가 표시되고 활성화된 경우 에만 LiveData연결하십시오 .

6. LiveData 변환

개체를 관찰자에게 전달하기 전에 개체에 저장된 값을 변경 하거나 다른 인스턴스의 값을 기반으로 LiveData다른 인스턴스를 반환해야 할 수 있습니다 . 패키지는 이러한 상황에 대한 도우미 메서드를 포함하는 클래스를 제공합니다.LiveDataLifecycleTransformations

  • Transformations.map()
LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    
    
    user.name + " " + user.lastName
});
  • Transformations.switchMap()

    와 유사하게 객체 map()에 저장된 값에 함수를 적용 하고 압축을 풀고 결과를 다운스트림으로 발송합니다. LiveData에 전달된 함수는 다음 예제와 같이 객체를 switchMap()반환해야 합니다 .LiveData

private LiveData<User> getUser(String id) {
    
    
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

전환 방법을 사용하여 관찰자의 수명 동안 정보를 전달할 수 있습니다. 관찰자가 반환된 객체를 관찰하지 않는 한 LiveData변환은 평가되지 않습니다 . 전환이 느리게 계산되기 때문에 수명 주기 관련 동작은 추가적인 명시적 호출이나 종속성 없이 암시적으로 전달됩니다.

개체 내에 개체가 ViewModel필요하다고 생각되면 변환이 더 나은 솔루션이 될 수 있습니다. Lifecycle예를 들어 주소를 받아 해당 주소의 우편번호를 반환하는 인터페이스 구성 요소가 있다고 가정합니다. ViewModel다음 샘플 코드와 같이 이 구성 요소에 대해 간단하게 구현할 수 있습니다 .

class MyViewModel extends ViewModel {
    
    
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
    
    
       this.repository = repository;
    }

    private LiveData<String> getPostalCode(String address) {
    
    
       // DON'T DO THIS
       return repository.getPostCode(address);
    }
}

그런 다음 인터페이스 구성 요소는 호출될 때마다 이전 LiveData개체의 등록을 취소하고 새 인스턴스에 등록 해야 합니다 . 또한 UI 구성 요소가 다시 생성되면 이전 호출의 결과를 사용하는 대신 메서드에 대한 getPostalCode()다른 호출을 트리거합니다 .repository.getPostCode()

다음 예와 같이 주소 입력의 변환으로 우편번호 조회를 구현할 수도 있습니다.

class MyViewModel extends ViewModel {
    
    
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
    
    
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
    
    
      this.repository = repository
  }

  private void setInput(String address) {
    
    
      addressInput.setValue(address);
  }
}

이 경우 postalCode필드는 addressInput의 변환으로 정의됩니다. 앱에 필드와 연결된 활성 관찰자가 있는 한 postalCode필드의 값은 addressInput변경될 때마다 다시 계산되고 검색됩니다.

이 메커니즘을 통해 저수준 애플리케이션은 지연 방식으로 주문형으로 계산되는 객체를 생성할 수 있습니다 LiveData. ViewModel개체는 개체에 대한 참조를 쉽게 얻은 LiveData다음 이를 기반으로 변환 규칙을 정의할 수 있습니다.

  • 새 변환 만들기

앱에서 유용할 수 있는 12개의 서로 다른 특정 변환이 있지만 기본적으로 제공되지는 않습니다. MediatorLiveData고유한 변환을 구현하기 위해 다른 개체를 수신하고 해당 개체에서 내보낸 이벤트를 처리하는 클래스를 사용할 수 있습니다 LiveData. MediatorLiveData해당 상태를 원본 LiveData개체에 올바르게 전파합니다. 이 패턴에 대한 자세한 내용은 Transformations클래스의 참조 문서를 참조하세요.

7. 여러 LiveData 소스 병합

MediatorLiveDataLiveData여러 소스를 결합할 수 있는 하위 클래스 입니다 LiveData. LiveData개체에 대한 관찰자는 원본 소스 개체가 변경될 때마다 트리거됩니다 MediatorLiveData.

예를 들어 인터페이스에 로컬 데이터베이스 또는 네트워크에서 업데이트할 수 있는 개체가 있는 경우 개체에 다음 소스를 추가 LiveData할 수 있습니다 .MediatorLiveData

  • 데이터베이스에 저장된 데이터와 관련된 객체 LiveData.
  • 네트워크에서 액세스한 데이터와 관련된 개체입니다 LiveData.

활동은 MediatorLiveData객체를 관찰하는 것만으로 두 소스 모두에서 업데이트를 받을 수 있습니다.

Guess you like

Origin blog.csdn.net/klylove/article/details/122039737