Intent Service

A Service which runs off a completely separate thread to the main. All IntentService requests are handled on a single background thread and are issued in order. Therefore IntentServices are good for tasks that need to happen in order.

Services must be registered in the AndroidManifest.xml:

        <service
            android:name=".sync.myIntentService"
            android:exported="false"
            ></service>

An Intent Service can be started in a very similar way to an Activity:

Intent myIntent = new Intent(this, myIntentService.class);
startService(myIntent);

Extra data can be attached to the Intent when starting the Service, as with Activities:

Intent myIntent = new Intent(this, myIntentService.class);
myIntent.setAction("Some specific action");
startService(myIntent);

To create the Service, extend IntentService. Override the onHandle Intent method to tell it what to do in the background:

public class MyIntentService extends IntentService {

    @Override
    protected void onHandleIntent(Intent intent) {
        String action = intent.getAction(); //Add this line if extra data attached
        //Do background work here
    }
}

The IntentService will then stop itself when it is finished.

ud851-Exercises-student\Lesson10-Hydration-Reminder\T10.01

ViewModelFactory

ViewModelFactory allows us to produce custom ViewModels that allow for the inclusion of arguments in the Constructor (standard ViewModels do not).

To implement a ViewModelFactory, first create a ViewModelFactory class (this example corresponds to the code in the @Entity, @DAO, Build a Database and LiveData posts):

public class AddContactViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private final AppDatabase mDb;
    private final int mContactId;

    public AddTaskViewModelFactory(AppDatabase mDb, int mContactId) { // Arguments to be passed in
        this.mDb = mDb;
        this.mContactId = mContactId;
    }


    // Note: This can be reused with minor modifications
    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) { // When ViewModelFactory is created it returns a new ViewModel with the arguments fed in
        //noinspection unchecked
        return (T) new AddContactViewModel(mDb, mContactId);
    }
}

Then create the class declared in the ViewModelFactory's create() override:

public class AddContactViewModel extends ViewModel {

    private LiveData<ContactEntry> contactEntry;

    public AddTaskViewModel(AppDatabase database, int contactId) { // Constructor which takes the arguments
        contactEntry = database.contactDao().loadContactById(contactId); // Code which retrieves the LiveData object through the @DAO
    }

    public LiveData<ContactEntry> getContactEntry() { // Method of delivering the data to the Activity
        return contactEntry;
    }
}

In the Activity:

AddContactViewModelFactory addContactViewModelFactory = new AddContactViewModelFactory(mDb, mContactId); // Feed in arguments to the ViewModelFactory
final  AddContactViewModel viewModel = ViewModelProviders.of(this, addContactViewModelFactory).get(AddContactViewModel.class); // Create the View Model, including the ViewModelFactory in the arguments
                viewModel.getContactEntry().observe(this, new Observer<ContactEntry>() { // Observe the LiveData object which is cached in the ViewModel
    @Override
    public void onChanged(@Nullable ContactEntry contactEntry) {
        viewModel.getContactEntry().removeObserver(this); // Not quite sure why we've removed the Observer here
        populateUI(contactEntry); // Apply changes to UI
    }
});

ViewModel

ViewModel preserves data beyond the lifecycle of individual Activities i.e. it is lifecycle-aware – it will hold data for the UI even if e.g. the screen is rotated, causing the Activity to be remade. It persists from onCreate() until onCleared() (after finish() is applied to the Activity), thus helping to avoid re-querying a data source repeatedly upon Activity changes. This goes beyond the savedInstanceState which is limited to small amounts of data which can be easily serialised and de-serialised.

It also helps to avoid zombie threads (memory leaks), which were begun but not terminated as the Activity which created them was destroyed before they completed. When the Activity or Fragment is finished any Observers are closed, so no orphaned processes are left running.

Works in collaboration with LiveData. For more information see here. If your ViewModel needs access to the application context then extend AndroidViewModel rather than ViewModel (as below).

Caution: A ViewModel must never reference a view, Lifecycle, or any class that may hold a reference to the activity context.

To add the dependencies for ViewModel, see here.

To implement a ViewModel, first create a ViewModel class(this example corresponds to the code in the @Entity, @DAO, Build a Database and LiveData posts):

public class MainViewModel extends AndroidViewModel {

    LiveData<List<ContactEntry>> contacts;

    public MainViewModel(@NonNull Application application) {
        super(application);
        contacts = AppDatabase.getInstance(this.getApplication()).contactDao().loadAllContacts(); // Gets the List of contact entries from the instance of the database (see @DAO post)
    }

    public LiveData<List<ContactEntry>> getContacts() { // Create a getter for the LivaData List above
        return contacts;
    }
}

In your Activity, set an Observer on the getContacts() method in the ViewModel, and tell it what to do if the data changes:

MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class);
viewModel.getContacts().observe(this, new Observer<List<ContactEntry>>() {
            @Override
            public void onChanged(@Nullable List<ContactEntry> contactEntries) {
                mAdapter.setContacts(contactEntries); // Code to run if data changes
            }
        });

For applications which require variables to be passed to the ViewModel refer to the ViewModelFactory post. The Factory holds a template for creating ViewModels that you can customise to allow passing arguments into the ViewModel constructor.

LiveData

LiveData is an observable data holder class. This means it sits between the database and UI, and monitors changes in the database. On observing a change, the LiveData object (will have its setValue method called and) will notify the Observers which will reflect the new data in the UI. Unlike a regular observable, LiveData is lifecycle-aware, meaning it respects the lifecycle of other app components, such as activities, fragments, or services. This awareness ensures LiveData only updates app component observers that are in an active lifecycle state.

Runs by default outside of the main thread.

Often used in conjunction with ViewModel.

To add the dependencies for LiveData, see here.

To observe data in a database, simply wrap the return type in a LiveData<> tag (this example corresponds to the code in the @Entity, @DAO and Build a Database posts). In the @DAO file:

@Dao
public interface ContactDao {

    @Query("SELECT * FROM contacts ORDER BY age")
    LiveData<List<ContactEntry>> loadAllContacts();

    @Insert
    void insertContact(ContactEntry contactEntry);
 
    @Update(onConflict = OnConflictStrategy.REPLACE)
    void updateContact(ContactEntry contactEntry);
 
    @Delete
    void deleteContact(ContactEntry contactEntry);
 
    @Query("SELECT * FROM contacts WHERE name = :name")
    ContactEntry loadEntryByName(String name);
}

Any references/calls to this method will need to have their type wrapped in LiveData<> also.
We set up an Observer in our Activity (do this just once at onCreate and not e.g. in onResume so that the data will only be refreshed when it is changed):

        final LiveData<List<ContactEntry>> contacts = mDb.contactDao().loadAllContacts(); // mDb is instance of database (extends RoomDatabase). contactDao is the @DAO for the database (see above)
        contacts.observe(this, new Observer<List<ContactEntry>>() { // LivecycleOwner is set to 'this' in this example
            @Override
            public void onChanged(@Nullable List<ContactEntry> contactEntries) {
                // Do something with the new data
            }
        });