1

Sorry if my english is bad

I'm new in mvvm architecture. I use Room, Dagger, and Reactive. In my project, i create sample with insert 'task' into database and get 'task' from database. the problem is when I save 'task', method in observer for get 'task' from database are triggered, so in RecycleView will add same data from database. But in method 'constructSaveTask' did not call 'getTask'

I use two MutableLiveData, one for getTask and saveTask

I don't know why. Please help me

TaskDao.kt

@Dao
abstract class TaskDao : BaseDao<TaskEntity>() {
    @Query("SELECT * FROM task")
    abstract fun getTasks() : Flowable<List<TaskEntity>>
}

TaskRepository.kt

@Singleton
class TaskRepository {
    private var taskDao: TaskDao

    @Inject constructor(taskDao: TaskDao) {
        this.taskDao = taskDao
    }

    fun saveTask(taskEntity: TaskEntity) : Flowable<Long> {
        return Flowable.fromCallable { taskDao.save(taskEntity) }
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }

    fun getTasks() : Flowable<List<TaskEntity>> {
        return taskDao.getTasks()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
    }
}

BaseResponse.kt

open class BaseResponse<T> {
    var isLoading: Boolean = false
    var isSuccess: Boolean = false
    var isNeedBack: Boolean = false
    var failedMessage: String = ""
    var throwable: Throwable? = null
    var data: T? = null

    fun onLoading(isLoading: Boolean) : BaseResponse<T> {
        this.isLoading = isLoading
        return this
    }

    fun onSuccess(data: T?) : BaseResponse<T> {
        this.data = data
        this.isSuccess = true
        this.isLoading = false

        return this
    }

    fun onFailed(isNeedBack: Boolean, failedMessage: String) : BaseResponse<T> {
        this.isSuccess = false
        this.isNeedBack = isNeedBack
        this.failedMessage = failedMessage
        this.isLoading = false
        return this
    }

    fun onError(throwable: Throwable) : BaseResponse<T> {
        this.throwable = throwable
        this.isSuccess = false
        this.isLoading = false
        return this
    }
}

TaskViewModel.kt

class TaskViewModel(private var taskRepository: TaskRepository) : BaseViewModel() {
    var getTasks: MutableLiveData<BaseResponse<List<TaskEntity>>> = MutableLiveData()
    var saveTask: MutableLiveData<BaseResponse<Long>> = MutableLiveData()

    fun getTasks() {
        addDisposable(taskRepository.getTasks()
                .doOnSubscribe {
                    getTasks.value = BaseResponse<List<TaskEntity>>().onLoading(true)
                }.doFinally {
                    getTasks.value = BaseResponse<List<TaskEntity>>().onLoading(false)
                }.subscribe{ data: List<TaskEntity>? ->
                    getTasks.value = BaseResponse<List<TaskEntity>>().onSuccess(data)
                })
    }

    fun saveTask(taskEntity: TaskEntity) {
        addDisposable(taskRepository.saveTask(taskEntity)
                .doOnSubscribe {
                    saveTask.value = BaseResponse<Long>().onLoading(true)
                }.doFinally {
                    saveTask.value = BaseResponse<Long>().onLoading(false)
                }.subscribe { data: Long? ->
                    saveTask.value = BaseResponse<Long>().onSuccess(data)
                })
    }
}

DatabaseActivity.kt

class DatabaseActivity : BaseActivity() {
    var taskAdapter: TaskAdapter? = null
    var viewModeFactory: ViewModelFactory = ViewModelFactory()
    var taskViewModel: TaskViewModel? = null

    override fun getContentViewResource(): Int {
        return R.layout.activity_database
    }

    override fun onBaseCreated(savedInstanceState: Bundle?) {
        showNavigationBackButton()

        initAdapter()
        initView()

        button_save.setOnClickListener {
            val task = TaskEntity()
            task.description = field_description.text.toString()
            task.task = field_task.text.toString()
            task.finishedBy = field_finished_by.text.toString()

            field_description.text.clear()
            field_task.text.clear()
            field_finished_by.text.clear()

            taskViewModel?.saveTask(task)
        }

        taskViewModel?.getTasks()
    }

