2

I'm learning how to use Android Room from google-developer-training, where I found example of repository class. I try to simplify my SportRepository class. I wonder how to avoid repetition of inner class ...AsyncTask in my code. Here is very sample example:

@Singleton
class SportRepository {
    val LOG_TAG = SportRepository::class.java.name

    @Inject
    lateinit var sportDAO: SportDAO
    var list: LiveData<List<Sport>>

    init {
        App.app().appComponent()?.inject(this)
        list = sportDAO.getAll()
    }

    fun insert(sport: Sport) {
        insertAsyncTask().execute(sport)
    }

    fun update(sport: Sport){
        updateAsyncTask().execute(sport)
    }

    fun delete(sport: Sport) {
        deleteAsyncTask().execute(sport)
    }

    @SuppressLint("StaticFieldLeak")
    private inner class insertAsyncTask() : AsyncTask<Sport, Void, Void>() {
        override fun doInBackground(vararg p0: Sport): Void? {
            sportDAO.insert(p0.get(0))
            return null
        }
    }

    @SuppressLint("StaticFieldLeak")
    private inner class updateAsyncTask() : AsyncTask<Sport, Void, Void>() {
        override fun doInBackground(vararg p0: Sport): Void? {
            sportDAO.update(p0[0])
            return null
        }
    }

    @SuppressLint("StaticFieldLeak")
    private inner class deleteAsyncTask() : AsyncTask<Sport, Void, Void>() {
        override fun doInBackground(vararg p0: Sport): Void? {
            sportDAO.delete(p0[0])
            return null
        }
    }
}

The AsyncTask classes differ only in name and in kind of method invoke from sportDAO class.

Is there any way to avoid creating many almost the same AsyncTask classes?

I've not found any example how to simplify that.

wapn
  • 359
  • 1
  • 7
  • 19

3 Answers3

3

Ok, I faced the same. There are 3 solutions that I use. 1. Use RX. Return a Flowable, and observe it on different thread. 2. Use LiveData. 3. Async Task. This is how I avoid multiple async tasks by using Generics. I hope this is what you are looking for.

This is the class that will perform your queries.

/**
 *
 * @param <T> type of result expected
 */
public abstract class DaoAsyncProcessor<T> {

    public interface DaoProcessCallback<T>{
        void  onResult(T result);
    }

    private DaoProcessCallback daoProcessCallback;

    public DaoAsyncProcessor(DaoProcessCallback daoProcessCallback) {
        this.daoProcessCallback = daoProcessCallback;
    }

    protected abstract T doAsync();

    public void start(){
        new DaoProcessAsyncTask().execute();
    }

    private class DaoProcessAsyncTask extends AsyncTask<Void, Void, T>{

        @Override
        protected T doInBackground(Void... params) {
            return doAsync();
        }

        @Override
        protected void onPostExecute(T t) {
            if(daoProcessCallback != null)
                daoProcessCallback.onResult(t);
        }
    }
}

Now for querying

fun putAllUsersAsync(vararg users: User) {
            object : DaoAsyncProcessor<Unit>(null) {
                override fun doAsync(): Unit {
                    yourDao.insertAll(*users)
                }

            }.start()
        }

Another example of fetching data.

fun getAllUsers(callback: DaoAsyncProcessor.DaoProcessCallback<List<User>>) {
            object : DaoAsyncProcessor<List<User>>(callback) {
                override fun doAsync(): List<User> {
                    return yourDao.getAll()
                }

            }.start()

You can call getAllUsers and pass a callback for getting the data.

As requested, this is the Kotlin equivalent

abstract class DaoAsyncProcessor<T>(val daoProcessCallback: DaoProcessCallback<T>?) {

    interface DaoProcessCallback<T> {
        fun onResult(result: T)
    }


    protected abstract fun doAsync(): T

    fun start() {
        DaoProcessAsyncTask().execute()
    }

    private inner class DaoProcessAsyncTask : AsyncTask<Void, Void, T>() {

        override fun doInBackground(vararg params: Void): T {
            return doAsync()
        }

        override fun onPostExecute(t: T) {
            daoProcessCallback?.onResult(t)
        }
    }
}
Debanjan
  • 2,817
  • 2
  • 24
  • 43
  • I like this solution, however, Android Studio says that DaoProcessAsyncTask should be static. – BlackCath May 23 '18 at 09:51
  • That's because I think you have made it an inner class – Debanjan May 23 '18 at 10:01
  • When I try to convert class `DaoAsyncProcessor`, I stuck on the problem with `onPostExecute(T t)` method. I get the message from IDE: `Out-projected type 'DaoAsyncProcessor.DaoProcessCallback<*>' prohibits the use of 'public abstract fun onResult(result: T): Unit`. Please provide Kotlin code for this class or tell how to solve this? – wapn Jul 12 '18 at 15:42
  • @wapn adding the kotlin equivalent – Debanjan Jul 12 '18 at 19:20
1

Using RX java you could replace AsyncTask with a Completable for your insert/update/delete commands.

fun insert(sport: Sport) : Completable = Completable.fromAction { sportDAO.insert(sport) }
fun update(sport: Sport) : Completable = Completable.fromAction { sportDAO.update(sport) }
fun delete(sport: Sport) : Completable = Completable.fromAction { sportDAO.delete(sport) }
Chris
  • 2,332
  • 1
  • 14
  • 17
0

You could use the same AsyncTask for each task if you pass it the code segment that it needs to actually execute.

You could for example do the following, using lambda-with-receivers (and without having to add Rx completables/singles or whatever):

@Singleton
class SportRepository @Inject constructor(val sportDAO: SportDao) {
    companion object {
        const val LOG_TAG = "SportRepository"
    }

    var list: LiveData<List<Sport>> = sportDAO.getAll() // is this necessary?

    fun insert(sport: Sport) {
        executeTask(sport) { insert(it) }
    }

    fun update(sport: Sport){
        executeTask(sport) { update(it) }
    }

    fun delete(sport: Sport) {
        executeTask(sport) { delete(it) }
    }

    private fun executeTask(sport: Sport, task: SportDao.(Sport) -> Unit) {
        BackgroundTask(sport, task).execute()
    }

    @SuppressLint("StaticFieldLeak")
    private inner class BackgroundTask(
        private val sport: Sport,
        private val task: SportDao.(Sport) -> Unit
    ) : AsyncTask<Void, Void, Void>() {
        override fun doInBackground(vararg p0: Void): Void? {
            task(sportDAO, sport)
            return null
        }
    }
}
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428