Reputation: 4385
Context: It is my first project in Android and I fill I am asking some silly question but I can't find any direction on it (certainly I am messing up some previous knowledge of Angular/Srping with Android/Kotlin)
Goal: Android App will get a Firestore customtoken from certain backend microservice and then start to listen a document. So far so good. I read about good practices of how close/detach the listen and I believe I have successfully done this by passing Android activity as first argument to snapshot. So far also so good. But in my case, I have to close/detach the snapshot listen either after 10 minutes or after a especific value from document is received. Now I really got stuck.
I tried the simplest first step as imagined and I am getting the naive warnning form this topic. So my straght question is: why it is complaining as ALWAYS TRUE condition? A complementaty comment from someone expert on Android would be how to close/detach the snapshot after 10 minutes and if I receive a specific value from the listened document. Please accept the idea that either one of these two conditions needs to stops listen and still keep in same MainActivity.kt.
Here is the code with warning when trying to check during onStop cycle phase
package com.mycomp.appfirestore
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.DocumentSnapshot
import com.google.firebase.firestore.EventListener
import com.google.firebase.firestore.FirebaseFirestore
import com.mycomp.appfirestore.data.service.Endpoint
import com.mycomp.appfirestore.data.service.NetworkUtils
import com.mycomp.appfirestore.model.Transfer
import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity() {
lateinit var auth: FirebaseAuth
lateinit var listenerReg : FirebaseFirestore
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val btnTransfer = findViewById(R.id.btnTransfer) as Button
val textViewTransfer = findViewById(R.id.textViewTransfer) as TextView
btnTransfer.setOnClickListener {
getToken()
}
}
fun getToken() {
val retrofitClient = NetworkUtils
.getRetrofitInstance("http://192.168.15.13:8080/")
val endpoint = retrofitClient.create(Endpoint::class.java)
...
callback.enqueue(object : Callback<Transfer> {
override fun onFailure(call: Call<Transfer>, t: Throwable) {
Toast.makeText(baseContext, t.message, Toast.LENGTH_SHORT).show()
}
override fun onResponse(call: Call<Transfer>, response: Response<Transfer>) {
listenStatus()
}
})
}
fun listenStatus() {
val TAG = "ListenStatus"
auth = FirebaseAuth.getInstance()
// to make simple for this question let's say the backend returned a valid customtoken used here
auth.signInWithCustomToken("eyJ **** gXsQ")
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
Log.d(TAG, "*** signInWithCustomToken:success")
startSnapshot()
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCustomToken:failure", task.exception)
Toast.makeText(
baseContext, "Authentication failed.",
Toast.LENGTH_SHORT
).show()
}
}
}
fun startSnapshot() {
val TAG = "StartSnapshot"
//Try to pass this(activity context) as first parameter.It will automatically handle acivity life cycle.
// Example if you are calling this listener in onCreate() and passing this as a first parameter then
// it will remove this listener in onDestroy() method of activity.
listenerReg = FirebaseFirestore.getInstance()
listenerReg.collection("transfer")
.document("sDme6IRIi4ezfeyfrU7y")
.addSnapshotListener(
this,
EventListener<DocumentSnapshot?> { snapshot, e ->
if (e != null) {
Log.w(TAG, "Listen failed.", e)
return@EventListener
}
if (snapshot != null && snapshot.exists()) {
textViewTransfer.text = snapshot.data!!["status"].toString()
//Log.d(TAG, snapshot.data!!["status"].toString())
} else {
Log.d(TAG, "Current data: null")
}
})
}
//here I get the warnning mentioned in my question topic
fun stopSnapshot() {
if (listenerReg != null) {
listenerReg.remove()
}
}
}
I am aware that since I added the activity as first argument of snapshot soon the activity is left it will detached automatically the listen. But I have two more condition to stop listen:
1 - after 10 minutes
2 - if I get a specific returned value
So as imaginary solution I would try more or less
...
EventListener<DocumentSnapshot?> { snapshot, e ->
if (e != null) {
Log.w(TAG, "Listen failed.", e)
return@EventListener
}
if (snapshot != null && snapshot.exists()) {
textViewTransfer.text = snapshot.data!!["status"].toString()
**** imagined solution ****
if (snapshot.data!!["status"].toString() == "FINISH"){
stopSnapshot()
}
//Log.d(TAG, snapshot.data!!["status"].toString())
} else {
Log.d(TAG, "Current data: null")
}
})
...
**** imagined solution ***
listenerReg.timeout(10000, stopSnapshot())
Upvotes: 3
Views: 2921
Reputation: 544
To answer your questions:
Why it is complaining as ALWAYS TRUE condition?
Your FirebaseFirestore
object is initialized as lateinit var listenerReg : FirebaseFirestore
, which means you've marked your listenerReg
variable as non-null and to be initialized later. lateinit
is used to mark the variable as not yet initialized, but basically promising that it will be initialized when first referenced in code. Kotlin will throw an error at runtime if trying to access this variable and it's not initialized.
If you want to make it nullable, you'd need to initialize it like this:
var listenerReg : FirebaseFirestore? = null
Then you could have your stop method looking something like this:
fun stopSnapshot() {
if (listenerReg != null) {
listenerReg.remove()
listenerReg.terminate()
listenerRef = null
}
}
But I have two more condition to stop listen: 1 - after 10 minutes
There are many ways to set certain timeouts on Android. The most straightforward way would be to post a handler that will invoke stopSnapshot()
method, e.g.:
Handler().postDelayed({
//Do something after 10mins
stopSnapshot();
}, 1000 * 60 * 10)
2 - if I get a specific returned value
Just call stopSnapshot()
when you receive this value.
Note: I assumed all the Firestore calls are correct. I don't have the API and this usage on top of my head. Hope my answer helps.
Upvotes: 3