2

I would like to have a system to call API (Retrofit) with cache (in Room), with just coroutines (without LiveData and NetworkBoundResource).

So worflow is:

  • Check data in db
  • if present show it
  • if not:
  1. Call API
  2. Save data in db
  3. show data

Problem app blocked in "Call API" step, here the stack

nativePollOnce:-1, MessageQueue (android.os) next:326, MessageQueue (android.os) loop:160, Looper (android.os) main:6669, ActivityThread (android.app) invoke:-1, Method (java.lang.reflect) run:493, RuntimeInit$MethodAndArgsCaller (com.android.internal.os) main:858, ZygoteInit (com.android.internal.os)

Retrofit service:

interface ProductService {
    @GET("products")
    suspend fun getProducts(): Response<List<Product>>
}

DAO Room:

@Dao
interface ProductDao {

    @Query("SELECT * FROM Product ORDER BY price")
    suspend fun getProducts(): List<Product>

    @Transaction
    @Insert(entity = Product::class)
    suspend fun insertProducts(products: List<Product>)
}

My fragment:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

    productService = createProductService()
    productDao = MyDatabase.getDatabase(requireContext()).productDao()
    CoroutineScope(Dispatchers.Main).launch {
        getProducts()
    }
}

private suspend fun getProducts() {
    progressBar.visibility = View.VISIBLE
    recyclerViewProducts.visibility = View.GONE
    
    var products = withContext(Dispatchers.IO){ productDao.getProducts() }

    if(products.isEmpty()) {

        val response = withContext(Dispatchers.IO) { productService.getProducts() }
        if(response.isSuccessful && response.body() != null) {
            products = response.body()!!
            withContext(Dispatchers.IO) { productDao.insertProducts(products) }
        }
    }

    withContext(Dispatchers.Main) {
        progressBar.visibility = View.GONE
        recyclerViewProducts.visibility = View.VISIBLE

        recyclerViewProducts.apply {
            layoutManager = LinearLayoutManager(context)
            // set the custom adapter to the RecyclerView
            adapter = ProductsAdapter(products, this@ListProductFragment)
        }
    }
}
LaurentY
  • 7,495
  • 3
  • 37
  • 55

2 Answers2

0

This is not clean architecture. You should have a Database layer (that you have) and a Repository and a Viewmodel. So when framgnet is created calls viewmodel to observe data from repository that also observe data from Db. If data from db is empty then it creates the api call in a coroutine scope and in the same thread it saves data to DB. So automatically viewmodel gets informed for new data

james04
  • 1,580
  • 2
  • 20
  • 46
0

I recommend using MVVM Design Pattern. You must do what you want in the repository pattern.

The repository pattern is a design pattern that isolates data sources from the rest of the app. A repository mediates between data sources (such as persistent models, web services, and caches) and the rest of the app. The diagram below shows how app components such as activities that use LiveData might interact with data sources by way of a repository. To implement a repository, you use a repository class, such as the VideosRepository class that you create in the next task. The repository class isolates the data sources from the rest of the app and provides a clean API for data access to the rest of the app. Using a repository class is a recommended best practice for code separation and architecture. Advantages of using a repository. A repository module handles data operations and allows you to use multiple backends. In a typical real-world app, the repository implements the logic for deciding whether to fetch data from a network or use results that are cached in a local database. This helps make your code modular and testable. You can easily mock up the repository and test the rest of the code.

MVVM Design Pattern

I suggest you check it out for the error code.

Control

Arda Kazancı
  • 8,341
  • 4
  • 28
  • 50