Tổng quan về LiveData   Một phần của Android Jetpack.

LiveData là một lớp lưu giữ dữ liệu có thể quan sát. Không như một lớp có thể quan sát thông thường, LiveData có tính năng nhận biết được vòng đời, nghĩa là tính năng này tuân theo vòng đời của các thành phần khác trong ứng dụng, chẳng hạn như các hoạt động, mảnh hoặc dịch vụ. Khả năng nhận biết này đảm bảo rằng LiveData chỉ cập nhật những trình quan sát thành phần của ứng dụng ở trạng thái vòng đời đang hoạt động.

LiveData sẽ coi trạng thái của một trình quan sát được đại diện bởi lớp Observer là đang hoạt động nếu vòng đời của lớp đó ở vào trạng tháiSTARTED hoặc RESUMED. LiveData chỉ thông báo các thông tin cập nhật cho trình quan sát đang hoạt động. Các trình quan sát không hoạt động đã đăng ký quan sát đối tượng LiveData sẽ không được thông báo về các thay đổi.

Bạn có thể đăng ký cho một trình quan sát ghép nối với đối tượng sẽ triển khai giao diện LifecycleOwner. Mối quan hệ này cho phép xoá trình quan sát khi trạng thái của đối tượng Lifecycle tương ứng thay đổi thành DESTROYED. Điều này đặc biệt hữu ích cho các hoạt động và mảnh vì chúng có thể quan sát các đối tượng LiveData một cách an toàn và không phải lo lắng về nguy cơ rò rỉ—các hoạt động và mảnh sẽ bị hủy đăng ký ngay khi vòng đời của chúng bị hủy bỏ.

Để biết thêm thông tin về cách sử dụng LiveData, hãy xem phần Làm việc với các đối tượng LiveData.

Ưu điểm của việc sử dụng LiveData

Việc sử dụng LiveData mang lại những lợi ích sau:

Đảm bảo giao diện người dùng khớp với trạng thái dữ liệu
LiveData tuân theo mẫu của trình quan sát. LiveData thông báo cho các đối tượng Observer khi dữ liệu cơ sở thay đổi. Bạn có thể hợp nhất mã để cập nhật giao diện người dùng trong các đối tượng Observer này. Nhờ đó, bạn không cần phải cập nhật giao diện người dùng mỗi khi dữ liệu ứng dụng thay đổi, vì trình quan sát sẽ làm việc đó thay cho bạn.
Không rò rỉ bộ nhớ
Trình quan sát được liên kết với các đối tượng Lifecycle và tự xoá sau khi vòng đời liên kết với các đối tượng bị huỷ bỏ.
Không có sự cố do các hoạt động đã dừng
Nếu vòng đời của trình quan sát không hoạt động, chẳng hạn như trong một hoạt động ở ngăn xếp lui, thì trình quan sát sẽ không nhận được bất kỳ sự kiện LiveData nào.
Không còn phải xử lý vòng đời theo cách thủ công
Các thành phần giao diện người dùng chỉ quan sát các dữ liệu liên quan và không ngừng hoặc tiếp tục việc quan sát. LiveData tự động quản lý tất cả những việc này nhờ khả năng nhận biết những thay đổi trạng thái liên quan đến vòng đời trong khi quan sát.
Luôn luôn cập nhật dữ liệu
Vòng đời không hoạt động sẽ nhận được dữ liệu mới nhất sau khi được kích hoạt trở lại. Ví dụ, một hoạt động chạy dưới nền sẽ nhận được dữ liệu mới nhất ngay sau khi hoạt động đó chuyển về chay trên nền trước.
Thay đổi cấu hình phù hợp
Nếu một hoạt động hoặc mảnh được tạo lại do có sự thay đổi về cấu hình, chẳng hạn như xoay thiết bị, thì hoạt động hoặc mảnh đó sẽ ngay lập tức nhận được dữ liệu mới nhất hiện có.
Chia sẻ tài nguyên
Bạn có thể mở rộng một đối tượng LiveData bằng mẫu singleton để gói và chia sẻ các dịch vụ hệ thống trong ứng dụng. Đối tượng LiveData sẽ kết nối với dịch vụ hệ thống một lần, sau đó bất kỳ trình quan sát nào cần tài nguyên có thể chỉ cần xem đối tượng LiveData. Để biết thêm thông tin, hãy xem phần Mở rộng LiveData.

