'How to access and return the result of an AsyncTask from a repository?

I'm new to coding, put already in a lot of hours and feel like I'm progressing step by step. However, I've encountered a problem that I was not able to solve even after days of researching and trying out different things. Sorry for being wordy or bad explanations - I'm trying to explain it as understandable as possible.

Thank you so much in advance already!

I'll describe the problem and what I have tried first. Then, down below you can see some more information on my final goal and why I thought about building the code the way it is. In the end you can find my code that I have so far.

Problem and potential solutions

The app I'm trying to code uses MVVM structure - I use Android Room and have a (pre-populated) database, two entities and corresponding DAOs, a view model and a repository.

In my repository I am trying to include a new method "getEntriesCountById" that takes an Integer ("correspondingChallengeId") and passes it to a new AsyncTask. Given that id, the AsyncTasks runs the corresponding query within the EntryDao to retrieve another integer that serves as the "entriesCount".

However, I cannot find a way to return the result (the "entriesCount") of that asyncTask within my repository-method "getEntriesCountById". I assume that I need to return it, so that I can call that method from another activity or the viewModel.

I tried to wrap the return variable in LiveData and I tried to make use of the PostExecute Method, but yet couldn't find a way to make it work. I also looked into the usage of Weak References and the "delegate" function but somehow I just cannot return that "entriesCount". By the way - I was successful in getting and showing the list of all entries (in a recycler view). I feel like it really is just as small step that I am missing. (By the way the repository in the code below includes the operation "return list of all available entries and the corresponding asyncTask - This is I guess irrelevant for this question.)

Background information

To be more precise, the described entity is called "ProgressEntry" which is basically serving as a table to log someone's progress for different challenges (running, swimming, playing the guitar and so on). It includes a (primary, unique) entryId, a correspondingChallengeId, an entryDate and an entryValue. One record in the table could be: e.g.: 1, 3, 09/10/2020, 5000) meaning the record has the unique id "1", the correspondingChallengeId "3" (that identifies the corresponding challenge, e.g. running), the particular date and the value of e.g. 5000 meters. Obviously, there can be many entries for the same correspondingChallengeId (e.g. if a person runs 5 times during September).

In the repository I want to use Async Tasks to handle various DAO SQL Queries such as "return a list of progress entries with correspondingChallengeId = 1, or "return the entry with the highest value" or "return number of entries for a particular challenge" (as in my case stated above).

Code snippets

My ProgressEntry Class

@Entity(tableName = "progress_entries_table")
public class ProgressEntry {

@PrimaryKey (autoGenerate = true)
private int progressEntryPrimaryId;
//gives information about correspondingChallenge
private int correspondingChallengeId;
//date for which entry was recorded
private String progressEntryDate;
//progress value for particular entry
private int progressEntryValue;

public ProgressEntry(int correspondingChallengeId, String progressEntryDate, int progressEntryValue) {
    this.correspondingChallengeId = correspondingChallengeId;
    this.progressEntryDate = progressEntryDate;
    this.progressEntryValue = progressEntryValue;
} 

(includes setters and getters for all fields/columns)

My ProgressEntry Dao (Query)

//return number of entries for given id (representing unique challenge)
@Query("SELECT count(*) FROM progress_entries_table WHERE correspondingChallengeId =:id")
Integer getEntriesCountById(int id);

My Repository

Note: the AsyncTask I was describing is at the top/ in the middle. The method "getAllAvailableProgressEntries" at the bottom is irrelevant and actually works in my application" quite well. I included it because it could maybe give a hint on what I am doing wrong.

public class Repository {
private ProgressEntryDao progressEntryDao;
private LiveData<List<ProgressEntry>> allAvailableProgressEntries;

public Repository(Application application) {
    Database database = Database.getInstance(application);

    progressEntryDao = database.progressEntryDao();
    allAvailableProgressEntries = progressEntryDao.getAllAvailableProgressEntries();
}

//--------------------------------------------------------

public Integer getEntriesCountById(Integer correspondingChallengeId) {
    new CountAsyncTask(progressEntryDao).execute(correspondingChallengeId);
    //return... - How do I return the Integer (entriesCount)
}

//passing and Integer (correspondingChallengeId) and result is also supposed to be an Integer (entriesCount)
private static class CountAsyncTask extends AsyncTask<Integer, Void, Integer>{
    private ProgressEntryDao progressEntryDao;

    //Constructor for AsyncTask
    public CountAsyncTask(ProgressEntryDao progressEntryDao) {
        this.progressEntryDao = progressEntryDao;
    }

    //doInBackground method accessing my Dao and the required method
    @Override
    protected Integer doInBackground(Integer... integers) {
        return progressEntryDao.getEntriesCountById(integers[0]);
    }

    //onPostExecute method - It should handle the result, right?
    @Override
    protected void onPostExecute(Integer integer) {
        super.onPostExecute(integer);
    }

}

//--------------------------------------------------------
public LiveData<List<ProgressEntry>> getAllAvailableProgressEntries() {
    return allAvailableProgressEntries;
}

public void insertProgressEntry(ProgressEntry progressEntry) {
    new InsertProgressEntryAsyncTask(progressEntryDao).execute(progressEntry);
}

private static class InsertProgressEntryAsyncTask extends AsyncTask<ProgressEntry, Void, Void> {
    private ProgressEntryDao progressEntryDao;

    //Constructor
    private InsertProgressEntryAsyncTask(ProgressEntryDao progressEntryDao){
        this.progressEntryDao = progressEntryDao;
    }

    @Override
    protected Void doInBackground(ProgressEntry... progressEntries) {
        progressEntryDao.insert(progressEntries[0]);
        return null;
    }
}

How I am calling the method from an other activity (Doing it without view model for reasons of simplicity.)

public class ExperimentActivity extends AppCompatActivity {
private TextView textView;
private Repository repository;



@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_experiment);

    repository = new Repository(getApplication());

    textView = findViewById(R.id.txtView);


    textView.setText(String.valueOf(repository.getEntriesCountById(2)));



Solution 1:[1]

AsyncTask is deprecated so you should not use it anymore (see AsyncTask docs). There is also mentioned what you should use instead: coroutines. They play well with LiveData so that you might end up with an up-to-date solution. As this might be very confusing I would recommend you to look at this codelab which illustrates how to put Room + Coroutines + LiveData together.

Solution 2:[2]

You don't simply return a value from the Repository's background thread. Instead, pass a LiveData object from the Dao up to the Activity.

In the Activity, observe the LiveData object. When the query completes, LiveData senses this and executes onChanged where you can then finally access the value contained in the LiveData variable.

MyDao: (add LiveData wrapper)

//return number of entries for given id (representing unique challenge)
@Query("SELECT count(*) FROM progress_entries_table WHERE correspondingChallengeId =:id")
LiveData<Integer> getEntriesCountById(int id);

Repository: (Don't use a background thread. Just return the LiveData object

public LiveData<Integer> getEntriesCountById(int id){
    return progressEntryDao.getEntriesCountByName(name);
}

Activity:

repository.getEntriesCountById(2).observe(this, new Observer<Integer>() {
    @Override
    public void onChanged(Integer countReceived) {
        textView.setText(String.valueOf(countReceived));
    }
});

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 and_dev
Solution 2 beebopbogo