Home > OS >  Reactive list on Android from Firebase Firestore
Reactive list on Android from Firebase Firestore

Time:11-20

I'm trying to get a RecyclerView to update once I have downloaded some data from Firestore but it doesn't seem to update the UI when the data changes. Here is the main activity:

class MainActivity : AppCompatActivity() {

private lateinit var dbService: DatabaseManager
private var mBound: Boolean = false

private val restaurantListModel: RestaurantList by viewModels()

val data = ArrayList<RestaurantCardModel>()
val adapter = RestaurantListAdapter(data)

/** Defines callbacks for service binding, passed to bindService()  */
private val connection = object : ServiceConnection {

    override fun onServiceConnected(className: ComponentName, service: IBinder) {

        // We've bound to LocalService, cast the IBinder and get LocalService instance
        val binder = service as DatabaseManager.LocalBinder
        dbService = binder.getService()
        mBound = true

        dbService.read_restaurants(object: MyCallback {
            override fun onCallback(value: MutableList<Restaurant>) {
                restaurantListModel.restList.value = value
                updateView()
            }
        })
    }

    override fun onServiceDisconnected(arg0: ComponentName) {
        mBound = false
    }
}

fun updateView(){
    for (rest in restaurantListModel.restList.value!!){
        data.add(RestaurantCardModel(R.drawable.pizza_express_logo, rest.name, 5, rest.review, rest.no_review,
            R.color.colorPrimary))
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // Bind to LocalService
    Intent(this, DatabaseManager::class.java).also { intent ->
        bindService(intent, connection, Context.BIND_AUTO_CREATE)
    }

    // getting the recyclerview by its id
    val recyclerview = findViewById<RecyclerView>(R.id.restaurant_recyclerview)

    // this creates a vertical layout Manager
    recyclerview.layoutManager = LinearLayoutManager(this)

    // Setting the Adapter with the recyclerview
    recyclerview.adapter = adapter
    
    if(mBound) { updateView() }
}

override fun onResume() {
    super.onResume()
    if (mBound) { updateView() }
}

override fun onStart() {
    super.onStart()
}

override fun onStop() {
    super.onStop()
    unbindService(connection)
    mBound = false
}

}

Then you have my database manager for fetching the data from firebase:

class DatabaseManager : Service() {
// Binder given to clients
private val binder = LocalBinder()

var db = FirebaseFirestore.getInstance()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    return super.onStartCommand(intent, flags, startId)
}

fun upload_review(review_data: String){

}

fun read_restaurants(myCallback : MyCallback): MutableList<Restaurant> {
    var restList : MutableList<Restaurant> = mutableListOf()
    db.collection("restaurant")
        .get()
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                restList = updateRestaurantList(task.result!!)
                myCallback.onCallback(restList)
            } else {
                Log.w(TAG, "Error getting documents.", task.exception)
            }
        }
    return restList
}

private fun updateRestaurantList(restaurants: QuerySnapshot): MutableList<Restaurant> {
    var restList : MutableList<Restaurant> = mutableListOf()
    for (doc in restaurants.documents){
        Log.d(TAG, doc.toString())
        var tempRest : Restaurant? = doc.toObject(Restaurant::class.java)?.withId(doc.id)
        if (tempRest != null) {
            restList.add(tempRest)
        }
    }
    return restList
}

/**
 * Class used for the client Binder.  Because we know this service always
 * runs in the same process as its clients, we don't need to deal with IPC.
 */
inner class LocalBinder : Binder() {
    // Return this instance of LocalService so clients can call public methods
    fun getService(): DatabaseManager = this@DatabaseManager
}

override fun onBind(intent: Intent): IBinder {
    return binder
}

}

The recycler view works fine when I add the data manually in the onCreate() method but doesn't update the view once firebase is finished, any help would be appreciated.

CodePudding user response:

The recycler view works fine when I add the data manually in the onCreate() method but doesn't update the view

That's the expected behavior since you are using a .get() call, which basically means that you are getting the data only once. What you are looking for is listening for real-time updates. So you have to use addSnapshotListener() call for that.

Besides that, there is also a useful library called FirebaseUI for Android that has a specific usage for Cloud Firestore.

  • Related