Làm việc với các đối tượng LiveData

Làm theo các bước sau để làm việc với các đối tượng LiveData:

  1. Tạo một thực thể của LiveData để lưu giữ một loại dữ liệu nhất định. Bước này thường được thực hiện trong lớp ViewModel.
  2. Tạo một đối tượng Observer để xác định phương thức onChanged(). Phương thức này sẽ kiểm soát hành động diễn ra khi đối tượng LiveData thay đổi dữ liệu đã lưu giữ. Bạn thường tạo một đối tượng Observer trong bộ điều khiển trên giao diện người dùng, chẳng hạn như một hoạt động hoặc mảnh.
  3. Gắn đối tượng Observer vào đối tượng LiveData bằng phương thức observe(). Phương thức observe() sẽ nhận đối tượng LifecycleOwner. Thao tác này sẽ đăng ký đối tượng Observer với đối tượng LiveData để được thông báo về các thay đổi. Bạn thường đính kèm đối tượng Observer trong bộ điều khiển giao diện người dùng, chẳng hạn như hoạt động hoặc mảnh.

Khi cập nhật giá trị được lưu trữ trong đối tượng LiveData, thao tác này sẽ kích hoạt tất cả các trình quan sát đã đăng ký, miễn là LifecycleOwner đính kèm vẫn ở trạng thái đang hoạt động.

LiveData cho phép trình quan sát giao diện người dùng đăng ký nhận nội dung cập nhật. Khi dữ liệu do đối tượng LiveData lưu giữ thay đổi, giao diện người dùng sẽ tự động cập nhật để phản hồi.

Tạo đối tượng LiveData

LiveData là một trình bao bọc có thể được sử dụng với bất kỳ dữ liệu nào, bao gồm cả các đối tượng triển khai Collections, chẳng hạn như List. Một đối tượng LiveData thường được lưu trữ trong một đối tượng ViewModel và được truy cập thông qua một phương thức getter, như đươc minh hoạ trong ví dụ sau:

Kotlin

class NameViewModel : ViewModel() {

    // Create a LiveData with a String
    val currentName: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

    // Rest of the ViewModel...
}

Java

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...
}

Ban đầu, dữ liệu trong đối tượng LiveData chưa được đặt.

Bạn có thể đọc thêm về các lợi ích và cách sử dụng lớp ViewModel trong hướng dẫn về ViewModel.

Quan sát các đối tượng LiveData

Trong hầu hết trường hợp, phương thức onCreate() của thành phần ứng dụng là nơi phù hợp để bắt đầu quan sát một đối tượng LiveData vì các lý do sau:

  • Để đảm bảo hệ thống không thực hiện các lệnh gọi thừa qua phương thức onResume() của một hoạt động hoặc mảnh.
  • Để đảm bảo hoạt động hoặc mảnh có dữ liệu để có thể hiển thị ngay khi bắt đầu hoạt động. Ngay khi một thành phần ứng dụng vào trạng thái STARTED, thành phần đó sẽ nhận được giá trị gần đây nhất từ các đối tượng LiveData mà thành phần đó đang quan sát. Điều này chỉ xảy ra nếu đối tượng LiveData cần quan sát đã được thiết lập.

