LiveData overview, kotlin interview questions

Keywords: Android Database Design Pattern Interview kotlin

Typically, LiveData provides updates only when the data changes and only when the observer is active.

An exception to this behavior is that observers also receive updates when they change from inactive to active.

In addition, if the observer changes from inactive to active for the second time, it will receive an update only if the value has changed since the last active state.

The following example code illustrates how to start observing the LiveData object:

public class NameActivity extends AppCompatActivity {
 
    private NameViewModel mModel;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        // Other code to setup the activity...
 
        // Get the ViewModel.
        mModel = ViewModelProviders.of(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.
                mNameTextView.setText(newName);
            }
        };
 
        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        mModel.getCurrentName().observe(this, nameObserver);
    }
}

After observe() calls nameObserverafter as a parameter, onChanged() calls immediately to provide the latest stored value mCurrentName. onChanged() is not called if the LiveData object does not have the value mCurrentName set.

2.3 updating LiveData objects

LiveData has no public method to update the stored data. This MutableLiveData class exposes setValue(T) and

The postValue(T) method is exposed. If you need to edit a value stored in, you must use these LiveData objects.

MutableLiveData is usually used for ViewModel, and then the unique immutable LiveData object of ViewModel is exposed to the observer.

After setting the observer relationship, you can update the value of the LiveData object, as shown in the following example. When the user clicks the button, all observers are triggered:

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

Calling the setValue(T) example causes observation onChanged() to call its method John Doe with a value. The example shows that under the button, but setValue() or postValue() can be called to update mName for a variety of reasons, including completion in response to network request or database load; In all cases, calling setValue (or postvalue () triggers the observer and updates the UI.

Note: you must call the setValue(T) method to update the object from the main thread with LiveData. If the code is in the worker thread, you can use the postValue(T) method to update the LiveData object.

2.3 using LiveData with rooms

The office's persistence library supports observable queries, this time the LiveData object. Observable queries are written as part of a database access object (DAO).

When updating the LiveData database, Room generates all the code required to update the object. The generated code runs queries asynchronously on the background thread when needed. This pattern is useful for keeping the data displayed in the UI synchronized with the data stored in the database. You can read more about Room and DAO in the Room persistence Library Guide.

###III. extend LiveData

If the observer's lifecycle is in the STARTED or RESUMED state, LiveData treats the observer as active. The following example code shows how to extend the LiveData class:

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager mStockManager;
 
    private SimplePriceListener mListener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };
 
    public StockLiveData(String symbol) {
        mStockManager = new StockManager(symbol);
    }
 
    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }
 
    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

The implementation of the price listener in this example includes the following important methods:

1.onActive() call this method when the LiveData object has an active observer. This means that you need to start observing stock price updates from this method.
2.onInactive() call this method when the LiveData object does not have any active observers. Since no observers are listening, there is no reason to remain connected to the StockManager service.
This setValue(T) method updates the value of the LiveData instance and notifies any active observer of the change.

You can use this class with StockLiveData as follows:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LiveData<BigDecimal> myPriceListener = ...;
        myPriceListener.observe(this, price -> {
            // Update the UI.
        });
    }
}

The observe() method passes the fragment, which is an instance of Lifecycle owner, as the first parameter. Doing so means that this observer is bound to the object associated with the owner of Lifecycle, which means:

If the Lifecycle object is not active, the observer is not invoked even if the value changes.

After that, the Lifecycle object is destroyed and the observer is automatically deleted.

The fact that LiveData objects are lifecycle aware means that you can share them among multiple activities, fragments, and services. To make the example simple, you can implement the LiveData class as a singleton, as follows:

public class StockLiveData extends LiveData<BigDecimal> {
    private static StockLiveData sInstance;
    private StockManager mStockManager;
 
    private SimplePriceListener mListener = 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) {
        mStockManager = new StockManager(symbol);
    }
 
    @Override
    protected void onActive() {
        mStockManager.requestPriceUpdates(mListener);
    }
 
    @Override
    protected void onInactive() {
        mStockManager.removeUpdates(mListener);
    }
}

You can use it in clips as follows:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        StockLiveData.get(symbol).observe(this, price -> {
            // Update the UI.
        });
    }
}

Multiple clips and activities can observe MyPriceListener instances. LiveData connects to system services only when one or more of the system services are visible and active.

###Quad transform LiveData

You may want to change the values stored in the LiveData object before distributing it to observers, or you may need to

LiveData returns another instance based on the value of another instance. The Lifecycle package provides Transformations classes that contain helper methods that support these scenarios.

Apply the function to the value stored in the LiveData object and propagate the result downstream.

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

Transformations.switchMap()

Similar to map(), apply the function to the value stored in the LiveData object, unpack and schedule the results downstream. The function switchMap() passed to must return a LiveData object, as shown in the following example

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

You can use transformation methods to pass information through the observer's lifecycle. Unless the observer is observing the returned LiveData

Object, otherwise the transform is not calculated. Because transformations are computed lazily, lifecycle related behaviors are passed implicitly without additional explicit calls or dependencies.

If you think the ViewModel object is needed inside the Lifecycle object, conversion may be a better solution. For example, suppose you have a UI component that accepts an address and returns the zip code of that address. You can use ViewModel to implement naive for this component, as shown in the following example code:

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

Then, the UI component needs to unregister from the previous LiveData object and register with the new getPostalCode() on each call. In addition, if you re create the UI component, it will trigger another call to the repository.getPostCode() method instead of using the result previously called.

Instead, you can implement a zip code lookup as a translation of address input, as shown in the following example:

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

In this case, the postalCode fields are public and final because the fields will never change. This postalCode

The field is defined to transform addressInput, which means that repository.getPostCode() changes in addressInput

This method is called when. If there is an active observer, if there is no active observer repository.getPostCode()

If called, this is true and no calculation is performed before adding an observer.

This mechanism allows lower level applications to create objects for LiveData to defer calculation as needed. A ViewModel object can be easily obtained to reference LiveData objects, and then define the transformation rules on top of them.

4.1 create a new transformation

There are a dozen different specific transformations in your application that may be useful, but they are not provided by default.

To implement your own transformation, you can use the MediatorLiveData class, which listens for other LiveData

Objects and handle the events they emit. MediatorLiveData correctly propagates its state to the source LiveData

Object. To learn more about this pattern, see the reference documentation for the Transformations class.

###V. merge multiple LiveData sources

Mediatorlivedata is a subclass of LiveData that allows you to merge multiple LiveData sources. MediatorLiveData

Whenever any original LiveData source object changes, the observer of the object is triggered.

For example, if there is an object in the LiveDataUI that can be updated from a local database or network, you can add the following sources to the MediatorLiveData object:

1.LiveData is the object associated with the data stored in the database.
2.LiveData is an object associated with data accessed from the network.
Your activity only needs to observe the MediatorLiveData object to receive updates from two sources

Handwritten event communication scheme LiveDataBus

https://pan.baidu.com/s/1S4Xjdw_cwYNsPL4SbP6tbA

Posted by Anim9or on Fri, 03 Sep 2021 20:20:09 -0700