    fun initAdapter() {
        taskAdapter = TaskAdapter()
        list_task.adapter = taskAdapter
        list_task.layoutManager = AdapterHelper.verticalLayout(this, false)
        list_task.addItemDecoration(AdapterHelper.addDividerVertical(this))
    }

    fun initView() {
        taskViewModel = ViewModelProviders.of(this, viewModeFactory).get(TaskViewModel::class.java)
        taskViewModel?.saveTask?.observe(this, Observer { response -> constructSaveTask(response) })
        taskViewModel?.getTasks?.observe(this, Observer { response -> constructGetTask(response) })
    }

    private fun constructGetTask(response: BaseResponse<List<TaskEntity>>?) {
        if (response?.isLoading == true) {
            Toast.makeText(this, "Retrieving Task", Toast.LENGTH_LONG).show()
        } else {
            Toast.makeText(this, "Task Retrieved", Toast.LENGTH_LONG).show()
        }

        if (response?.isSuccess == true) {
            response.isSuccess = false
            if (response.data != null) {
                taskAdapter?.addAll(ArrayList(response.data!!))
            }
        }
    }

    private fun constructSaveTask(response: BaseResponse<Long>?) {
        if (response?.isLoading == true) {
            Toast.makeText(this, "Saving Task", Toast.LENGTH_LONG).show()
        } else {
            Toast.makeText(this, "Task Has Been Saved", Toast.LENGTH_LONG).show()
        }

        if (response?.isSuccess == true) {
            Toast.makeText(this, "Success Saved", Toast.LENGTH_LONG).show()
        }
    }
}

Update : Here adapter code

TaskAdapter.kt

    class TaskAdapter : BaseAdapter<TaskEntity, TaskHolder>() {

    override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): TaskHolder {
        val view: View = LayoutInflater.from(viewGroup.context).inflate(R.layout.adapter_task, viewGroup, false)
        return TaskHolder(view)
    }

    override fun onBindViewHolder(holder: TaskHolder, position: Int) {
        val task: TaskEntity = contents[position]

        holder.descriptionText.text = task.description
        holder.taskText.text = task.task
        holder.finishedByText.text = task.finishedBy
    }
}

BaseAdapter.kt

abstract class BaseAdapter<ListContent, Holder : RecyclerView.ViewHolder> : RecyclerView.Adapter<Holder>() {
    var contents: ArrayList<ListContent> = ArrayList()

    override fun getItemCount(): Int {
        return contents.size
    }

    fun addAll(contents: ArrayList<ListContent>) {
        this.contents.addAll(contents)
        notifyDataSetChanged()
    }

    fun add(content: ListContent) {
        this.contents.add(content)
        notifyDataSetChanged()
    }

    fun put(position: Int, content: ListContent) {
        this.contents[position] = content
        notifyItemChanged(position)
    }

    fun remove(position: Int) {
        this.contents.removeAt(position)
        notifyItemChanged(position)
    }

    fun clear() {
        this.contents.clear()
        notifyDataSetChanged()
    }
}
Dhinx
  • 105
  • 1
  • 7
  • Please post `taskAdapter`'s code as well – Sandip Fichadiya Jan 25 '19 at 09:18
  • `taskAdapter?.addAll(ArrayList(response.data!!))` this is the problem, you're supposed to use `response.data` as is, instead of adding them to the adapter. Adapter shouldn't even store a mutable list in the first place, it should be `List` and not `ArrayList` inside Adapter. – EpicPandaForce Jan 25 '19 at 12:31
  • let me try @EpicPandaForce – Dhinx Jan 28 '19 at 06:47
  • @SandipSoni Done, I've added adapter code – Dhinx Jan 28 '19 at 06:52
  • Try by calling `clear()` first in the `addAll()` of your adapter. You already had 1st item in your list and when you add the second item, you again load all the items from db which gives you 2 items. Now without clearing existing one from your DB, you are adding all 2 new items and hence it shows 3 items – Sandip Fichadiya Jan 28 '19 at 09:24
  • if I use `clear()` before calling `addAll()` I cannot use 'load more' feature – Dhinx Jan 29 '19 at 09:00

0 Answers0