Nhìn chung, LiveData chỉ phân phối thông tin cập nhật đến các trình quan sát đang hoạt động và chỉ khi dữ liệu có sự thay đổi. Hành vi này có một ngoại lệ là trình quan sát cũng sẽ nhận được thông tin cập nhật khi thay đổi trạng thái từ không hoạt động sang đang hoạt động. Hơn nữa, nếu trình quan sát thay đổi trạng thái từ không hoạt động sang hoạt động lần thứ hai, thì trình quan sát sẽ chỉ nhận được nội dung cập nhật nếu giá trị đã thay đổi kể từ lần hoạt động gần đây nhất.

Mã mẫu sau đây minh hoạ cách bắt đầu quan sát một đối tượng LiveData:

Kotlin

class NameActivity : AppCompatActivity() {

    // Use the 'by viewModels()' Kotlin property delegate
    // from the activity-ktx artifact
    private val model: NameViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Other code to setup the activity...

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView.
            nameTextView.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentName.observe(this, nameObserver)
    }
}

Java

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);
    }
}

Sau khi observe() được gọi và có nameObserver được chuyển dưới dạng thông số, onChanged() sẽ được gọi ngay lập để cung cấp tức giá trị gần đây nhất được lưu trữ trong mCurrentName. Nếu đối tượng LiveData không đặt giá trị trong mCurrentName, thì onChanged() sẽ không được gọi.

Cập nhật đối tượng DataData

LiveData không có phương pháp công khai để cập nhật dữ liệu đã lưu trữ. Lớp MutableLiveData sẽ hiển thị công khai phương thức setValue(T)postValue(T). Bạn phải sử các phương thức này nếu cần chỉnh sửa giá trị được lưu trữ trong đối tượng LiveData. Thông thường, MutableLiveData được sử dụng trong ViewModel và sau đó ViewModel chỉ hiển thị các đối tượng LiveData không thể thay đổi cho trình quan sát.

Sau khi thiết lập mối quan hệ với trình quan sát, bạn có thể cập nhật giá trị của đối tượng LiveData (như được minh họa trong ví dụ sau) để kích hoạt tất cả trình quan sát khi người dùng nhấn vào một nút:

Kotlin

button.setOnClickListener {
    val anotherName = "John Doe"
    model.currentName.setValue(anotherName)
}

Java

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

Trong ví dụ, việc gọi setValue(T) sẽ khiến trình quan sát gọi phương thức onChanged() với giá trị John Doe. Ví dụ minh hoạ một thao tác nhấn nút, nhưng setValue() hoặcpostValue() có thể được gọi để cập nhật mName vì nhiều lý do, như để phản hồi một yêu cầu mạng hoặc việc hoàn tất tải cơ sở dữ liệu. Trong mọi trường hợp, lệnh gọi đếnsetValue() hoặcpostValue() sẽ kích hoạt trình quan sát và cập nhật giao diện người dùng.

Sử dụng DataData với Room

Thư viện lưu trữ Room hỗ trợ các truy vấn có thể quan sát và trả về các đối tượng LiveData. Các truy vấn có thể quan sát được viết như một phần của một Đối tượng truy cập cơ sở dữ liệu (DAO).

Room tạo ra tất cả mã cần thiết để cập nhật đối tượng LiveData khi cập nhật một cơ sở dữ liệu. Mã được tạo này sẽ chạy truy vấn không đồng bộ trên một luồng chạy nền (background thread) khi cần. Mẫu này rất hữu ích trong việc giữ cho dữ liệu hiển thị trên giao diện người dùng đồng bộ hoá với dữ liệu được lưu trữ trong cơ sở dữ liệu. Bạn có thể đọc thêm về Room và DAO trong phần Hướng dẫn về thư viện lưu trữ Room.

Sử dụng coroutine với LiveData

LiveData có hỗ trợ cho coroutine Kotlin. Để biết thêm thông tin, hãy xem phần Sử dụng coroutine Kotlin với Bộ thành phần cấu trúc Android.

LiveData trong cấu trúc ứng dụng

