Search code examples
javaandroidandroid-asynctaskandroid-room

Room query that returns List<Object> freezes application


I've tried using the below code to load some data from my database and display them in an activity:

PhoneNumberViewModel phoneNumberViewModel =
        new ViewModelProvider(WorksideContactCard.this).get(PhoneNumberViewModel.class);

phoneNumberViewModel
    .getPhoneNumbersById(contactID)
    .observe(this,
             numbers -> {
                 phoneNumberList = numbers;
             });

To be precise, I needn't observe the data changing, as the data will be refreshed if the activity is resumed. But the above code freezes my application, although I am accessing the DB using an AsyncTask in my other class.

Why could this be so?

EDIT:

PhoneNumberViewModel.class:

public class PhoneNumberViewModel extends AndroidViewModel {
    private PhoneNumberRepository phoneNumberRepository;
    private LiveData<List<PhoneNumber>> allPhoneNumbers;

    public PhoneNumberViewModel(@NonNull Application application) {
        super(application);
        phoneNumberRepository = new PhoneNumberRepository(application);
        allPhoneNumbers = phoneNumberRepository.getAllPhoneNumbersLive();
    }

    public void insert(PhoneNumber phoneNumber) {
        System.out.println("[PhoneNumberRepository] Adding new phoneNumber");
        phoneNumberRepository.insert(phoneNumber);
    }

    public void update(PhoneNumber phoneNumber) {
        phoneNumberRepository.update(phoneNumber);
    }

    public void delete(PhoneNumber phoneNumber) {
        phoneNumberRepository.delete(phoneNumber);
    }

    public LiveData<List<PhoneNumber>> getAllPhoneNumbers() {
        return allPhoneNumbers;
    }

    public LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId)
            throws ExecutionException, InterruptedException {
        return phoneNumberRepository.getPhoneNumbersByContactId(contactId);
    }

PhoneNumberRepository.class

public class PhoneNumberRepository {
    private PhoneNumberDao phoneNumberDao;
    private LiveData<List<PhoneNumber>> allPhoneNumbers;
    private LiveData<List<PhoneNumber>> phoneNumbersByIdList;

    public PhoneNumberRepository(Application application) {

        WorksideDatabase database = WorksideDatabase.getInstance(application);
        phoneNumberDao = database.phoneNumberDao();
        allPhoneNumbers = phoneNumberDao.getAllPhoneNumbers();
    }

    ...

    public LiveData<List<PhoneNumber>> getAllPhoneNumbersLive() {
        return allPhoneNumbers;
    }

    public void deleteAllPhoneNumbers() {
        new DeleteAllPhoneNumbersAsyncTask(phoneNumberDao).execute();
    }

    public LiveData<List<PhoneNumber>> getPhoneNumbersByContactId(long id)
            throws ExecutionException, InterruptedException {
        return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get();
    }

    private static class SelectPhoneNumberByIdAsyncTask
            extends AsyncTask<Long, Void, LiveData<List<PhoneNumber>>> {

        private PhoneNumberDao phoneNumberDao;
        private Long ID;

        private SelectPhoneNumberByIdAsyncTask(PhoneNumberDao phoneNumberDao, Long contactId) {
            this.phoneNumberDao = phoneNumberDao;
            ID = contactId;
        }

        @Override
        protected LiveData<List<PhoneNumber>> doInBackground(Long... contactId) {

            ID = contactId[0];

            return phoneNumberDao.getPhoneNumbersById(ID);
        }
    }

    ...
}

PhoneNumberDao.class:

@Dao
public interface PhoneNumberDao {

    @Insert
    void insert(PhoneNumber phoneNumber);

    @Update
    void update(PhoneNumber phoneNumber);

    @Delete
    void delete(PhoneNumber phoneNumber);

    // Probably uneeded method
    @Query("DELETE FROM phone_numbers_table")
    void deleteAllPhoneNumbers();

    // Delete entry/entries by ID
    @Query("DELETE FROM phone_numbers_table WHERE id = :phoneNumberId")
    void deleteByPhoneNumberId(long phoneNumberId);

    // Retrieve entry/entries by contact ID
    //    @Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
    //    List<PhoneNumber> getPhoneNumbersById(long contactId);

    // Retrieve all saved phone numbers in LiveData format
    @Query("SELECT * FROM phone_numbers_table")
    LiveData<List<PhoneNumber>> getAllPhoneNumbers();

    @Query("SELECT * FROM phone_numbers_table WHERE contact_id = :contactId")
    LiveData<List<PhoneNumber>> getPhoneNumbersById(long contactId);

    @Query("SELECT * FROM phone_numbers_table")
    List<PhoneNumber> getAll();
}

Solution

  • return new SelectPhoneNumberByIdAsyncTask(phoneNumberDao, id).get(); 
    

    this here is your problem ,.get() call blocks the main thread I would suggest switching to Kotlin and using Coroutines or here you can handle this using callbacks and not using AsyncTask.get() which blocks the main thread.Also BTW Async Task is going to be deprecated last I heard ,So keep that in mind too. This is one solution the problem

    public class Repository implements 
    SelectPhoneNumberByIdAsyncTask.OnPhoneNumberFound {
    
    
    @Override
    public void onPhoneNumberFound(LiveData<List<Object>> list) {
       //you can get the data you want here
    }
    
    
    
    } 
    
    
     class SelectPhoneNumberByIdAsyncTask extends AsyncTask<Long, Void, 
      LiveData<List<PhoneNumber//Correct this>>> {
    
    interface OnPhoneNumberFound{
        void onPhoneNumberFound(LiveData<List<PhoneNumberDao>> list);
    }
    OnPhoneNumberFound mListener;
    
    
    private PhoneNumberDao phoneNumberDao;
    private Long ID;
    
    private SelectPhoneNumberByIdAsyncTask(Object phoneNumberDao, Long contactId) {
        this.phoneNumberDao = phoneNumberDao;
        ID = contactId;
    }
    
    @Override
    protected LiveData<List<Object>> doInBackground(Long... contactId) {
        ID = contactId[0];
        mListener.onPhoneNumberFound(phoneNumberDao.getPhoneNumbersById(ID));
        return null;
    }
    

    }

    use .execute() call now instead of .get()