У меня есть небольшое приложение, которое я использую, чтобы узнать больше о некоторых новых компонентах Android. Мне сложно найти информацию и понять, как лучше всего делать то, что я хочу.
В настоящее время: открытое приложение - ›загрузка данных + хранилища в БД -› отображение данных в списке
Я хочу иметь возможность снова запрашивать данные при нажатии кнопки. У меня есть 2 кнопки, 1 для повторного получения данных, 1 для удаления данных списка из БД.
Проблема в том, что кажется, что вы не можете обновиться, если вы наблюдаете за экземпляром LiveData, каковым я и являюсь. Я понимаю, что, однако, способ, которым я на самом деле выполнял сетевой вызов и сохранял в базе данных, возвращает экземпляр LiveData, и я не уверен, как лучше всего поступить.
Позвольте показать вам код.
Фрагмент
private val viewModel: quoteViewModel by viewModels()
private lateinit var binding: FragmentHomeBinding
private lateinit var adapter: QuoteAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
initRecyclerView()
setupRetrieveQuotesObserver()
setupDeleteDataListener()
setupFetchNewDataListener()
setupSwipeToRefresh()
}
private fun initRecyclerView() {
adapter = QuoteAdapter()
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
}
private fun setupDeleteDataListener() {
binding.removeQuotesButton.setOnClickListener {
viewModel.removeAllQuotes()
}
}
private fun setupFetchNewDataListener() {
binding.getQuotesButton.setOnClickListener {
viewModel.removeQuotes()
viewModel.getQuotes()
}
}
private fun setupRetrieveQuotesObserver() {
viewModel.quoteLiveDataList.observe(viewLifecycleOwner, Observer { result ->
when (result.status) {
NewResult.Status.SUCCESS -> {
result.data.let { adapter.setItems(ArrayList(result.data)) }
binding.progressBar.visibility = View.GONE
binding.swipeContainer.isRefreshing = false
}
NewResult.Status.ERROR -> {
binding.progressBar.visibility = View.GONE
Snackbar.make(binding.root, "Some error has occurred", Snackbar.LENGTH_SHORT)
.show()
}
NewResult.Status.LOADING -> {
binding.progressBar.visibility = View.VISIBLE
}
}
})
}
private fun setupSwipeToRefresh() {
binding.swipeContainer.setOnRefreshListener {
viewModel.getQuotes()
}
}
ViewModel
val quoteLiveDataList: LiveData<NewResult<List<Quote>>> = repository.quotes
fun getQuotes() = viewModelScope.launch {
repository.quotes
}
fun removeAllQuotes() = viewModelScope.launch {
repository.deleteAllQuotes()
}
Репозиторий
val quotes = performGetOperation(
databaseQuery = { dao.getAllQuotes() },
networkCall = { remoteSource.getAllQuotes() },
saveCallResult = {
val quotesList = ArrayList<Quote>()
for (messageString in it.messages.non_personalized) {
quotesList.add(
Quote(
messageString,
FaceImageProvider().getRandomFacePicture(),
false
)
)
}
dao.insertQuotes(quotesList)
}
)
@WorkerThread
suspend fun deleteAllQuotes() = withContext(Dispatchers.IO) { dao.deleteAllQuotes() }
performGetOperation Это класс, который я видел в Интернете для обработки того, что я хочу сделать. Я думаю, что проблема связана с этим, поскольку он возвращает LiveData, я не уверен, как лучше это исправить
fun <T, A> performGetOperation(
databaseQuery: () -> LiveData<T>,
networkCall: suspend () -> NewResult<A>,
saveCallResult: suspend (A) -> Unit
): LiveData<NewResult<T>> =
liveData(Dispatchers.IO) {
emit(NewResult.loading())
val source = databaseQuery.invoke().map { NewResult.success(it) }
emitSource(source)
val responseStatus = networkCall.invoke()
if (responseStatus.status == NewResult.Status.SUCCESS) {
saveCallResult(responseStatus.data!!)
} else if (responseStatus.status == NewResult.Status.ERROR) {
emit(NewResult.error(responseStatus.message!!))
emitSource(source)
}
}
RemoteDataSource
suspend fun getQuotes() = getResult { service.getQuotes() }
getResult
protected suspend fun <T> getResult(call: suspend () -> Response<T>): NewResult<T> {
try {
val response = call.invoke()
if (response.isSuccessful) {
val body = response.body()
if (body != null) {
return NewResult.success(body)
}
}
return error("${response.code()} ${response.message()}")
} catch (e: Exception) {
return error(e.message ?: e.toString())
}
}
private fun <T> error(message: String): NewResult<T> {
Log.d("BaseDataSource", message)
return NewResult.error("Network called failed due to: $message")
}
NewResult
data class NewResult<out T>(val status: Status, val data: T?, val message: String?) {
enum class Status {
SUCCESS,
ERROR,
LOADING,
}
companion object {
fun <T> success(data: T): NewResult<T> {
return NewResult(Status.SUCCESS, data, null)
}
fun <T> error(message: String, data: T? = null): NewResult<T> {
return NewResult(Status.ERROR, data, message)
}
fun <T> loading(data: T? = null): NewResult<T> {
return NewResult(Status.LOADING, data, null)
}
}
Извиняюсь за очень длинное сообщение, но я думаю, мне нужно показать все мелочи, которые я использую. Я думаю, что проблема в фрагменте, где я выполняю viewModel.quoteLiveDataList.observe, поскольку он возвращает новые LiveData, если он вызывается снова. Поэтому я не уверен, как я могу сделать еще один вызов сервера, обновить БД и вернуть ее сюда. Ценю любую помощь! Спасибо