LiveData có thể nhận biết và tuân theo vòng đời của các thực thể như hoạt động và mảnh. Hãy sử dụng LiveData để giao tiếp giữa các chủ sở hữu vòng đời này và các đối tượng có vòng đời khác, chẳng hạn như đối tượng ViewModel. Trách nhiệm chính của ViewModel là tải và quản lý dữ liệu liên quan đến giao diện người dùng, khiến lớp này trở thành một ứng viên tiềm năng để lưu giữ các đối tượng LiveData. Tạo các đối tượng LiveData trong ViewModel và sử dụng các đối tượng đó để hiển thị trạng thái cho lớp giao diện người dùng.

Các hoạt động và mảnh không được lưu giữ các bản sao LiveData vì vai trò của chúng là hiển thị dữ liệu chứ không phải trạng thái. Ngoài ra, việc không để cho hoạt động và mảnh lưu giữ dữ liệu cũng giúp bạn viết mã kiểm thử đơn vị dễ dàng hơn.

Bạn có thể muốn để các đối tượng LiveData hoat động trong lớp (class) của lớp (layer) dữ liệu, nhưng LiveData không được thiết kế để xử lý các luồng dữ liệu (streams of data) theo cách không đồng bộ. Mặc dù bạn có thể sử dụng phép biến đổi LiveDataMediatorLiveData để làm được điều này, nhưng phương pháp này có một số trở ngại: khả năng kết hợp các luồng dữ liệu (streams of data) rất hạn chế và tất cả đối tượng LiveData (bao gồm cả các đối tượng được tạo bằng các phép biến đổi) sẽ được quan sát trên luồng chính (main thread). Mã dưới đây là một ví dụ cho thấy việc lưu giữ một LiveData trong Repository có thể chặn mất luồng chính (main thread):

Kotlin

class UserRepository {

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

    fun getNewPremiumUsers(): LiveData<List<User>> {
        return getUsers().map { users ->
            // This is an expensive call being made on the main thread and may
            // cause noticeable jank in the UI!
            users
                .filter { user ->
                  user.isPremium
                }
          .filter { user ->
              val lastSyncedTime = dao.getLastSyncedTime()
              user.timeCreated > lastSyncedTime
                }
    }
}

Java

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()));
    }
}

Nếu bạn cần sử dụng các luồng dữ liệu (streams of data) trong các lớp khác của ứng dụng, hãy sử dụng Luồng Kotlin, sau đó chuyển đổi chúng sang LiveData trong ViewModel bằng asLiveData(). Hãy tìm hiểu thêm về cách sử dụng Flow Kotlin với LiveData trong lớp học lập trình này. Đối với các cơ sở mã được tạo bằng Java, hãy cân nhắc sử dụng Trình thực thi cùng với lệnh gọi lại hoặc RxJava.

Mở rộng LiveData

LiveData sẽ coi trạng thái của trình quan sát là đang hoạt động nếu vòng đời của trình quan sát đó ở trạng thái STARTED hoặc RESUMED. Mã mẫu sau đây minh hoạ cách mở rộng lớp LiveData:

Kotlin

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }
}

Java

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);
    }
}

Việc triển khai trình nghe giá trong ví dụ này gồm có các phương thức quan trọng sau:

  • Phương thức onActive() được gọi khi đối tượng LiveData có một trình quan sát đang hoạt động. Điều này nghĩa là bạn cần bắt đầu quan sát các thông tin cập nhật về giá cổ phiếu từ phương pháp này.
  • Phương thức onInactive() sẽ được gọi khi đối tượng LiveData không có người quan sát nào đang hoạt động. Vì không có người quan sát nào đang nghe nên không cần phải tiếp tục kết nối với dịch vụ StockManager.
  • Phương thức setValue(T) sẽ cập nhật giá trị của thực thể LiveData và thông báo cho mọi trình quan sát đang hoạt động về thay đổi đó.

Bạn có thể sử dụng lớp StockLiveData như sau:

Kotlin

public class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val myPriceListener: LiveData<BigDecimal> = ...
        myPriceListener.observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })
    }
}

Java

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.
        });
    }
}

Phương thức observe() sẽ chuyển LifecycleOwner liên kết với chế độ xem của mảnh dưới dạng đối số đầu tiên. Điều đó cho thấy trình quan sát này phụ thuộc vào đối tượng Lifecycle liên kết với chủ sở hữu, nghĩa là:

  • Nếu đối tượng Lifecycle không ở trạng thái đang hoạt động, thì trình quan sát sẽ không được gọi ngay cả khi giá trị thay đổi.
  • Sau khi đối tượng Lifecycle bị huỷ, trình quan sát sẽ tự động bị xoá.

Thực tế là các đối tượng LiveData có thể nhận biết được vòng đời, nghĩa là bạn có thể chia sẻ các đối tượng đó giữa nhiều hoạt động, mảnh và dịch vụ. Để đơn giản hoá ví dụ này, bạn có thể triển khai lớp LiveData dưới dạng singleton như sau:

Kotlin

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager: StockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }

    companion object {
        private lateinit var sInstance: StockLiveData

        @MainThread
        fun get(symbol: String): StockLiveData {
            sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol)
            return sInstance
        }
    }
}

Java

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);
    }
}

Và bạn có thể sử dụng đoạn mã này trong mảnh như sau:

Kotlin

class MyFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        StockLiveData.get(symbol).observe(viewLifecycleOwner, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })

    }

Java

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.
        });
    }
}

Nhiều mảnh và hoạt động có thể quan sát thực thể MyPriceListener. LiveData chỉ kết nối với dịch vụ hệ thống nếu một hoặc nhiều dịch vụ trong số đó hiển thị và đang hoạt động.

Biến đổi LiveData

Bạn nên thay đổi giá trị lưu trữ trong đối tượng LiveData trước khi gửi đối tượng này cho trình quan sát, hoặc có thể bạn sẽ cần trả về một thực thể LiveData khác dựa trên giá trị của một thực thể khác. Gói Lifecycle cung cấp lớp Transformations, bao gồm các phương thức trợ giúp để hỗ trợ cho các trường hợp này.

Transformations.map()
Áp dụng một hàm trên giá trị được lưu trữ trong đối tượng LiveData và áp dụng kết quả xuôi dòng.

Kotlin

val userLiveData: LiveData<User> = UserLiveData()
val userName: LiveData<String> = userLiveData.map {
    user -> "${user.name} ${user.lastName}"
}

Java

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});
Transformations.switchMap()
Tương tự như map(), áp dụng một hàm cho giá trị được lưu trữ trong đối tượng LiveData, mở gói và gửi kết quả đi xuôi chiều. Hàm được chuyển đến switchMap() phải trả về một đối tượng LiveData, như được minh hoạ trong ví dụ sau:

Kotlin

private fun getUser(id: String): LiveData<User> {
  ...
}
val userId: LiveData<String> = ...
val user = userId.switchMap { id -> getUser(id) }

Java

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

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

Bạn có thể sử dụng các phép biến đổi để truyền thông tin trong suốt vòng đời của trình quan sát. Các phép biến đổi sẽ không được tính trừ khi có một trình quan sát đang xem đối tượng LiveData được trả về. Do các phép biến đổi được tính toán từng phần, nên hoạt động liên quan đến vòng đời sẽ ngầm được chuyển xuống mà không đòi hỏi các lệnh gọi hoặc phần phụ thuộc rõ ràng khác.

Nếu cho rằng bạn cần có một đối tượng Lifecycle bên trong đối tượng ViewModel, phép biến đổi có thể là một giải pháp tốt hơn. Ví dụ, giả sử bạn có một thành phần giao diện người dùng chấp nhận một địa chỉ và trả về mã bưu chính cho địa chỉ đó. Bạn có thể triển khai hàm ViewModel đơn thuần cho thành phần này như được minh hoạ bằng mã mẫu sau:

Kotlin

class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {

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

Java

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);
    }
}

Sau đó, thành phần giao diện người dùng cần huỷ đăng ký khỏi đối tượng LiveData trước đó và đăng ký bản sao mới mỗi khi thành phần này gọi getPostalCode(). Ngoài ra, nếu thành phần giao diện người dùng này được tạo lại, thì thành phần này sẽ kích hoạt một lệnh gọi khác đến phương thức repository.getPostCode() thay vì sử dụng kết quả của lệnh gọi trước đó.

Thay vào đó, bạn có thể triển khai quá trình tra cứu mã bưu chính dưới dạng một lượt chuyển đổi địa chỉ nhập vào, như trong ví dụ sau:

Kotlin

class MyViewModel(private val repository: PostalCodeRepository) : ViewModel() {
    private val addressInput = MutableLiveData<String>()
    val postalCode: LiveData<String> = addressInput.switchMap {
            address -> repository.getPostCode(address) }


    private fun setInput(address: String) {
        addressInput.value = address
    }
}

Java

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);
  }
}

Trong trường hợp này, trường postalCode được xem là sự biến đổi của addressInput. Miễn là ứng dụng của bạn có một trình quan sát đang hoạt động liên kết với trường postalCode, giá trị của trường đó sẽ được tính toán lại và truy xuất bất cứ khi nào addressInput thay đổi.

Cơ chế này cho phép các cấp thấp hơn của ứng dụng tạo các đối tượng LiveData được tính toán từng phần theo yêu cầu. Đối tượng ViewModel có thể dễ dàng lấy thông tin tham chiếu đến các đối tượng LiveData và sau đó xác định các quy tắc biến đổi ở trên các đối tượng đó.

Tạo các phép biến đổi mới

Có nhiều phép biển đổi cụ thể khác nhau có thể hữu ích trong ứng dụng của bạn, nhưng chúng không được cung cấp theo mặc định. Để triển khai phép biến đổi của mình, bạn có thể sử dụng lớp MediatorLiveData để nghe các đối tượng LiveData khác và xử lý các sự kiện do các đối tượng đó tạo ra. MediatorLiveData truyền chính xác trạng thái của lớp con này đến đối tượng LiveData nguồn. Để tìm hiểu thêm về mẫu này, hãy xem tài liệu tham khảo của lớp Transformations.

Hợp nhất nhiều nguồn LiveData

MediatorLiveData là một lớp con của LiveData cho phép bạn hợp nhất nhiều nguồn LiveData. Sau đó, trình quan sát các đối tượng MediatorLiveData sẽ được kích hoạt bất cứ khi nào có một đối tượng nguồn LiveData nguyên gốc thay đổi.

Ví dụ, nếu có một đối tượng LiveData trong giao diện người dùng có thể được cập nhật từ cơ sở dữ liệu cục bộ hoặc mạng, thì bạn có thể thêm các nguồn sau vào đối tượng MediatorLiveData:

  • Đối tượng LiveData liên kết với dữ liệu được lưu trữ trong cơ sở dữ liệu.
  • Đối tượng LiveData liên kết với dữ liệu truy cập từ mạng.

Chỉ cần quan sát đối tượng MediatorLiveData để hoạt động của bạn nhận được thông tin cập nhật từ cả hai nguồn. Để biết ví dụ chi tiết, hãy xem Phụ lục: hiển thị trạng thái mạng trong mục Hướng dẫn về cấu trúc ứng dụng.

Tài nguyên khác

Để tìm hiểu thêm về lớp LiveData, hãy tham khảo các tài nguyên sau.

Mẫu

  • Sunflower, một ứng dụng minh hoạ giới thiệu các phương pháp tốt nhất cho Thành phần cấu trúc

Lớp học lập trình

Blog

Video